View Javadoc
1   /**
2    * 
3    */
4   package edu.jiangxin.apktoolbox.android.i18n;
5   
6   import edu.jiangxin.apktoolbox.swing.extend.EasyPanel;
7   import edu.jiangxin.apktoolbox.swing.extend.listener.SelectDirectoryListener;
8   import edu.jiangxin.apktoolbox.utils.Constants;
9   import edu.jiangxin.apktoolbox.utils.SAXBuilderHelper;
10  import org.apache.commons.collections4.map.HashedMap;
11  import org.apache.commons.io.FileUtils;
12  import org.apache.commons.lang3.StringUtils;
13  import org.apache.commons.lang3.Strings;
14  import org.jdom2.Document;
15  import org.jdom2.Element;
16  import org.jdom2.JDOMException;
17  import org.jdom2.input.SAXBuilder;
18  import org.jdom2.output.Format;
19  import org.jdom2.output.XMLOutputter;
20  
21  import javax.swing.*;
22  import java.awt.*;
23  import java.io.*;
24  import java.util.ArrayList;
25  import java.util.Arrays;
26  import java.util.List;
27  import java.util.Map;
28  
29  /**
30   * @author jiangxin
31   * @author 2019-04-12
32   *
33   */
34  public class I18nAddPanel extends EasyPanel {
35      
36      @Serial
37      private static final long serialVersionUID = 1L;
38      
39      private static final String CHARSET = "UTF-8";
40  
41      private static final boolean REMOVE_LAST_LF_OPEN = true;
42  
43      private static final Map<String, String> replace = new HashedMap<>();
44  
45      private JTextField srcTextField;
46  
47      private JTextField targetTextField;
48  
49      private JTextField itemTextField;
50  
51      private int operationCount = 0;
52  
53      static {
54          replace.put("&quot;", "jiangxin001");
55          replace.put("&#160;", "jiangxin002");
56      }
57      
58      public I18nAddPanel() throws HeadlessException {
59          super();
60      }
61  
62      @Override
63      public void initUI() {
64          BoxLayout boxLayout = new BoxLayout(this, BoxLayout.Y_AXIS);
65          setLayout(boxLayout);
66  
67          createSourcePanel();
68          add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
69          createTargetPanel();
70          add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
71          createItemPanel();
72          add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
73          createOperationPanel();
74      }
75  
76      private void createOperationPanel() {
77          JPanel operationPanel = new JPanel();
78          operationPanel.setLayout(new BoxLayout(operationPanel, BoxLayout.X_AXIS));
79          add(operationPanel);
80  
81          JButton addButton = new JButton(bundle.getString("android.i18n.add.title"));
82          addButton.addActionListener(e -> {
83              String srcPath = checkAndGetDirContent(srcTextField, "android.i18n.add.src.dir", "Source directory is invalid");
84              if (srcPath == null) {
85                  return;
86              }
87  
88              String targetPath = checkAndGetDirContent(targetTextField, "android.i18n.add.target.dir", "Target directory is invalid");
89              if (targetPath == null) {
90                  return;
91              }
92  
93              String itemStr = checkAndGetStringContent(itemTextField, "android.i18n.add.items", "Items is empty");
94              if (itemStr == null) {
95                  return;
96              }
97  
98              List<String> items = new ArrayList<>(Arrays.asList(itemStr.split(";")));
99              operationCount = 0;
100 
101             for (String item : items) {
102                 int ret = innerProcessor(srcPath, targetPath, item);
103                 if (ret != 0) {
104                     Toolkit.getDefaultToolkit().beep();
105                     JOptionPane.showMessageDialog(this, "Failed, please see the log", "ERROR",
106                             JOptionPane.ERROR_MESSAGE);
107                     return;
108                 }
109             }
110             String message = String.format("Success: items: %d", operationCount);
111             JOptionPane.showMessageDialog(this, message, "INFO", JOptionPane.INFORMATION_MESSAGE);
112         });
113 
114         operationPanel.add(addButton);
115     }
116 
117     private void createItemPanel() {
118         JPanel itemPanel = new JPanel();
119         itemPanel.setLayout(new BoxLayout(itemPanel, BoxLayout.X_AXIS));
120         add(itemPanel);
121         
122         itemTextField = new JTextField();
123         itemTextField.setText(conf.getString("android.i18n.add.items"));
124 
125         JLabel itemLabel = new JLabel("Items");
126 
127         itemPanel.add(itemTextField);
128         itemPanel.add(Box.createHorizontalGlue());
129         itemPanel.add(itemLabel);
130     }
131 
132     private void createTargetPanel() {
133         JPanel targetPanel = new JPanel();
134         targetPanel.setLayout(new BoxLayout(targetPanel, BoxLayout.X_AXIS));
135         add(targetPanel);
136         
137         targetTextField = new JTextField();
138         targetTextField.setText(conf.getString("android.i18n.add.target.dir"));
139 
140         JButton targetButton = new JButton("Save Directory");
141         targetButton.addActionListener(new SelectDirectoryListener("save to", targetTextField));
142 
143         targetPanel.add(targetTextField);
144         targetPanel.add(Box.createHorizontalGlue());
145         targetPanel.add(targetButton);
146     }
147 
148     private void createSourcePanel() {
149         JPanel sourcePanel = new JPanel();
150         sourcePanel.setLayout(new BoxLayout(sourcePanel, BoxLayout.X_AXIS));
151         add(sourcePanel);
152         
153         srcTextField = new JTextField();
154         srcTextField.setText(conf.getString("android.i18n.add.src.dir"));
155 
156         JButton srcButton = new JButton("Source Directory");
157         srcButton.addActionListener(new SelectDirectoryListener("select a directory", srcTextField));
158 
159         sourcePanel.add(srcTextField);
160         sourcePanel.add(Box.createHorizontalGlue());
161         sourcePanel.add(srcButton);
162     }
163 
164     private int innerProcessor(String sourceBaseStr, String targetBaseStr, String itemName) {
165         if (StringUtils.isAnyEmpty(sourceBaseStr, targetBaseStr, itemName)) {
166             logger.error("params are invalid: sourceBaseStr: {}, targetBaseStr: {}, itemName: {}", sourceBaseStr, targetBaseStr, itemName);
167             return -1;
168         }
169         File sourceBaseFile = new File(sourceBaseStr);
170         File targetBaseFile = new File(targetBaseStr);
171         int count = 0;
172 
173         File[] sourceParentFiles = sourceBaseFile.listFiles(new FileFilter() {
174             @Override
175             public boolean accept(File pathname) {
176                 return pathname.getName().startsWith("values");
177             }
178         });
179         if (sourceParentFiles == null) {
180             logger.error("sourceParentFiles is null");
181             return -1;
182         }
183         for (File sourceParentFile : sourceParentFiles) {
184             File sourceFile = new File(sourceParentFile, "strings.xml");
185 
186             Element sourceElement = getSourceElement(sourceFile, itemName);
187             if (sourceElement == null) {
188                 logger.warn("sourceElement is null: {}", sourceFile);
189                 continue;
190             }
191 
192             File targetFile = new File(new File(targetBaseFile, sourceParentFile.getName()), "strings.xml");
193             if (!targetFile.exists()) {
194                 logger.warn("targetFile does not exist: {}", sourceFile);
195                 continue;
196             }
197             try {
198                 preProcess(targetFile);
199             } catch (IOException e) {
200                 logger.error("preProcess failed.", e);
201                 return -1;
202             }
203             boolean res = setTargetElement(targetFile, sourceElement, itemName);
204             if (!res) {
205                 logger.error("setTargetElement failed.");
206                 return -1;
207             }
208             try {
209                 postProcess(targetFile);
210             } catch (IOException e) {
211                 logger.error("postProcess failed.", e);
212                 return -1;
213             }
214             logger.info("count: {}, in path: {}, out path: {}", ++count, sourceFile, targetFile);
215         }
216         operationCount += count;
217         logger.info("finish one cycle");
218         return 0;
219     }
220 
221     private Element getSourceElement(File sourceFile, String itemName) {
222         if (!sourceFile.exists()) {
223             logger.warn("sourceFile does not exist: {}", sourceFile);
224             return null;
225         }
226         SAXBuilder builder = new SAXBuilder();
227         SAXBuilderHelper.setSecurityFeatures(builder);
228         Document sourceDoc = null;
229         try (InputStream in = new FileInputStream(sourceFile)) {
230             sourceDoc = builder.build(in);
231             logger.info("build source document: {}", sourceFile);
232         } catch (JDOMException | IOException e) {
233             logger.error("build source document failed: {}", sourceFile);
234             return null;
235         }
236         if (sourceDoc == null) {
237             logger.error("sourceDoc is null");
238             return null;
239         }
240         Element sourceElement = null;
241         for (Element sourceChild : sourceDoc.getRootElement().getChildren()) {
242             String sourceValue = sourceChild.getAttributeValue("name");
243             if (sourceValue != null && sourceValue.equals(itemName)) {
244                 sourceElement = sourceChild.clone();
245                 break;
246             }
247         }
248         return sourceElement;
249     }
250 
251     private boolean setTargetElement(File targetFile, Element sourceElement, String itemName) {
252         SAXBuilder builder = new SAXBuilder();
253         SAXBuilderHelper.setSecurityFeatures(builder);
254         Document targetDoc;
255         try {
256             targetDoc = builder.build(targetFile);
257             logger.info("build target document: {}", targetFile);
258         } catch (JDOMException | IOException e) {
259             logger.error("build target document failed: {}", targetFile);
260             return false;
261         }
262         Element targetRoot = targetDoc.getRootElement();
263         boolean isFinished = false;
264         for (Element targetChild : targetRoot.getChildren()) {
265             String targetValue = targetChild.getAttributeValue("name");
266             if (targetValue != null && targetValue.equals(itemName)) {
267                 targetChild.setText(sourceElement.getText());
268                 isFinished = true;
269                 break;
270             }
271         }
272         if (!isFinished) {
273             targetRoot.addContent("    ");
274             targetRoot.addContent(sourceElement);
275             targetRoot.addContent("\n");
276         }
277         XMLOutputter out = new XMLOutputter();
278         Format format = Format.getRawFormat();
279         format.setEncoding("UTF-8");
280         format.setLineSeparator("\n");
281         out.setFormat(format);
282         OutputStream os = null;
283         try {
284             os = new FileOutputStream(targetFile);
285             out.output(targetDoc, os);
286         } catch (IOException e) {
287             logger.error("output fail", e);
288             return false;
289         } finally {
290             if (os != null) {
291                 try {
292                     os.close();
293                 } catch (IOException e) {
294                     logger.error("close output stream exception", e);
295                 }
296             }
297         }
298         return true;
299     }
300 
301     private static void preProcess(File file) throws IOException {
302         String content = FileUtils.readFileToString(file, CHARSET);
303         for (Map.Entry<String, String> entry : replace.entrySet()) {
304             content = content.replaceAll(entry.getKey(), entry.getValue());
305         }
306         FileUtils.writeStringToFile(file, content, CHARSET);
307     }
308 
309     private static void postProcess(File file) throws IOException {
310         String content = FileUtils.readFileToString(file, CHARSET);
311         for (Map.Entry<String, String> entry : replace.entrySet()) {
312             content = content.replaceAll(entry.getValue(), entry.getKey());
313         }
314         if (REMOVE_LAST_LF_OPEN) {
315             content = Strings.CS.removeEnd(content, "\n");
316         }
317         FileUtils.writeStringToFile(file, content, CHARSET);
318     }
319 
320 }