1 package edu.jiangxin.apktoolbox.file.batchrename;
2
3 import edu.jiangxin.apktoolbox.swing.extend.listener.SelectDirectoryListener;
4 import edu.jiangxin.apktoolbox.swing.extend.EasyPanel;
5 import edu.jiangxin.apktoolbox.utils.Constants;
6 import org.apache.commons.io.FileUtils;
7 import org.apache.commons.io.FilenameUtils;
8 import org.apache.commons.lang3.StringUtils;
9
10 import javax.swing.*;
11 import java.awt.*;
12 import java.awt.event.ActionEvent;
13 import java.awt.event.ActionListener;
14 import java.io.File;
15 import java.io.IOException;
16 import java.nio.file.Files;
17 import java.nio.file.Path;
18 import java.util.Collection;
19 import java.util.Set;
20 import java.util.TreeSet;
21
22
23 public class BatchRenamePanel extends EasyPanel {
24 private static final String STRING_TYPE_PARENT_NAME = "使用父目录名";
25
26 private static final String STRING_TYPE_PARENT_PATH = "使用目录路径";
27
28 private static final String STRING_TYPE_SPECIALISE_STRING = "使用指定字符";
29
30 private JPanel warningPanel;
31
32 private JPanel sourcePanel;
33
34 private JPanel rulePanel;
35
36 private JPanel operationPanel;
37
38 private JPanel statusPanel;
39
40 private JTextField sourceTextField;
41 private JTextField suffixTextField;
42 private JCheckBox recursiveCheckBox;
43 private JRadioButton ruleRadioButton1;
44 private JRadioButton ruleRadioButton2;
45 private JRadioButton ruleRadioButton3;
46 private JRadioButton ruleRadioButton4;
47 private JRadioButton ruleRadioButton5;
48 private JRadioButton ruleRadioButton6;
49 private JTextField textField21;
50 private JSpinner spinner21;
51 private JCheckBox checkBox31;
52 private JCheckBox checkBox32;
53 private JCheckBox checkBox33;
54 private JTextField textField31;
55 private JTextField textField32;
56 private JSpinner spinner31;
57 private JSpinner spinner32;
58 private JSpinner spinner41;
59 private JComboBox<String> comboBoxStringType;
60 private JTextField textField43;
61 private JSpinner spinner51;
62 private JSpinner spinner52;
63 private JTextField textField61;
64 private JTextField textField62;
65 private JTextField currentTextField;
66
67 public BatchRenamePanel() throws HeadlessException {
68 super();
69 }
70
71 @Override
72 public void initUI() {
73 BoxLayout boxLayout = new BoxLayout(this, BoxLayout.Y_AXIS);
74 setLayout(boxLayout);
75
76 createWarningPanel();
77 add(warningPanel);
78 add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
79
80 createSourcePanel();
81 add(sourcePanel);
82 add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
83
84 createRulePanel();
85 add(rulePanel);
86 add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
87
88 createOperationPanel();
89 add(operationPanel);
90 add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
91
92 createStatusPanel();
93 add(statusPanel);
94 }
95
96 private void createWarningPanel() {
97 warningPanel = new JPanel();
98 warningPanel.setLayout(new BoxLayout(warningPanel, BoxLayout.X_AXIS));
99
100 JTextArea warningTextArea = new JTextArea();
101 warningTextArea.setText("It is dangerous to operate on the original files! \nPlease back up at first and check the result carefully at end!");
102 warningTextArea.setForeground(Color.RED);
103 warningTextArea.setFont(new Font("宋体", Font.BOLD, 20));
104 warningTextArea.setBorder(null);
105 warningTextArea.setEditable(false);
106
107 warningPanel.add(warningTextArea);
108 warningPanel.add(Box.createHorizontalGlue());
109 }
110
111 private void createSourcePanel() {
112 sourcePanel = new JPanel();
113 sourcePanel.setBorder(BorderFactory.createTitledBorder("1. 源文件"));
114 sourcePanel.setLayout(new BorderLayout());
115
116 JPanel secondLevelPanel = new JPanel();
117 secondLevelPanel.setLayout(new BoxLayout(secondLevelPanel, BoxLayout.Y_AXIS));
118 sourcePanel.add(secondLevelPanel);
119
120 JPanel thirdLevelPanel1 = new JPanel();
121 thirdLevelPanel1.setLayout(new BoxLayout(thirdLevelPanel1, BoxLayout.X_AXIS));
122
123 JPanel thirdLevelPanel2 = new JPanel();
124 thirdLevelPanel2.setLayout(new BoxLayout(thirdLevelPanel2, BoxLayout.X_AXIS));
125
126 secondLevelPanel.add(thirdLevelPanel1);
127 secondLevelPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
128 secondLevelPanel.add(thirdLevelPanel2);
129
130 JLabel label = new JLabel("源文件所在目录:");
131 sourceTextField = new JTextField();
132 JButton button = new JButton("选择");
133 button.addActionListener(new SelectDirectoryListener("Select a directory", sourceTextField));
134 thirdLevelPanel1.add(label);
135 thirdLevelPanel1.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
136 thirdLevelPanel1.add(sourceTextField);
137 thirdLevelPanel1.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
138 thirdLevelPanel1.add(button);
139
140 JLabel label2 = new JLabel("后缀:");
141 suffixTextField = new JTextField();
142 suffixTextField.setToolTipText("an array of extensions, ex. java,xml,mp4. If this parameter is empty, all files are returned.");
143 recursiveCheckBox = new JCheckBox("包括子目录");
144
145 thirdLevelPanel2.add(label2);
146 thirdLevelPanel2.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
147 thirdLevelPanel2.add(suffixTextField);
148 thirdLevelPanel2.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
149 thirdLevelPanel2.add(recursiveCheckBox);
150 }
151
152 private void createRulePanel() {
153 rulePanel = new JPanel();
154 rulePanel.setBorder(BorderFactory.createTitledBorder("3. 更名规则"));
155 rulePanel.setLayout(new BorderLayout());
156
157 JPanel secondLevelPanel = new JPanel();
158 secondLevelPanel.setLayout(new BoxLayout(secondLevelPanel, BoxLayout.Y_AXIS));
159 rulePanel.add(secondLevelPanel);
160
161 JPanel thirdLevelPanel1 = new JPanel();
162 thirdLevelPanel1.setLayout(new FlowLayout(FlowLayout.LEFT,10,3));
163
164 JPanel thirdLevelPanel2 = new JPanel();
165 thirdLevelPanel2.setLayout(new FlowLayout(FlowLayout.LEFT,10,3));
166
167 JPanel thirdLevelPanel3 = new JPanel();
168 thirdLevelPanel3.setLayout(new FlowLayout(FlowLayout.LEFT,10,3));
169
170 JPanel thirdLevelPanel4 = new JPanel();
171 thirdLevelPanel4.setLayout(new FlowLayout(FlowLayout.LEFT,10,3));
172
173 JPanel thirdLevelPanel5 = new JPanel();
174 thirdLevelPanel5.setLayout(new FlowLayout(FlowLayout.LEFT,10,3));
175
176 JPanel thirdLevelPanel6 = new JPanel();
177 thirdLevelPanel6.setLayout(new FlowLayout(FlowLayout.LEFT,10,3));
178
179 secondLevelPanel.add(thirdLevelPanel1);
180 secondLevelPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
181 secondLevelPanel.add(thirdLevelPanel2);
182 secondLevelPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
183 secondLevelPanel.add(thirdLevelPanel3);
184 secondLevelPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
185 secondLevelPanel.add(thirdLevelPanel4);
186 secondLevelPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
187 secondLevelPanel.add(thirdLevelPanel5);
188 secondLevelPanel.add(Box.createHorizontalStrut(Constants.DEFAULT_X_BORDER));
189 secondLevelPanel.add(thirdLevelPanel6);
190
191 ButtonGroup buttonGroup = new ButtonGroup();
192
193 {
194 ruleRadioButton1 = new JRadioButton("只在文件重名时才更名");
195 ruleRadioButton1.setSelected(true);
196 thirdLevelPanel1.add(ruleRadioButton1);
197 buttonGroup.add(ruleRadioButton1);
198 }
199
200 {
201 ruleRadioButton2 = new JRadioButton("将前缀{String}后的数字改为{Int}位");
202 thirdLevelPanel2.add(ruleRadioButton2);
203 buttonGroup.add(ruleRadioButton2);
204
205 JLabel label21 = new JLabel("String");
206 textField21 = new JTextField("");
207 JLabel label22 = new JLabel("Int");
208 spinner21 = new JSpinner();
209 spinner21.setValue(4);
210 thirdLevelPanel2.add(label21);
211 thirdLevelPanel2.add(textField21);
212 thirdLevelPanel2.add(label22);
213 thirdLevelPanel2.add(spinner21);
214 }
215
216 {
217 ruleRadioButton3 = new JRadioButton("重编号所有文件");
218 thirdLevelPanel3.add(ruleRadioButton3);
219 buttonGroup.add(ruleRadioButton3);
220
221 checkBox31 = new JCheckBox("编号先按原文件名排序");
222 checkBox32 = new JCheckBox("按末尾数字排序");
223 checkBox33 = new JCheckBox("用目录名为前缀");
224 JLabel label31 = new JLabel("分隔字符");
225 textField31 = new JTextField("_");
226 JLabel label32 = new JLabel("前缀");
227 textField32 = new JTextField("File");
228 JLabel label33 = new JLabel("起始号码");
229 spinner31 = new JSpinner();
230 spinner31.setValue(1);
231 JLabel label34 = new JLabel("号码位数");
232 spinner32 = new JSpinner();
233 spinner32.setValue(3);
234 thirdLevelPanel3.add(checkBox31);
235 thirdLevelPanel3.add(checkBox32);
236 thirdLevelPanel3.add(checkBox33);
237 thirdLevelPanel3.add(label31);
238 thirdLevelPanel3.add(textField31);
239 thirdLevelPanel3.add(label32);
240 thirdLevelPanel3.add(textField32);
241 thirdLevelPanel3.add(label33);
242 thirdLevelPanel3.add(spinner31);
243 thirdLevelPanel3.add(label34);
244 thirdLevelPanel3.add(spinner32);
245 }
246
247 {
248 ruleRadioButton4 = new JRadioButton("在第{Int}个字符后添加{String}");
249 thirdLevelPanel4.add(ruleRadioButton4);
250 buttonGroup.add(ruleRadioButton4);
251
252 JLabel stringType = new JLabel("字符串类型");
253
254 comboBoxStringType = new JComboBox<>();
255 comboBoxStringType.addItem(STRING_TYPE_PARENT_NAME);
256 comboBoxStringType.addItem(STRING_TYPE_PARENT_PATH);
257 comboBoxStringType.addItem(STRING_TYPE_SPECIALISE_STRING);
258 comboBoxStringType.setSelectedItem(STRING_TYPE_SPECIALISE_STRING);
259 comboBoxStringType.addItemListener(e -> textField43.setEditable(e.getItem().equals(STRING_TYPE_SPECIALISE_STRING)));
260
261 JLabel label41 = new JLabel("Int");
262 spinner41 = new JSpinner();
263 spinner41.setValue(0);
264
265 JLabel label43 = new JLabel("String");
266 textField43 = new JTextField("");
267
268 thirdLevelPanel4.add(label41);
269 thirdLevelPanel4.add(spinner41);
270 thirdLevelPanel4.add(label43);
271 thirdLevelPanel4.add(textField43);
272 thirdLevelPanel4.add(stringType);
273 thirdLevelPanel4.add(comboBoxStringType);
274 }
275
276 {
277 ruleRadioButton5 = new JRadioButton("删除自第{Int:1}个字符开始的{Int:2}个字符");
278 thirdLevelPanel5.add(ruleRadioButton5);
279 buttonGroup.add(ruleRadioButton5);
280
281 JLabel label51 = new JLabel("Int:1");
282 spinner51 = new JSpinner();
283 spinner51.setValue(1);
284 JLabel label52 = new JLabel("Int:2");
285 spinner52 = new JSpinner();
286 spinner52.setValue(1);
287 thirdLevelPanel5.add(label51);
288 thirdLevelPanel5.add(spinner51);
289 thirdLevelPanel5.add(label52);
290 thirdLevelPanel5.add(spinner52);
291 }
292
293 {
294 ruleRadioButton6 = new JRadioButton("将文件名中出现的字符串{String:1}替换为{String:2}");
295 thirdLevelPanel6.add(ruleRadioButton6);
296 buttonGroup.add(ruleRadioButton6);
297
298 JLabel label61 = new JLabel("String:1");
299 textField61 = new JTextField("");
300 JLabel label62 = new JLabel("String:2");
301 textField62 = new JTextField("");
302 thirdLevelPanel6.add(label61);
303 thirdLevelPanel6.add(textField61);
304 thirdLevelPanel6.add(label62);
305 thirdLevelPanel6.add(textField62);
306 }
307 }
308
309 private void createOperationPanel() {
310 operationPanel = new JPanel();
311 operationPanel.setBorder(BorderFactory.createTitledBorder("6. 操作"));
312 operationPanel.setLayout(new BorderLayout());
313
314 JPanel secondLevelPanel = new JPanel();
315 secondLevelPanel.setLayout(new BoxLayout(secondLevelPanel, BoxLayout.X_AXIS));
316 operationPanel.add(secondLevelPanel);
317
318 JButton button = new JButton("开始");
319 button.addActionListener(new StartButtonActionListener());
320 secondLevelPanel.add(button);
321 secondLevelPanel.add(Box.createHorizontalGlue());
322 }
323
324 private void createStatusPanel() {
325 statusPanel = new JPanel();
326 statusPanel.setBorder(BorderFactory.createTitledBorder("正在处理"));
327 statusPanel.setLayout(new BorderLayout());
328
329 JPanel secondLevelPanel = new JPanel();
330 secondLevelPanel.setLayout(new BoxLayout(secondLevelPanel, BoxLayout.Y_AXIS));
331 statusPanel.add(secondLevelPanel);
332
333 currentTextField = new JTextField();
334 secondLevelPanel.add(currentTextField);
335 }
336
337 private final class StartButtonActionListener implements ActionListener {
338 @Override
339 public void actionPerformed(ActionEvent e) {
340 String sourceString = sourceTextField.getText();
341 if (StringUtils.isEmpty(sourceString)) {
342 JOptionPane.showMessageDialog(BatchRenamePanel.this, "sourceString is empty", "ERROR",
343 JOptionPane.ERROR_MESSAGE);
344 return;
345 }
346 File sourceFile = new File(sourceString);
347 if (!sourceFile.exists() || !sourceFile.isDirectory()) {
348 JOptionPane.showMessageDialog(BatchRenamePanel.this, "sourceFile does not exist or not directory", "ERROR",
349 JOptionPane.ERROR_MESSAGE);
350 return;
351 }
352 String[] extensions = null;
353 if (StringUtils.isNotEmpty(suffixTextField.getText())) {
354 extensions = suffixTextField.getText().split(",");
355 }
356 Collection<File> fileCollection = FileUtils.listFiles(sourceFile, extensions, recursiveCheckBox.isSelected());
357 Set<File> fileSet = new TreeSet<>(fileCollection);
358 if (fileSet.isEmpty()) {
359 JOptionPane.showMessageDialog(BatchRenamePanel.this, "fileSet is empty", "ERROR",
360 JOptionPane.ERROR_MESSAGE);
361 return;
362 }
363 for (File file : fileSet) {
364 renameSingleFile(file);
365 }
366 }
367 }
368
369 private void renameSingleFile(File file) {
370 String sourceFile = FilenameUtils.normalizeNoEndSeparator(file.getAbsolutePath());
371 String sourceFileName = FilenameUtils.getName(sourceFile);
372 String currentDir = FilenameUtils.getPath(sourceFile);
373 String targetFileName = "";
374 if (ruleRadioButton1.isSelected()) {
375 logger.info("unimplemented");
376 } else if (ruleRadioButton2.isSelected()) {
377 targetFileName = getNewFileNameWhen2Selected(sourceFileName);
378 } else if (ruleRadioButton3.isSelected()) {
379 targetFileName = getNewFileNameWhen3Selected();
380 } else if (ruleRadioButton4.isSelected()) {
381 targetFileName = getNewFileNameWhen4Selected(file);
382 } else if (ruleRadioButton5.isSelected()) {
383 targetFileName = getNewFileNameWhen5Selected(sourceFileName);
384 } else if (ruleRadioButton6.isSelected()) {
385 targetFileName = getNewFileNameWhen6Selected(sourceFileName);
386 } else {
387 logger.error("ruleRadioButton select status error");
388 return;
389 }
390
391 try {
392 Path sourcePath = new File(sourceFile).toPath();
393 Files.move(sourcePath, sourcePath.resolveSibling(targetFileName));
394 StringBuilder sb = new StringBuilder();
395 sb.append(currentDir).append(" [").append(sourceFileName).append("]->[").append(targetFileName).append("]");
396 currentTextField.setText(sb.toString());
397 logger.info(sb.toString());
398 } catch (IOException e) {
399 logger.error("copy file failed: IOException. current: " + sourceFile);
400 }
401 }
402
403 private String getNewFileNameWhen2Selected(String oldFileName) {
404 String prefix = textField21.getText();
405 String sourceFileNameWithoutSuffix = FilenameUtils.getBaseName(oldFileName);
406 String serialNumberStr = StringUtils.substringAfter(sourceFileNameWithoutSuffix, prefix);
407 int serialNumber = Integer.parseInt(serialNumberStr);
408 int digit = (Integer) spinner21.getValue();
409 String format = "%0" + digit + "d";
410 return oldFileName.replaceAll(serialNumberStr, String.format(format, serialNumber));
411 }
412
413 private String getNewFileNameWhen3Selected() {
414 String separator = textField31.getText();
415 String prefix = textField32.getText();
416 int start = (Integer) spinner31.getValue();
417 int digit = (Integer) spinner32.getValue();
418 boolean isCheckBox31Selected = checkBox31.isSelected();
419 boolean isCheckBox32Selected = checkBox32.isSelected();
420 boolean isCheckBox33Selected = checkBox33.isSelected();
421 logger.info("unimplemented: separator: {}, prefix: {}, start: {}, digit: {}", separator, prefix, start, digit);
422 logger.info("unimplemented: isCheckBox31Selected: {}, isCheckBox32Selected: {}, isCheckBox33Selected: {}",
423 isCheckBox31Selected, isCheckBox32Selected, isCheckBox33Selected);
424 return "";
425 }
426
427 private String getNewFileNameWhen4Selected(File file) {
428 String sourceFile = FilenameUtils.normalizeNoEndSeparator(file.getAbsolutePath());
429 String sourceFileName = FilenameUtils.getName(sourceFile);
430 int index = (Integer) spinner41.getValue();
431 String insertString = "";
432 switch ((String) comboBoxStringType.getSelectedItem()) {
433 case STRING_TYPE_PARENT_PATH:
434 break;
435 case STRING_TYPE_PARENT_NAME:
436 String parentFile = FilenameUtils.normalizeNoEndSeparator(file.getParentFile().getAbsolutePath());
437 insertString = FilenameUtils.getName(parentFile);
438 break;
439 case STRING_TYPE_SPECIALISE_STRING:
440 insertString = textField43.getText();
441 break;
442 }
443 return StringUtils.substring(sourceFileName, 0, index) + insertString + StringUtils.substring(sourceFileName, index);
444 }
445
446 private String getNewFileNameWhen5Selected(String oldFileName) {
447 int start = (Integer) spinner51.getValue();
448 int length = (Integer) spinner52.getValue();
449 return StringUtils.substring(oldFileName, 0, start - 1) + StringUtils.substring(oldFileName, start + length - 1);
450 }
451
452 private String getNewFileNameWhen6Selected(String oldFileName) {
453 return StringUtils.replace(oldFileName, textField61.getText(), textField62.getText());
454 }
455 }