• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

jiangxincode / ApkToolBoxGUI / #1207

13 Sep 2025 11:58PM UTC coverage: 2.895% (-0.006%) from 2.901%
#1207

push

jiangxincode
fix #569: 可能在完全初始化子类之前逃逸了 'this'

0 of 32 new or added lines in 22 files covered. (0.0%)

9 existing lines in 8 files now uncovered.

248 of 8567 relevant lines covered (2.89%)

0.03 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/main/java/edu/jiangxin/apktoolbox/pdf/stat/PdfStatPanel.java
1
package edu.jiangxin.apktoolbox.pdf.stat;
2

3
import edu.jiangxin.apktoolbox.pdf.PdfUtils;
4
import edu.jiangxin.apktoolbox.swing.extend.EasyPanel;
5
import edu.jiangxin.apktoolbox.swing.extend.FileListPanel;
6
import edu.jiangxin.apktoolbox.utils.Constants;
7
import edu.jiangxin.apktoolbox.utils.DateUtils;
8
import edu.jiangxin.apktoolbox.utils.FileUtils;
9

10
import javax.swing.*;
11
import javax.swing.table.DefaultTableModel;
12
import java.awt.*;
13
import java.awt.event.ActionEvent;
14
import java.awt.event.ActionListener;
15
import java.io.File;
16
import java.io.Serial;
17
import java.util.*;
18
import java.util.List;
19
import java.util.concurrent.ExecutorService;
20
import java.util.concurrent.Executors;
21
import java.util.concurrent.Future;
22
import java.util.concurrent.atomic.AtomicInteger;
23

24
public class PdfStatPanel extends EasyPanel {
×
25

26
    @Serial
27
    private static final long serialVersionUID = 1L;
28

29
    private JTabbedPane tabbedPane;
30

31
    private JPanel mainPanel;
32

33
    private FileListPanel fileListPanel;
34

35
    private JRadioButton pageCountRadioButton;
36

37
    private JCheckBox isRecursiveSearched;
38

39
    private JPanel resultPanel;
40

41
    private JTable resultTable;
42

43
    private DefaultTableModel resultTableModel;
44

45
    private JLabel statInfoLabel;
46

47
    private JButton statButton;
48
    private JButton cancelButton;
49

50
    private JProgressBar progressBar;
51

52
    private transient SearchThread searchThread;
53

54
    private long totalFileSize = 0;
×
55

56
    private int totalPageCount = 0;
×
57

58
    private transient final List<Vector<Object>> resultFileList = new ArrayList<>();
×
59

60

61
    @Override
62
    public void initUI() {
63
        tabbedPane = new JTabbedPane();
×
64
        add(tabbedPane);
×
65

66
        createMainPanel();
×
67
        tabbedPane.addTab("Option", null, mainPanel, "Show Stat Options");
×
68

69
        createResultPanel();
×
70
        tabbedPane.addTab("Result", null, resultPanel, "Show Stat Result");
×
71
    }
×
72

73
    private void createMainPanel() {
74
        mainPanel = new JPanel();
×
75
        mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
×
76

77
        fileListPanel = new FileListPanel();
×
NEW
78
        fileListPanel.initialize();
×
79

80
        JPanel checkOptionPanel = new JPanel();
×
81
        checkOptionPanel.setLayout(new BoxLayout(checkOptionPanel, BoxLayout.X_AXIS));
×
82
        checkOptionPanel.setBorder(BorderFactory.createTitledBorder("Check Options"));
×
83

84
        ButtonGroup buttonGroup = new ButtonGroup();
×
85

86
        pageCountRadioButton = new JRadioButton("统计页数");
×
87
        pageCountRadioButton.setSelected(true);
×
88
        pageCountRadioButton.setEnabled(false);
×
89
        buttonGroup.add(pageCountRadioButton);
×
90

91
        JPanel typePanel = new JPanel();
×
92
        typePanel.setLayout(new FlowLayout(FlowLayout.LEFT,10,3));
×
93
        typePanel.add(pageCountRadioButton);
×
94

95
        checkOptionPanel.add(typePanel);
×
96
        checkOptionPanel.add(Box.createHorizontalGlue());
×
97

98
        JPanel searchOptionPanel = new JPanel();
×
99
        searchOptionPanel.setLayout(new BoxLayout(searchOptionPanel, BoxLayout.X_AXIS));
×
100
        searchOptionPanel.setBorder(BorderFactory.createTitledBorder("Stat Options"));
×
101

102
        isRecursiveSearched = new JCheckBox("Recursive");
×
103
        isRecursiveSearched.setSelected(true);
×
104
        searchOptionPanel.add(isRecursiveSearched);
×
105
        searchOptionPanel.add(Box.createHorizontalGlue());
×
106

107
        JPanel operationPanel = new JPanel();
×
108
        operationPanel.setLayout(new BoxLayout(operationPanel, BoxLayout.X_AXIS));
×
109
        operationPanel.setBorder(BorderFactory.createTitledBorder("Operations"));
×
110

111
        JPanel buttonPanel = new JPanel();
×
112
        buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
×
113

114
        statButton = new JButton("Stat");
×
115
        cancelButton = new JButton("Cancel");
×
116
        cancelButton.setEnabled(false);
×
117
        statButton.addActionListener(new OperationButtonActionListener());
×
118
        cancelButton.addActionListener(new OperationButtonActionListener());
×
119
        operationPanel.add(statButton);
×
120
        operationPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
×
121
        operationPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
×
122
        operationPanel.add(cancelButton);
×
123
        operationPanel.add(Box.createHorizontalGlue());
×
124

125
        progressBar = new JProgressBar();
×
126
        progressBar.setStringPainted(true);
×
127
        progressBar.setString("Ready");
×
128

129
        mainPanel.add(fileListPanel);
×
130
        mainPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
×
131
        mainPanel.add(checkOptionPanel);
×
132
        mainPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
×
133
        mainPanel.add(searchOptionPanel);
×
134
        mainPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
×
135
        mainPanel.add(operationPanel);
×
136
        mainPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
×
137
        mainPanel.add(progressBar);
×
138
    }
×
139

140
    private void createResultPanel() {
141
        resultPanel = new JPanel();
×
142
        resultPanel.setLayout(new BoxLayout(resultPanel, BoxLayout.Y_AXIS));
×
143

144
        resultTableModel = new PdfFilesTableModel(new Vector<>(), PdfFilesConstants.COLUMN_NAMES);
×
145
        resultTable = new JTable(resultTableModel);
×
146
        resultTable.setDefaultRenderer(Vector.class, new PdfFilesTableCellRenderer());
×
147
        for (int i = 0; i < resultTable.getColumnCount(); i++) {
×
148
            resultTable.getColumn(resultTable.getColumnName(i)).setCellRenderer(new PdfFilesTableCellRenderer());
×
149
        }
150
        resultTable.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
×
151
        JScrollPane scrollPane = new JScrollPane(resultTable);
×
152

153
        JPanel statInfoPanel = new JPanel();
×
154
        statInfoPanel.setLayout(new BoxLayout(statInfoPanel, BoxLayout.X_AXIS));
×
155
        statInfoLabel = new JLabel("");
×
156
        statInfoPanel.add(statInfoLabel);
×
157
        statInfoPanel.add(Box.createHorizontalGlue());
×
158

159
        resultPanel.add(scrollPane);
×
160
        resultPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
×
161
        resultPanel.add(statInfoPanel);
×
162
    }
×
163

164
    private void processFile(File file) {
165
        if (pageCountRadioButton.isSelected()) {
×
166
            Vector<Object> fileVector = getRowVector(file);
×
167
            resultFileList.add(fileVector);
×
168
        } else {
×
169
            logger.error("Invalid option selected");
×
170
        }
171
    }
×
172

173
    class OperationButtonActionListener implements ActionListener {
×
174
        @Override
175
        public void actionPerformed(ActionEvent e) {
176
            Object source = e.getSource();
×
177
            if (source.equals(statButton)) {
×
178
                statButton.setEnabled(false);
×
179
                cancelButton.setEnabled(true);
×
180
                searchThread = new SearchThread(isRecursiveSearched.isSelected());
×
181
                searchThread.start();
×
182
            } else if (source.equals(cancelButton)) {
×
183
                statButton.setEnabled(true);
×
184
                cancelButton.setEnabled(false);
×
185
                if (searchThread.isAlive()) {
×
186
                    searchThread.interrupt();
×
187
                    searchThread.executorService.shutdownNow();
×
188
                }
189
            }
190

191
        }
×
192
    }
193

194
    private void showResult() {
195
        SwingUtilities.invokeLater(() -> {
×
196
            int index = 0;
×
197
            for (Vector<Object> file : resultFileList) {
×
198
                file.add(0, ++index);
×
199
                resultTableModel.addRow(file);
×
200
            }
×
201
            tabbedPane.setSelectedIndex(1);
×
202
            statInfoLabel.setText("Page Count: " + totalPageCount + ", Total Size: " + FileUtils.sizeOfInHumanFormat(totalFileSize));
×
203
        });
×
204
    }
×
205

206
    private Vector<Object> getRowVector(File file) {
207
        Vector<Object> rowData = new Vector<>();
×
208
        rowData.add(file.getParent());
×
209
        rowData.add(file.getName());
×
210
        totalFileSize += file.length();
×
211
        rowData.add(FileUtils.sizeOfInHumanFormat(file));
×
212
        rowData.add(DateUtils.millisecondToHumanFormat(file.lastModified()));
×
213
        int pageCount = PdfUtils.getPageCount(file);
×
214
        totalPageCount += pageCount;
×
215
        rowData.add(pageCount);
×
216
        return rowData;
×
217
    }
218

219
    class SearchThread extends Thread {
220
        public final ExecutorService executorService;
221
        private final AtomicInteger processedFiles = new AtomicInteger(0);
×
222
        private int totalFiles = 0;
×
223
        private final boolean isRecursiveSearched;
224

225
        public SearchThread(boolean isRecursiveSearched) {
×
226
            super();
×
227
            this.isRecursiveSearched = isRecursiveSearched;
×
228
            this.executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
×
229

230
            resultFileList.clear();
×
231
            totalFileSize = 0;
×
232
            totalPageCount = 0;
×
233

234
            SwingUtilities.invokeLater(() -> {
×
235
                progressBar.setValue(0);
×
236
                progressBar.setString("Starting search...");
×
237
                resultTableModel.setRowCount(0);
×
238
                statInfoLabel.setText("");
×
239
            });
×
240
        }
×
241

242
        @Override
243
        public void run() {
244
            try {
245
                List<File> fileList = fileListPanel.getFileList();
×
246
                Set<File> fileSet = new TreeSet<>();
×
247
                String[] extensions = new String[]{"pdf", "PDF"};
×
248
                for (File file : fileList) {
×
249
                    fileSet.addAll(FileUtils.listFiles(file, extensions, isRecursiveSearched));
×
250
                }
×
251

252
                List<Future<?>> futures = new ArrayList<>();
×
253
                totalFiles = fileSet.size();
×
254
                updateProgress();
×
255

256
                for (File file : fileSet) {
×
257
                    if (currentThread().isInterrupted()) {
×
258
                        return;
×
259
                    }
260
                    futures.add(executorService.submit(() -> {
×
261
                        if (currentThread().isInterrupted()) {
×
262
                            return null;
×
263
                        }
264
                        processFile(file);
×
265
                        incrementProcessedFiles();
×
266
                        return null;
×
267
                    }));
268
                }
×
269

270
                // Wait for all tasks to complete
271
                for (Future<?> future : futures) {
×
272
                    try {
273
                        future.get();
×
274
                    } catch (InterruptedException e) {
×
275
                        logger.error("Search interrupted", e);
×
276
                        currentThread().interrupt(); // Restore interrupted status
×
277
                        return;
×
278
                    }
×
279
                }
×
280

281
                showResult();
×
282
            } catch (Exception e) {
×
283
                logger.error("Search failed", e);
×
284
                SwingUtilities.invokeLater(() -> progressBar.setString("Search failed"));
×
285
            } finally {
286
                executorService.shutdown();
×
287
                SwingUtilities.invokeLater(() -> {
×
288
                    statButton.setEnabled(true);
×
289
                    cancelButton.setEnabled(false);
×
290
                });
×
291
            }
292
        }
×
293

294
        private void incrementProcessedFiles() {
295
            processedFiles.incrementAndGet();
×
296
            updateProgress();
×
297
        }
×
298

299
        private void updateProgress() {
300
            if (totalFiles > 0) {
×
301
                SwingUtilities.invokeLater(() -> {
×
302
                    int processed = processedFiles.get();
×
303
                    int percentage = (int) (processed * 100.0 / totalFiles);
×
304
                    progressBar.setValue(percentage);
×
305
                    progressBar.setString(String.format("Processing: %d/%d files (%d%%)", processed, totalFiles, percentage));
×
306
                });
×
307
            }
308
        }
×
309
    }
310
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc