I18nAddPanel.java

/**
 * 
 */
package edu.jiangxin.apktoolbox.android.i18n;

import edu.jiangxin.apktoolbox.swing.extend.EasyPanel;
import edu.jiangxin.apktoolbox.swing.extend.listener.SelectDirectoryListener;
import edu.jiangxin.apktoolbox.utils.Constants;
import org.apache.commons.collections4.map.HashedMap;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;

import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * @author jiangxin
 * @author 2019-04-12
 *
 */
public class I18nAddPanel extends EasyPanel {
    
    @Serial
    private static final long serialVersionUID = 1L;
    
    private static final String CHARSET = "UTF-8";

    private static final boolean REMOVE_LAST_LF_OPEN = true;

    private static final Map<String, String> replace = new HashedMap<>();

    private JTextField srcTextField;

    private JTextField targetTextField;

    private JTextField itemTextField;

    private int operationCount = 0;

    static {
        replace.put("&quot;", "jiangxin001");
        replace.put("&#160;", "jiangxin002");
    }
    
    public I18nAddPanel() throws HeadlessException {
        super();
    }

    @Override
    public void initUI() {
        BoxLayout boxLayout = new BoxLayout(this, BoxLayout.Y_AXIS);
        setLayout(boxLayout);

        createSourcePanel();
        add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
        createTargetPanel();
        add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
        createItemPanel();
        add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
        createOperationPanel();
    }

    private void createOperationPanel() {
        JPanel operationPanel = new JPanel();
        operationPanel.setLayout(new BoxLayout(operationPanel, BoxLayout.X_AXIS));
        add(operationPanel);

        JButton addButton = new JButton(bundle.getString("android.i18n.add.title"));
        addButton.addActionListener(e -> {
            String srcPath = checkAndGetDirContent(srcTextField, "android.i18n.add.src.dir", "Source directory is invalid");
            if (srcPath == null) {
                return;
            }

            String targetPath = checkAndGetDirContent(targetTextField, "android.i18n.add.target.dir", "Target directory is invalid");
            if (targetPath == null) {
                return;
            }

            String itemStr = checkAndGetStringContent(itemTextField, "android.i18n.add.items", "Items is empty");
            if (itemStr == null) {
                return;
            }

            List<String> items = new ArrayList<>(Arrays.asList(itemStr.split(";")));
            operationCount = 0;

            for (String item : items) {
                int ret = innerProcessor(srcPath, targetPath, item);
                if (ret != 0) {
                    Toolkit.getDefaultToolkit().beep();
                    JOptionPane.showMessageDialog(I18nAddPanel.this, "Failed, please see the log", "ERROR",
                            JOptionPane.ERROR_MESSAGE);
                    return;
                }
            }
            String message = String.format("Success: items: %d", operationCount);
            JOptionPane.showMessageDialog(I18nAddPanel.this, message, "INFO", JOptionPane.INFORMATION_MESSAGE);
        });

        operationPanel.add(addButton);
    }

    private void createItemPanel() {
        JPanel itemPanel = new JPanel();
        itemPanel.setLayout(new BoxLayout(itemPanel, BoxLayout.X_AXIS));
        add(itemPanel);
        
        itemTextField = new JTextField();
        itemTextField.setText(conf.getString("android.i18n.add.items"));

        JLabel itemLabel = new JLabel("Items");

        itemPanel.add(itemTextField);
        itemPanel.add(Box.createHorizontalGlue());
        itemPanel.add(itemLabel);
    }

    private void createTargetPanel() {
        JPanel targetPanel = new JPanel();
        targetPanel.setLayout(new BoxLayout(targetPanel, BoxLayout.X_AXIS));
        add(targetPanel);
        
        targetTextField = new JTextField();
        targetTextField.setText(conf.getString("android.i18n.add.target.dir"));

        JButton targetButton = new JButton("Save Directory");
        targetButton.addActionListener(new SelectDirectoryListener("save to", targetTextField));

        targetPanel.add(targetTextField);
        targetPanel.add(Box.createHorizontalGlue());
        targetPanel.add(targetButton);
    }

    private void createSourcePanel() {
        JPanel sourcePanel = new JPanel();
        sourcePanel.setLayout(new BoxLayout(sourcePanel, BoxLayout.X_AXIS));
        add(sourcePanel);
        
        srcTextField = new JTextField();
        srcTextField.setText(conf.getString("android.i18n.add.src.dir"));

        JButton srcButton = new JButton("Source Directory");
        srcButton.addActionListener(new SelectDirectoryListener("select a directory", srcTextField));

        sourcePanel.add(srcTextField);
        sourcePanel.add(Box.createHorizontalGlue());
        sourcePanel.add(srcButton);
    }

    private int innerProcessor(String sourceBaseStr, String targetBaseStr, String itemName) {
        if (StringUtils.isAnyEmpty(sourceBaseStr, targetBaseStr, itemName)) {
            logger.error("params are invalid: sourceBaseStr: {}, targetBaseStr: {}, itemName: {}", sourceBaseStr, targetBaseStr, itemName);
            return -1;
        }
        File sourceBaseFile = new File(sourceBaseStr);
        File targetBaseFile = new File(targetBaseStr);
        int count = 0;

        File[] sourceParentFiles = sourceBaseFile.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                return pathname.getName().startsWith("values");
            }
        });
        if (sourceParentFiles == null) {
            logger.error("sourceParentFiles is null");
            return -1;
        }
        for (File sourceParentFile : sourceParentFiles) {
            File sourceFile = new File(sourceParentFile, "strings.xml");

            Element sourceElement = getSourceElement(sourceFile, itemName);
            if (sourceElement == null) {
                logger.warn("sourceElement is null: {}", sourceFile);
                continue;
            }

            File targetFile = new File(new File(targetBaseFile, sourceParentFile.getName()), "strings.xml");
            if (!targetFile.exists()) {
                logger.warn("targetFile does not exist: {}", sourceFile);
                continue;
            }
            try {
                preProcess(targetFile);
            } catch (IOException e) {
                logger.error("preProcess failed.", e);
                return -1;
            }
            boolean res = setTargetElement(targetFile, sourceElement, itemName);
            if (!res) {
                logger.error("setTargetElement failed.");
                return -1;
            }
            try {
                postProcess(targetFile);
            } catch (IOException e) {
                logger.error("postProcess failed.", e);
                return -1;
            }
            logger.info("count: {}, in path: {}, out path: {}", ++count, sourceFile, targetFile);
        }
        operationCount += count;
        logger.info("finish one cycle");
        return 0;
    }

    private Element getSourceElement(File sourceFile, String itemName) {
        if (!sourceFile.exists()) {
            logger.warn("sourceFile does not exist: {}", sourceFile);
            return null;
        }
        SAXBuilder builder = new SAXBuilder();
        Document sourceDoc = null;
        try (InputStream in = new FileInputStream(sourceFile)) {
            sourceDoc = builder.build(in);
            logger.info("build source document: {}", sourceFile);
        } catch (JDOMException | IOException e) {
            logger.error("build source document failed: {}", sourceFile);
            return null;
        }
        if (sourceDoc == null) {
            logger.error("sourceDoc is null");
            return null;
        }
        Element sourceElement = null;
        for (Element sourceChild : sourceDoc.getRootElement().getChildren()) {
            String sourceValue = sourceChild.getAttributeValue("name");
            if (sourceValue != null && sourceValue.equals(itemName)) {
                sourceElement = sourceChild.clone();
                break;
            }
        }
        return sourceElement;
    }

    private boolean setTargetElement(File targetFile, Element sourceElement, String itemName) {
        SAXBuilder builder = new SAXBuilder();
        Document targetDoc;
        try {
            targetDoc = builder.build(targetFile);
            logger.info("build target document: {}", targetFile);
        } catch (JDOMException | IOException e) {
            logger.error("build target document failed: {}", targetFile);
            return false;
        }
        Element targetRoot = targetDoc.getRootElement();
        boolean isFinished = false;
        for (Element targetChild : targetRoot.getChildren()) {
            String targetValue = targetChild.getAttributeValue("name");
            if (targetValue != null && targetValue.equals(itemName)) {
                targetChild.setText(sourceElement.getText());
                isFinished = true;
                break;
            }
        }
        if (!isFinished) {
            targetRoot.addContent("    ");
            targetRoot.addContent(sourceElement);
            targetRoot.addContent("\n");
        }
        XMLOutputter out = new XMLOutputter();
        Format format = Format.getRawFormat();
        format.setEncoding("UTF-8");
        format.setLineSeparator("\n");
        out.setFormat(format);
        OutputStream os = null;
        try {
            os = new FileOutputStream(targetFile);
            out.output(targetDoc, os);
        } catch (IOException e) {
            logger.error("output fail", e);
            return false;
        } finally {
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
                    logger.error("close output stream exception", e);
                }
            }
        }
        return true;
    }

    private static void preProcess(File file) throws IOException {
        String content = FileUtils.readFileToString(file, CHARSET);
        for (Map.Entry<String, String> entry : replace.entrySet()) {
            content = content.replaceAll(entry.getKey(), entry.getValue());
        }
        FileUtils.writeStringToFile(file, content, CHARSET);
    }

    private static void postProcess(File file) throws IOException {
        String content = FileUtils.readFileToString(file, CHARSET);
        for (Map.Entry<String, String> entry : replace.entrySet()) {
            content = content.replaceAll(entry.getValue(), entry.getKey());
        }
        if (REMOVE_LAST_LF_OPEN) {
            content = StringUtils.removeEnd(content, "\n");
        }
        FileUtils.writeStringToFile(file, content, CHARSET);
    }

}