├── README.md ├── StringExtractor.iml ├── libs └── gson-2.8.0.jar ├── release └── StringExtractor.zip ├── resources └── META-INF │ └── plugin.xml └── src ├── cn └── vearn │ └── checktreetable │ └── FiledTreeTableModel.java ├── org └── jdesktop │ └── swingx │ └── ux │ ├── CellProvider.java │ ├── CheckTreeCellProvider.java │ ├── CheckTreeSelectionModel.java │ ├── CheckTreeTableManager.java │ ├── Selector.java │ └── TristateCheckBox.java └── top └── wuhaojie └── se ├── MainAction.java ├── action └── DataWriter.java ├── common ├── PsiClassUtil.java ├── StringUtils.java └── Try.java ├── config └── Config.java ├── entity ├── FieldEntity.kt └── TaskHolder.kt ├── process ├── AbsWriter.kt ├── FileProcessor.kt ├── JavaWriter.kt ├── PrefixProcessor.kt ├── StringsWriter.kt ├── TextFormatProcessor.kt ├── TranslateProcessor.kt ├── XmlWriter.kt └── finder │ ├── AbsFieldFinder.kt │ ├── JavaFieldFinder.kt │ └── LayoutXmlFieldFinder.kt ├── translate ├── HttpGet.java ├── JsonUtils.kt ├── MD5.java ├── TransApi.java ├── TransResponse.kt ├── TransResult.kt └── Translator.kt └── ui ├── FieldsDialog.form ├── FieldsDialog.java └── Toast.java /README.md: -------------------------------------------------------------------------------- 1 | # String Extractor 插件 2 | 3 | ![logo.png](http://cdn.wuhaojie.top/logo.png) 4 | 5 | 帮助 Android 开发者一键释放字符串资源的 Android Studio 插件,[最新版本 v1.0](https://github.com/a-voyager/StringExtractor/raw/master/release/StringExtractor.zip) 6 | 7 | 欢迎 Fork & Star 8 | 9 | ![se_xml_file.gif](http://cdn.wuhaojie.top/se_xml_file.gif) 10 | 11 | ## 为什么开发? 12 | 13 | 在 Android 开发中,常常需要将字符串资源释放到项目的 res/values/strings.xml 文件中,面临:**来回切换文件**、**英文 ID 难取名**、**特定前缀重复工作多**等等的问题,不少 Android 开发者为之苦恼,故开发这样一款一键释放字符串资源的 Android Studio 插件 —— String Extractor 14 | 15 | ## 有什么用? 16 | 17 | String Extractor 以 Android Studio 插件的形式提供,对项目零污染,主要包含以下特性: 18 | 19 | (1)批量释放当前文件中的字符串资源 20 | 21 | (2)链接翻译 API,自动为字符串资源取英文 ID 名 22 | 23 | (3)支持自定义前缀名,便于匹配公司编码规范 24 | 25 | 26 | 27 | ## 怎么用? 28 | 29 | (1)安装 30 | 31 | 点击[此处](https://github.com/a-voyager/StringExtractor/raw/master/release/StringExtractor.zip)下载 String Extractor 插件,在 Android Studio 中的 Plugins 页面中选择「Install plugin from disk」从本地安装,之后重启 Android Studio 生效 32 | 33 | (2)打开 34 | 35 | 在包含字符串资源的 Java 文件或 XML 布局文件中,选择主菜单 Refactor -> Extract String 即可打开插件(推荐使用**快捷键 Alt + E**) 36 | 37 | ![WX20180515-194758@2x.png](http://cdn.wuhaojie.top/WX20180515-194758@2x.png) 38 | 39 | (3)用法 40 | 41 | 弹窗出现后,可以看到默认生成的字符串资源 ID,之后检查并修改资源 ID 前缀。如果是释放 Java 代码中的字符串,需要再检查并修改生成 Java 代码的模板。最后点击 OK,即可在 对应模块的 strings.xml 中生成字符串资源 42 | 43 | ![WX20180515-195507@2x.png](http://cdn.wuhaojie.top/WX20180515-195507@2x.png) 44 | 45 | 来一个动图展示: 46 | 47 | ![se_java_file.gif](http://cdn.wuhaojie.top/se_java_file.gif) 48 | 49 | 50 | 51 | ## 还有疑问? 52 | 53 | (1)插件有多大?会不会造成 Android Studio 卡顿? 54 | 55 | 插件大小为 300KB 左右,在性能较差或网络速度较慢的电脑上,可能打开插件会较慢,请耐心等待 56 | 57 | (2)字符串资源的 ID 根据什么生成的? 58 | 59 | 字符串资源 ID 根据「前缀」+「翻译后的原始资源」生成。其中前缀默认为「模块名」+「文件名」,翻译接入的百度翻译 API 60 | 61 | (3)使用一段时间后,不能翻译出结果了,为什么? 62 | 63 | 该插件目前采用的百度翻译免费 API,每月有一定的免费翻译额度限制 64 | 65 | 66 | ## 开源许可 67 | The MIT License (MIT) 68 | 69 | Copyright (c) 2018 WuHaojie 70 | 71 | Permission is hereby granted, free of charge, to any person obtaining a copy 72 | of this software and associated documentation files (the "Software"), to deal 73 | in the Software without restriction, including without limitation the rights 74 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 75 | copies of the Software, and to permit persons to whom the Software is 76 | furnished to do so, subject to the following conditions: 77 | 78 | The above copyright notice and this permission notice shall be included in all 79 | copies or substantial portions of the Software. 80 | 81 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 82 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 83 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 84 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 85 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 86 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 87 | SOFTWARE. 88 | -------------------------------------------------------------------------------- /StringExtractor.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /libs/gson-2.8.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a-voyager/StringExtractor/e1639d473512873ac3e4c339f50901b8c7800c18/libs/gson-2.8.0.jar -------------------------------------------------------------------------------- /release/StringExtractor.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a-voyager/StringExtractor/e1639d473512873ac3e4c339f50901b8c7800c18/release/StringExtractor.zip -------------------------------------------------------------------------------- /resources/META-INF/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | top.wuhaojie.plugin.se 3 | String Extractor 4 | 1.0 5 | Contact me 6 | 7 | 10 | 11 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/cn/vearn/checktreetable/FiledTreeTableModel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | package cn.vearn.checktreetable; 6 | 7 | import org.jdesktop.swingx.treetable.DefaultMutableTreeTableNode; 8 | import org.jdesktop.swingx.treetable.DefaultTreeTableModel; 9 | import org.jdesktop.swingx.treetable.TreeTableNode; 10 | import org.jdesktop.swingx.ux.CellProvider; 11 | 12 | /** 13 | * @author vearn 14 | */ 15 | public class FiledTreeTableModel extends DefaultTreeTableModel { 16 | 17 | private String[] _names = {" Key ", "Value"}; 18 | private Class[] _types = {Object.class, Object.class}; 19 | 20 | 21 | public FiledTreeTableModel(TreeTableNode node) { 22 | super(node); 23 | } 24 | 25 | /** 26 | * 列的类型 27 | */ 28 | @Override 29 | public Class getColumnClass(int col) { 30 | return _types[col]; 31 | } 32 | 33 | /** 34 | * 列的数量 35 | */ 36 | @Override 37 | public int getColumnCount() { 38 | return _names.length; 39 | } 40 | 41 | /** 42 | * 表头显示的内容 43 | */ 44 | @Override 45 | public String getColumnName(int column) { 46 | return _names[column]; 47 | } 48 | 49 | /** 50 | * 返回在单元格中显示的实例 51 | */ 52 | @Override 53 | public Object getValueAt(Object node, int column) { 54 | Object value = ""; 55 | if (node instanceof DefaultMutableTreeTableNode) { 56 | DefaultMutableTreeTableNode mutableNode = (DefaultMutableTreeTableNode) node; 57 | Object o = mutableNode.getUserObject(); 58 | if (o != null && o instanceof CellProvider) { 59 | CellProvider cellProvider = (CellProvider) o; 60 | value = cellProvider.getCellTitle(column); 61 | 62 | } 63 | } 64 | return value; 65 | } 66 | 67 | @Override 68 | public void setValueAt(Object value, Object node, int column) { 69 | super.setValueAt(value, node, column); 70 | if (node instanceof DefaultMutableTreeTableNode) { 71 | DefaultMutableTreeTableNode mutableNode = (DefaultMutableTreeTableNode) node; 72 | Object o = mutableNode.getUserObject(); 73 | if (o != null && o instanceof CellProvider) { 74 | CellProvider cellProvider = (CellProvider) o; 75 | cellProvider.setValueAt(column, value.toString()); 76 | } 77 | } 78 | } 79 | 80 | 81 | /** 82 | * 是否可以编辑该列 83 | */ 84 | @Override 85 | public boolean isCellEditable(Object node, int column) { 86 | return column != 0; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/org/jdesktop/swingx/ux/CellProvider.java: -------------------------------------------------------------------------------- 1 | package org.jdesktop.swingx.ux; 2 | 3 | /** 4 | * Created by dim on 16/11/7. 5 | */ 6 | public interface CellProvider { 7 | 8 | String getCellTitle(int index); 9 | 10 | void setValueAt(int column, String text); 11 | } 12 | -------------------------------------------------------------------------------- /src/org/jdesktop/swingx/ux/CheckTreeCellProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | package org.jdesktop.swingx.ux; 6 | 7 | import org.jdesktop.swingx.renderer.CellContext; 8 | import org.jdesktop.swingx.renderer.ComponentProvider; 9 | import org.jdesktop.swingx.treetable.DefaultMutableTreeTableNode; 10 | import top.wuhaojie.se.entity.FieldEntity; 11 | 12 | import javax.swing.*; 13 | import javax.swing.tree.TreePath; 14 | import java.awt.*; 15 | 16 | /** 17 | * @author vearn 18 | */ 19 | public class CheckTreeCellProvider extends ComponentProvider { 20 | 21 | private CheckTreeSelectionModel selectionModel; 22 | private TristateCheckBox _checkBox = null; 23 | private JLabel _label = null; 24 | 25 | public CheckTreeCellProvider(CheckTreeSelectionModel selectionModel) { 26 | this.selectionModel = selectionModel; 27 | _checkBox = new TristateCheckBox(); // 创建一个TristateCheckBox实例 28 | _checkBox.setOpaque(false); // 设置TristateCheckBox不绘制背景 29 | _label = new JLabel(); // 创建一个JLabel实例 30 | } 31 | 32 | @Override 33 | protected void format(CellContext arg0) { 34 | // 从CellContext获取tree中的文字和图标 35 | JTree tree = (JTree) arg0.getComponent(); 36 | DefaultMutableTreeTableNode node = (DefaultMutableTreeTableNode) arg0.getValue(); 37 | Object obj = node.getUserObject(); 38 | if (obj instanceof FieldEntity) { 39 | _label.setText(((FieldEntity) obj).getSource()); 40 | _checkBox.setSelector((FieldEntity) obj); 41 | } 42 | 43 | // _label.setIcon(arg0.getIcon()); 44 | 45 | // 根据selectionModel中的状态来绘制TristateCheckBox的外观 46 | TreePath path = tree.getPathForRow(arg0.getRow()); 47 | if (path != null) { 48 | if (selectionModel.isPathSelected(path, true)) { 49 | _checkBox.setState(Boolean.TRUE); 50 | } else if (selectionModel.isPartiallySelected(path)) { 51 | _checkBox.setState(null); // 注意“部分选中”状态的API 52 | } else { 53 | _checkBox.setState(Boolean.FALSE); 54 | } 55 | } 56 | if (obj instanceof FieldEntity) { 57 | _checkBox.setState(((FieldEntity) obj).isSelected()); 58 | } 59 | 60 | // 使用BorderLayout布局,依次放置TristateCheckBox和JLabel 61 | rendererComponent.setLayout(new BorderLayout()); 62 | rendererComponent.add(_checkBox); 63 | rendererComponent.add(_label, BorderLayout.LINE_END); 64 | } 65 | 66 | @Override 67 | protected void configureState(CellContext arg0) { 68 | } 69 | 70 | /** 71 | * 初始化一个JPanel来放置TristateCheckBox和JLabel 72 | */ 73 | @Override 74 | protected JPanel createRendererComponent() { 75 | JPanel panel = new JPanel(); 76 | return panel; 77 | } 78 | } -------------------------------------------------------------------------------- /src/org/jdesktop/swingx/ux/CheckTreeSelectionModel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | package org.jdesktop.swingx.ux; 6 | 7 | import org.jdesktop.swingx.treetable.DefaultMutableTreeTableNode; 8 | 9 | import javax.swing.tree.*; 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | import java.util.Stack; 13 | 14 | /** 15 | * 16 | * @author Santhosh Kumar T - santhosh@in.fiorano.com 17 | */ 18 | public class CheckTreeSelectionModel extends DefaultTreeSelectionModel { 19 | 20 | private TreeModel model; 21 | 22 | public CheckTreeSelectionModel(TreeModel model) { 23 | this.model = model; 24 | setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION); 25 | } 26 | 27 | // tests whether there is any unselected node in the subtree of given path 28 | public boolean isPartiallySelected(TreePath path) { 29 | if (isPathSelected(path, true)) { 30 | return false; 31 | } 32 | TreePath[] selectionPaths = getSelectionPaths(); 33 | if (selectionPaths == null) { 34 | return false; 35 | } 36 | for (int j = 0; j < selectionPaths.length; j++) { 37 | if (isDescendant(selectionPaths[j], path)) { 38 | return true; 39 | } 40 | } 41 | return false; 42 | } 43 | 44 | // tells whether given path is selected. 45 | // if dig is true, then a path is assumed to be selected, if 46 | // one of its ancestor is selected. 47 | public boolean isPathSelected(TreePath path, boolean dig) { 48 | if (!dig) { 49 | return super.isPathSelected(path); 50 | } 51 | while (path != null && !super.isPathSelected(path)) { 52 | path = path.getParentPath(); 53 | } 54 | return path != null; 55 | } 56 | 57 | // is path1 descendant of path2 58 | private boolean isDescendant(TreePath path1, TreePath path2) { 59 | Object obj1[] = path1.getPath(); 60 | Object obj2[] = path2.getPath(); 61 | for (int i = 0; i < obj2.length; i++) { 62 | if (obj1[i] != obj2[i]) { 63 | return false; 64 | } 65 | } 66 | return true; 67 | } 68 | 69 | public void addPathsByNodes(List selectedNodes) { 70 | int num = selectedNodes.size(); 71 | TreePath[] tps = new TreePath[num]; 72 | for (int i = 0; i < num; i++) { 73 | DefaultMutableTreeTableNode node = (DefaultMutableTreeTableNode) selectedNodes.get(i); 74 | tps[i] = new TreePath(getPathToRoot(node)); 75 | } 76 | this.addSelectionPaths(tps); 77 | } 78 | 79 | @Override 80 | public void addSelectionPaths(TreePath[] paths) { 81 | // unselect all descendants of paths[] 82 | for (int i = 0; i < paths.length; i++) { 83 | TreePath path = paths[i]; 84 | TreePath[] selectionPaths = getSelectionPaths(); 85 | if (selectionPaths == null) { 86 | break; 87 | } 88 | ArrayList toBeRemoved = new ArrayList(); 89 | for (int j = 0; j < selectionPaths.length; j++) { 90 | if (isDescendant(selectionPaths[j], path)) { 91 | toBeRemoved.add(selectionPaths[j]); 92 | } 93 | } 94 | super.removeSelectionPaths((TreePath[]) toBeRemoved.toArray(new TreePath[0])); 95 | } 96 | 97 | // if all siblings are selected then unselect them and select parent recursively 98 | // otherwize just select that path. 99 | for (int i = 0; i < paths.length; i++) { 100 | TreePath path = paths[i]; 101 | TreePath temp = null; 102 | while (areSiblingsSelected(path)) { 103 | temp = path; 104 | if (path.getParentPath() == null) { 105 | break; 106 | } 107 | path = path.getParentPath(); 108 | } 109 | if (temp != null) { 110 | if (temp.getParentPath() != null) { 111 | addSelectionPath(temp.getParentPath()); 112 | } else { 113 | if (!isSelectionEmpty()) { 114 | removeSelectionPaths(getSelectionPaths()); 115 | } 116 | super.addSelectionPaths(new TreePath[]{temp}); 117 | } 118 | } else { 119 | super.addSelectionPaths(new TreePath[]{path}); 120 | } 121 | } 122 | } 123 | 124 | // tells whether all siblings of given path are selected. 125 | private boolean areSiblingsSelected(TreePath path) { 126 | TreePath parent = path.getParentPath(); 127 | if (parent == null) { 128 | return true; 129 | } 130 | Object node = path.getLastPathComponent(); 131 | Object parentNode = parent.getLastPathComponent(); 132 | 133 | int childCount = model.getChildCount(parentNode); 134 | for (int i = 0; i < childCount; i++) { 135 | Object childNode = model.getChild(parentNode, i); 136 | if (childNode == node) { 137 | continue; 138 | } 139 | if (!isPathSelected(parent.pathByAddingChild(childNode))) { 140 | return false; 141 | } 142 | } 143 | return true; 144 | } 145 | 146 | @Override 147 | public void removeSelectionPaths(TreePath[] paths) { 148 | for (int i = 0; i < paths.length; i++) { 149 | TreePath path = paths[i]; 150 | if (path.getPathCount() == 1) { 151 | super.removeSelectionPaths(new TreePath[]{path}); 152 | } else { 153 | toggleRemoveSelection(path); 154 | } 155 | } 156 | } 157 | 158 | // if any ancestor node of given path is selected then unselect it 159 | // and selection all its descendants except given path and descendants. 160 | // otherwise just unselect the given path 161 | private void toggleRemoveSelection(TreePath path) { 162 | Stack stack = new Stack(); 163 | TreePath parent = path.getParentPath(); 164 | while (parent != null && !isPathSelected(parent)) { 165 | stack.push(parent); 166 | parent = parent.getParentPath(); 167 | } 168 | if (parent != null) { 169 | stack.push(parent); 170 | } else { 171 | super.removeSelectionPaths(new TreePath[]{path}); 172 | return; 173 | } 174 | 175 | while (!stack.isEmpty()) { 176 | TreePath temp = (TreePath) stack.pop(); 177 | TreePath peekPath = stack.isEmpty() ? path : (TreePath) stack.peek(); 178 | Object node = temp.getLastPathComponent(); 179 | Object peekNode = peekPath.getLastPathComponent(); 180 | int childCount = model.getChildCount(node); 181 | for (int i = 0; i < childCount; i++) { 182 | Object childNode = model.getChild(node, i); 183 | if (childNode != peekNode) { 184 | super.addSelectionPaths(new TreePath[]{temp.pathByAddingChild(childNode)}); 185 | } 186 | } 187 | } 188 | super.removeSelectionPaths(new TreePath[]{parent}); 189 | } 190 | 191 | private TreeNode[] getPathToRoot(TreeNode aNode) { 192 | TreeNode[] retNodes; 193 | ArrayList temp = new ArrayList(); 194 | 195 | /* Check for null, in case someone passed in a null node, or 196 | they passed in an element that isn't rooted at root. */ 197 | while (aNode != null) { 198 | temp.add(aNode); 199 | aNode = aNode.getParent(); 200 | } 201 | 202 | int num = temp.size(); 203 | retNodes = new TreeNode[num]; 204 | for (int i = num - 1; i >= 0; i--) { 205 | retNodes[num - 1 - i] = temp.get(i); 206 | } 207 | 208 | return retNodes; 209 | } 210 | } -------------------------------------------------------------------------------- /src/org/jdesktop/swingx/ux/CheckTreeTableManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | package org.jdesktop.swingx.ux; 6 | 7 | import org.jdesktop.swingx.JXTreeTable; 8 | import org.jdesktop.swingx.renderer.DefaultTreeRenderer; 9 | 10 | import javax.swing.*; 11 | import javax.swing.event.TreeSelectionEvent; 12 | import javax.swing.event.TreeSelectionListener; 13 | import javax.swing.tree.TreePath; 14 | import java.awt.event.MouseAdapter; 15 | import java.awt.event.MouseEvent; 16 | 17 | /** 18 | * 19 | * @author Santhosh Kumr T - santhosh@in.fiorano.com 20 | */ 21 | public class CheckTreeTableManager extends MouseAdapter implements TreeSelectionListener { 22 | 23 | private CheckTreeSelectionModel selectionModel; 24 | private JXTreeTable treetable; 25 | private JTree tree; 26 | int hotspot = new JCheckBox().getPreferredSize().width; 27 | 28 | public CheckTreeTableManager(JXTreeTable treeTable) { 29 | this.treetable = treeTable; 30 | this.tree = (JTree) treeTable.getCellRenderer(0, 0); 31 | selectionModel = new CheckTreeSelectionModel(tree.getModel()); 32 | tree.setCellRenderer(new DefaultTreeRenderer(new CheckTreeCellProvider(selectionModel))); 33 | treeTable.addMouseListener(this); 34 | 35 | selectionModel.addTreeSelectionListener(this); 36 | } 37 | 38 | @Override 39 | public void mouseClicked(MouseEvent me) { 40 | TreePath path = tree.getPathForLocation(me.getX(), me.getY()); 41 | if (path == null) { 42 | return; 43 | } 44 | if (me.getX() > tree.getPathBounds(path).x + hotspot) { 45 | return; 46 | } 47 | 48 | boolean selected = selectionModel.isPathSelected(path, true); 49 | selectionModel.removeTreeSelectionListener(this); 50 | 51 | try { 52 | if (selected) { 53 | selectionModel.removeSelectionPath(path); 54 | } else { 55 | selectionModel.addSelectionPath(path); 56 | } 57 | } finally { 58 | selectionModel.addTreeSelectionListener(this); 59 | treetable.repaint(); 60 | } 61 | } 62 | 63 | public CheckTreeSelectionModel getSelectionModel() { 64 | return selectionModel; 65 | } 66 | 67 | public void valueChanged(TreeSelectionEvent e) { 68 | } 69 | } -------------------------------------------------------------------------------- /src/org/jdesktop/swingx/ux/Selector.java: -------------------------------------------------------------------------------- 1 | package org.jdesktop.swingx.ux; 2 | 3 | /** 4 | * Created by dim on 16/11/7. 5 | */ 6 | public interface Selector { 7 | 8 | void setSelect(boolean select); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/org/jdesktop/swingx/ux/TristateCheckBox.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | package org.jdesktop.swingx.ux; 6 | 7 | 8 | import javax.swing.*; 9 | import javax.swing.event.ChangeListener; 10 | import javax.swing.plaf.ActionMapUIResource; 11 | import java.awt.event.*; 12 | 13 | /** 14 | * Maintenance tip - There were some tricks to getting this code 15 | * working: 16 | * 17 | * 1. You have to overwite addMouseListener() to do nothing 18 | * 2. You have to add a mouse event on mousePressed by calling 19 | * super.addMouseListener() 20 | * 3. You have to replace the UIActionMap for the keyboard event 21 | * "pressed" with your own one. 22 | * 4. You have to remove the UIActionMap for the keyboard event 23 | * "released". 24 | * 5. You have to grab focus when the next state is entered, 25 | * otherwise clicking on the component won't get the focus. 26 | * 6. You have to make a TristateDecorator as a button model that 27 | * wraps the original button model and does state management. 28 | */ 29 | public class TristateCheckBox extends JCheckBox { 30 | 31 | private final TristateDecorator decorator; 32 | 33 | private Selector selector; 34 | 35 | 36 | 37 | public void setSelector(Selector selector) { 38 | this.selector = selector; 39 | } 40 | 41 | public TristateCheckBox(String text, Icon icon, Boolean initial) { 42 | super(text, icon); 43 | // Add a listener for when the mouse is pressed 44 | super.addMouseListener(new MouseAdapter() { 45 | 46 | @Override 47 | public void mousePressed(MouseEvent e) { 48 | grabFocus(); 49 | decorator.nextState(); 50 | } 51 | }); 52 | // Reset the keyboard action map 53 | ActionMap map = new ActionMapUIResource(); 54 | map.put("pressed", new AbstractAction() { //NOI18N 55 | 56 | public void actionPerformed(ActionEvent e) { 57 | grabFocus(); 58 | decorator.nextState(); 59 | } 60 | }); 61 | map.put("released", null); //NOI18N 62 | SwingUtilities.replaceUIActionMap(this, map); 63 | // set the model to the adapted model 64 | decorator = new TristateDecorator(getModel()); 65 | setModel(decorator); 66 | setState(initial); 67 | } 68 | 69 | public TristateCheckBox(String text, Boolean initial) { 70 | this(text, null, initial); 71 | } 72 | 73 | public TristateCheckBox(String text) { 74 | this(text, true); 75 | } 76 | 77 | public TristateCheckBox() { 78 | this(null); 79 | } 80 | 81 | /** No one may add mouse listeners, not even Swing! */ 82 | @Override 83 | public void addMouseListener(MouseListener l) { 84 | } 85 | 86 | /** 87 | * Set the new state to either SELECTED, NOT_SELECTED or 88 | * DONT_CARE. If state == null, it is treated as DONT_CARE. 89 | */ 90 | public void setState(Boolean state) { 91 | decorator.setState(state); 92 | } 93 | 94 | /** Return the current state, which is determined by the 95 | * selection status of the model. */ 96 | public Boolean getState() { 97 | return decorator.getState(); 98 | } 99 | 100 | /** 101 | * Exactly which Design Pattern is this? Is it an Adapter, 102 | * a Proxy or a Decorator? In this case, my vote lies with the 103 | * Decorator, because we are extending functionality and 104 | * "decorating" the original model with a more powerful model. 105 | */ 106 | private class TristateDecorator implements ButtonModel { 107 | 108 | private final ButtonModel other; 109 | 110 | private TristateDecorator(ButtonModel other) { 111 | this.other = other; 112 | } 113 | 114 | private void setState(Boolean state) { 115 | if (state == Boolean.FALSE) { 116 | other.setArmed(false); 117 | if(selector != null){ 118 | selector.setSelect(false); 119 | } 120 | setPressed(false); 121 | setSelected(false); 122 | } else if (state == Boolean.TRUE) { 123 | other.setArmed(false); 124 | setPressed(false); 125 | setSelected(true); 126 | if(selector != null){ 127 | selector.setSelect(true); 128 | } 129 | } else { 130 | other.setArmed(true); 131 | setPressed(true); 132 | setSelected(true); 133 | if(selector != null){ 134 | selector.setSelect(true); 135 | } 136 | 137 | } 138 | } 139 | 140 | /** 141 | * The current state is embedded in the selection / armed 142 | * state of the model. 143 | * 144 | * We return the SELECTED state when the checkbox is selected 145 | * but not armed, DONT_CARE state when the checkbox is 146 | * selected and armed (grey) and NOT_SELECTED when the 147 | * checkbox is deselected. 148 | */ 149 | private Boolean getState() { 150 | if (isSelected() && !isArmed()) { 151 | // normal black tick 152 | return Boolean.TRUE; 153 | } else if (isSelected() && isArmed()) { 154 | // don't care grey tick 155 | return null; 156 | } else { 157 | // normal deselected 158 | return Boolean.FALSE; 159 | } 160 | } 161 | 162 | /** We rotate between NOT_SELECTED, SELECTED and DONT_CARE.*/ 163 | private void nextState() { 164 | Boolean current = getState(); 165 | if (current == Boolean.FALSE) { 166 | setState(Boolean.TRUE); 167 | } else if (current == Boolean.TRUE) { 168 | setState(null); 169 | } else if (current == null) { 170 | setState(Boolean.FALSE); 171 | } 172 | } 173 | 174 | /** Filter: No one may change the armed status except us. */ 175 | public void setArmed(boolean b) { 176 | } 177 | 178 | public boolean isFocusTraversable() { 179 | return isEnabled(); 180 | } 181 | 182 | /** We disable focusing on the component when it is not 183 | * enabled. */ 184 | public void setEnabled(boolean b) { 185 | // setFocusable(b); 186 | other.setEnabled(b); 187 | } 188 | 189 | /** All these methods simply delegate to the "other" model 190 | * that is being decorated. */ 191 | public boolean isArmed() { 192 | return other.isArmed(); 193 | } 194 | 195 | public boolean isSelected() { 196 | return other.isSelected(); 197 | } 198 | 199 | public boolean isEnabled() { 200 | return other.isEnabled(); 201 | } 202 | 203 | public boolean isPressed() { 204 | return other.isPressed(); 205 | } 206 | 207 | public boolean isRollover() { 208 | return other.isRollover(); 209 | } 210 | 211 | public void setSelected(boolean b) { 212 | other.setSelected(b); 213 | } 214 | 215 | public void setPressed(boolean b) { 216 | other.setPressed(b); 217 | } 218 | 219 | public void setRollover(boolean b) { 220 | other.setRollover(b); 221 | } 222 | 223 | public void setMnemonic(int key) { 224 | other.setMnemonic(key); 225 | } 226 | 227 | public int getMnemonic() { 228 | return other.getMnemonic(); 229 | } 230 | 231 | public void setActionCommand(String s) { 232 | other.setActionCommand(s); 233 | } 234 | 235 | public String getActionCommand() { 236 | return other.getActionCommand(); 237 | } 238 | 239 | public void setGroup(ButtonGroup group) { 240 | other.setGroup(group); 241 | } 242 | 243 | public void addActionListener(ActionListener l) { 244 | other.addActionListener(l); 245 | } 246 | 247 | public void removeActionListener(ActionListener l) { 248 | other.removeActionListener(l); 249 | } 250 | 251 | public void addItemListener(ItemListener l) { 252 | other.addItemListener(l); 253 | } 254 | 255 | public void removeItemListener(ItemListener l) { 256 | other.removeItemListener(l); 257 | } 258 | 259 | public void addChangeListener(ChangeListener l) { 260 | other.addChangeListener(l); 261 | } 262 | 263 | public void removeChangeListener(ChangeListener l) { 264 | other.removeChangeListener(l); 265 | } 266 | 267 | public Object[] getSelectedObjects() { 268 | return other.getSelectedObjects(); 269 | } 270 | } 271 | } -------------------------------------------------------------------------------- /src/top/wuhaojie/se/MainAction.java: -------------------------------------------------------------------------------- 1 | package top.wuhaojie.se; 2 | 3 | import com.intellij.codeInsight.CodeInsightActionHandler; 4 | import com.intellij.codeInsight.generation.actions.BaseGenerateAction; 5 | import com.intellij.ide.highlighter.JavaFileType; 6 | import com.intellij.ide.highlighter.XmlFileType; 7 | import com.intellij.openapi.actionSystem.AnActionEvent; 8 | import com.intellij.openapi.actionSystem.PlatformDataKeys; 9 | import com.intellij.openapi.editor.Editor; 10 | import com.intellij.openapi.fileTypes.FileType; 11 | import com.intellij.openapi.project.Project; 12 | import com.intellij.psi.JavaPsiFacade; 13 | import com.intellij.psi.PsiClass; 14 | import com.intellij.psi.PsiElementFactory; 15 | import com.intellij.psi.PsiFile; 16 | import com.intellij.psi.util.PsiUtilBase; 17 | import org.jetbrains.annotations.NotNull; 18 | import top.wuhaojie.se.entity.TaskHolder; 19 | import top.wuhaojie.se.process.*; 20 | import top.wuhaojie.se.process.finder.AbsFieldFinder; 21 | import top.wuhaojie.se.process.finder.JavaFieldFinder; 22 | import top.wuhaojie.se.process.finder.LayoutXmlFieldFinder; 23 | import top.wuhaojie.se.ui.FieldsDialog; 24 | 25 | public class MainAction extends BaseGenerateAction { 26 | 27 | @SuppressWarnings("unused") 28 | public MainAction() { 29 | super(null); 30 | } 31 | 32 | @SuppressWarnings("unused") 33 | public MainAction(CodeInsightActionHandler handler) { 34 | super(handler); 35 | } 36 | 37 | @Override 38 | protected boolean isValidForClass(final PsiClass targetClass) { 39 | return super.isValidForClass(targetClass); 40 | } 41 | 42 | @Override 43 | public boolean isValidForFile(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) { 44 | return true; 45 | } 46 | 47 | public void actionPerformed(AnActionEvent event) { 48 | Project project = event.getData(PlatformDataKeys.PROJECT); 49 | Editor editor = event.getData(PlatformDataKeys.EDITOR); 50 | if (project == null || editor == null) { 51 | return; 52 | } 53 | PsiFile psiFile = PsiUtilBase.getPsiFileInEditor(editor, project); 54 | if (psiFile == null) { 55 | return; 56 | } 57 | PsiClass psiClass = getTargetClass(editor, psiFile); 58 | PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); 59 | 60 | AbsFieldFinder fieldFinder; 61 | FileType fileType = psiFile.getFileType(); 62 | if (fileType instanceof XmlFileType) { 63 | fieldFinder = new LayoutXmlFieldFinder(); 64 | } else if (fileType instanceof JavaFileType) { 65 | fieldFinder = new JavaFieldFinder(); 66 | } else { 67 | return; 68 | } 69 | // 70 | // ModuleManager instance = ModuleManager.getInstance(project); 71 | // Module[] modules = instance.getModules(); 72 | 73 | 74 | TaskHolder taskHolder = fieldFinder.find(psiFile); 75 | 76 | FileProcessor.INSTANCE.process(project, psiFile, taskHolder); 77 | TranslateProcessor.INSTANCE.process(taskHolder); 78 | TextFormatProcessor.INSTANCE.process(taskHolder); 79 | PrefixProcessor.INSTANCE.refreshDefaultPrefix(project, psiFile, taskHolder); 80 | 81 | FieldsDialog dialog = new FieldsDialog(factory, psiClass, psiFile, project, taskHolder); 82 | dialog.setSize(800, 500); 83 | dialog.setLocationRelativeTo(null); 84 | dialog.setVisible(true); 85 | 86 | 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /src/top/wuhaojie/se/action/DataWriter.java: -------------------------------------------------------------------------------- 1 | package top.wuhaojie.se.action; 2 | 3 | import com.intellij.openapi.application.RunResult; 4 | import com.intellij.openapi.command.WriteCommandAction; 5 | import com.intellij.openapi.progress.ProgressIndicator; 6 | import com.intellij.openapi.progress.ProgressManager; 7 | import com.intellij.openapi.progress.Task; 8 | import com.intellij.openapi.project.Project; 9 | import com.intellij.openapi.ui.MessageType; 10 | import com.intellij.psi.*; 11 | import com.intellij.psi.xml.XmlFile; 12 | import org.jetbrains.annotations.NotNull; 13 | import top.wuhaojie.se.entity.TaskHolder; 14 | import top.wuhaojie.se.process.JavaWriter; 15 | import top.wuhaojie.se.process.StringsWriter; 16 | import top.wuhaojie.se.process.XmlWriter; 17 | import top.wuhaojie.se.ui.Toast; 18 | 19 | /** 20 | * Created with IntelliJ IDEA. 21 | * User: dim 22 | * Date: 14-7-4 23 | * Time: 下午3:58 24 | */ 25 | public class DataWriter extends WriteCommandAction.Simple { 26 | 27 | private PsiClass cls; 28 | private PsiElementFactory factory; 29 | private Project project; 30 | private PsiFile file; 31 | private TaskHolder taskHolder; 32 | 33 | public DataWriter(PsiFile file, Project project, PsiClass cls, TaskHolder taskHolder) { 34 | super(project, file); 35 | factory = JavaPsiFacade.getElementFactory(project); 36 | this.file = file; 37 | this.project = project; 38 | this.cls = cls; 39 | this.taskHolder = taskHolder; 40 | } 41 | 42 | public void go() { 43 | ProgressManager.getInstance().run(new Task.Backgroundable(project, "Extract String") { 44 | 45 | @Override 46 | public void run(@NotNull ProgressIndicator progressIndicator) { 47 | progressIndicator.setIndeterminate(true); 48 | long currentTimeMillis = System.currentTimeMillis(); 49 | execute(); 50 | progressIndicator.setIndeterminate(false); 51 | progressIndicator.setFraction(1.0); 52 | Toast.make(project, MessageType.INFO, "String Extracted [" + (System.currentTimeMillis() - currentTimeMillis) + " ms]\n"); 53 | } 54 | }); 55 | } 56 | 57 | @NotNull 58 | @Override 59 | public RunResult execute() { 60 | return super.execute(); 61 | } 62 | 63 | @Override 64 | protected void run() { 65 | 66 | if (file instanceof PsiJavaFile) { 67 | JavaWriter javaWriter = new JavaWriter(); 68 | javaWriter.process(taskHolder); 69 | } else if (file instanceof XmlFile) { 70 | XmlWriter xmlWriter = new XmlWriter(); 71 | xmlWriter.process(taskHolder); 72 | } 73 | 74 | StringsWriter stringsWriter = new StringsWriter(project); 75 | stringsWriter.process(taskHolder); 76 | 77 | 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/top/wuhaojie/se/common/PsiClassUtil.java: -------------------------------------------------------------------------------- 1 | package top.wuhaojie.se.common; 2 | 3 | import com.intellij.ide.util.DirectoryUtil; 4 | import com.intellij.openapi.fileEditor.FileEditorManager; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.psi.*; 7 | import com.intellij.psi.impl.source.PsiJavaFileImpl; 8 | import com.intellij.psi.search.EverythingGlobalScope; 9 | import com.intellij.psi.search.GlobalSearchScope; 10 | import org.apache.http.util.TextUtils; 11 | 12 | import java.io.File; 13 | 14 | /** 15 | * Created by dim on 15/8/22. 16 | */ 17 | public class PsiClassUtil { 18 | 19 | 20 | public static PsiClass exist(PsiFile psiFile, String generateClass) { 21 | PsiClass psiClass = null; 22 | PsiDirectory psiDirectory = getJavaSrc(psiFile); 23 | if (psiDirectory == null || psiDirectory.getVirtualFile().getCanonicalPath() == null) { 24 | return null; 25 | } 26 | 27 | File file = new File(psiDirectory.getVirtualFile().getCanonicalPath().concat("/") 28 | .concat(generateClass.trim().replace(".", "/")).concat(".java")); 29 | 30 | String[] strArray = generateClass.replace(" ", "").split("\\."); 31 | if (TextUtils.isEmpty(generateClass)) { 32 | return null; 33 | } 34 | String className = strArray[strArray.length - 1]; 35 | String packName = generateClass.substring(generateClass.length() - className.length(), generateClass.length()); 36 | if (file.exists()) { 37 | for (int i = 0; i < strArray.length - 1; i++) { 38 | psiDirectory = psiDirectory.findSubdirectory(strArray[i]); 39 | if (psiDirectory == null) { 40 | return null; 41 | } 42 | } 43 | PsiFile psiFile1 = psiDirectory.findFile(className + ".java"); 44 | if ((psiFile1 instanceof PsiJavaFile) && ((PsiJavaFile) psiFile1).getClasses().length > 0) { 45 | psiClass = ((PsiJavaFile) psiFile1).getClasses()[0]; 46 | } 47 | } 48 | return psiClass; 49 | } 50 | 51 | public static PsiDirectory getJavaSrc(PsiFile psiFile) { 52 | PsiDirectory psiDirectory = null; 53 | if (psiFile instanceof PsiJavaFileImpl) { 54 | String packageName = ((PsiJavaFileImpl) psiFile).getPackageName(); 55 | String[] arg = packageName.split("\\."); 56 | psiDirectory = psiFile.getContainingDirectory(); 57 | 58 | for (int i = 0; i < arg.length; i++) { 59 | psiDirectory = psiDirectory.getParent(); 60 | if (psiDirectory == null) { 61 | break; 62 | } 63 | } 64 | } 65 | return psiDirectory; 66 | } 67 | 68 | public static File getPackageFile(PsiFile psiFile, String packageName) { 69 | PsiDirectory psiDirectory = getJavaSrc(psiFile); 70 | if (psiDirectory == null || psiDirectory.getVirtualFile().getCanonicalPath() == null) { 71 | return null; 72 | } 73 | 74 | if (packageName == null) { 75 | return new File(psiDirectory.getVirtualFile().getCanonicalPath()); 76 | } 77 | File file = new File(psiDirectory.getVirtualFile().getCanonicalPath().concat("/") 78 | .concat(packageName.trim().replace(".", "/"))); 79 | if (file.exists()) { 80 | return file; 81 | } 82 | return null; 83 | } 84 | 85 | 86 | public static PsiClass getPsiClass(PsiFile psiFile, Project project, String generateClass) throws Throwable { 87 | 88 | PsiClass psiClass = null; 89 | PsiDirectory psiDirectory = getJavaSrc(psiFile); 90 | 91 | if (psiDirectory == null || psiDirectory.getVirtualFile().getCanonicalPath() == null) { 92 | return null; 93 | } 94 | 95 | File file = new File(psiDirectory.getVirtualFile().getCanonicalPath().concat("/") 96 | .concat(generateClass.trim().replace(".", "/")).concat(".java")); 97 | 98 | String[] strArray = generateClass.replace(" ", "").split("\\."); 99 | if (TextUtils.isEmpty(generateClass)) { 100 | return null; 101 | } 102 | String className = strArray[strArray.length - 1]; 103 | String packName = generateClass.substring(0, generateClass.length() - className.length()); 104 | if (file.exists()) { 105 | for (int i = 0; i < strArray.length - 1; i++) { 106 | psiDirectory = psiDirectory.findSubdirectory(strArray[i]); 107 | if (psiDirectory == null) { 108 | return null; 109 | } 110 | } 111 | PsiFile psiFile1 = psiDirectory.findFile(className + ".java"); 112 | if ((psiFile1 instanceof PsiJavaFile) && ((PsiJavaFile) psiFile1).getClasses().length > 0) { 113 | psiClass = ((PsiJavaFile) psiFile1).getClasses()[0]; 114 | } 115 | if (psiClass != null) { 116 | FileEditorManager manager = FileEditorManager.getInstance(project); 117 | manager.openFile(psiClass.getContainingFile().getVirtualFile(), true, true); 118 | } 119 | 120 | } else { 121 | if (!file.getParentFile().exists() && !TextUtils.isEmpty(packName)) { 122 | psiDirectory = createPackageInSourceRoot(packName, psiDirectory); 123 | 124 | } else { 125 | for (int i = 0; i < strArray.length - 1; i++) { 126 | psiDirectory = psiDirectory.findSubdirectory(strArray[i]); 127 | if (psiDirectory == null) { 128 | return null; 129 | } 130 | } 131 | } 132 | 133 | psiClass = JavaDirectoryService.getInstance().createClass(psiDirectory, className); 134 | FileEditorManager manager = FileEditorManager.getInstance(project); 135 | manager.openFile(psiClass.getContainingFile().getVirtualFile(), true, true); 136 | } 137 | 138 | return psiClass; 139 | } 140 | 141 | public static PsiDirectory createPackageInSourceRoot(String packageName, PsiDirectory sourcePackageRoot) { 142 | return DirectoryUtil.createSubdirectories(packageName, sourcePackageRoot, "."); 143 | } 144 | 145 | private PsiClass getPsiClassByName(Project project, String cls) { 146 | GlobalSearchScope searchScope = GlobalSearchScope.allScope(project); 147 | JavaPsiFacade javaPsiFacade = JavaPsiFacade.getInstance(project); 148 | return javaPsiFacade.findClass(cls, searchScope); 149 | } 150 | 151 | 152 | public static String getPackage(PsiClass cls) { 153 | if (cls.getQualifiedName() == null) { 154 | return null; 155 | } 156 | int i = cls.getQualifiedName().lastIndexOf("."); 157 | if (i > -1) { 158 | return cls.getQualifiedName().substring(0, i); 159 | } else { 160 | return ""; 161 | } 162 | } 163 | 164 | public static boolean isClassAvailableForProject(Project project, String className) { 165 | PsiClass classInModule = JavaPsiFacade.getInstance(project).findClass(className, 166 | new EverythingGlobalScope(project)); 167 | return classInModule != null; 168 | } 169 | 170 | 171 | // public static a(PsiElementFactory factory){ 172 | // PsiElement psiElement = cls.addAfter(factory.createCommentFromText("// todo dim " + fieldEntity.getFieldName(), cls), add); 173 | //// CharTable charTableByTree = SharedImplUtil.findCharTableByTree( 174 | //// (ASTNode) psiElement); 175 | //// PsiWhiteSpace psiWhiteSpace = (PsiWhiteSpace) Factory.createSingleLeafElement(TokenType.WHITE_SPACE, "\n\n", 176 | //// charTableByTree, PsiManager.getInstance(cls.getProject())); 177 | //// cls.addAfter(psiWhiteSpace, psiElement); 178 | // } 179 | 180 | } 181 | -------------------------------------------------------------------------------- /src/top/wuhaojie/se/common/StringUtils.java: -------------------------------------------------------------------------------- 1 | package top.wuhaojie.se.common; 2 | 3 | import org.apache.http.util.TextUtils; 4 | 5 | /** 6 | * Created by dim on 16/11/5. 7 | */ 8 | public class StringUtils { 9 | 10 | /** 11 | * 转成驼峰 12 | * 13 | * @param text 14 | * @return 15 | */ 16 | public static String captureStringLeaveUnderscore(String text) { 17 | if (TextUtils.isEmpty(text)) { 18 | return text; 19 | } 20 | String temp = text.replaceAll("^_+", ""); 21 | 22 | if (!TextUtils.isEmpty(temp)) { 23 | text = temp; 24 | } 25 | String[] strings = text.split("_"); 26 | StringBuilder stringBuilder = new StringBuilder(); 27 | stringBuilder.append(strings[0]); 28 | for (int i = 1; i < strings.length; i++) { 29 | stringBuilder.append(captureName(strings[i])); 30 | } 31 | return stringBuilder.toString(); 32 | } 33 | 34 | public static String captureName(String text) { 35 | 36 | if (text.length() > 0) { 37 | text = text.substring(0, 1).toUpperCase() + text.substring(1); 38 | } 39 | return text; 40 | } 41 | 42 | public static String getPackage(String generateClassName) { 43 | int index = generateClassName.lastIndexOf("."); 44 | if (index > 0) { 45 | return generateClassName.substring(0, index); 46 | } 47 | return null; 48 | } 49 | 50 | public static String underscoreString(String text) { 51 | if (TextUtils.isEmpty(text)) { 52 | return ""; 53 | } 54 | StringBuilder builder = new StringBuilder(); 55 | char[] array = text.toCharArray(); 56 | for (int i = 0; i < array.length; i++) { 57 | if (Character.isUpperCase(array[i]) && i != 0) { 58 | builder.append('_'); 59 | } 60 | builder.append(array[i]); 61 | } 62 | return builder.toString().toLowerCase(); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/top/wuhaojie/se/common/Try.java: -------------------------------------------------------------------------------- 1 | package top.wuhaojie.se.common; 2 | 3 | /** 4 | * Created by dim on 17/1/21. 5 | */ 6 | public class Try { 7 | 8 | public static void run(TryListener tryListener) { 9 | try { 10 | tryListener.run(); 11 | } catch (Exception e) { 12 | e.printStackTrace(); 13 | try { 14 | tryListener.runAgain(); 15 | } catch (Exception e1) { 16 | e1.printStackTrace(); 17 | try { 18 | tryListener.error(); 19 | }catch (Exception e3){ 20 | e3.printStackTrace(); 21 | } 22 | } 23 | } 24 | } 25 | 26 | public interface TryListener { 27 | void run(); 28 | void runAgain(); 29 | void error(); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/top/wuhaojie/se/config/Config.java: -------------------------------------------------------------------------------- 1 | package top.wuhaojie.se.config; 2 | 3 | import com.intellij.ide.util.PropertiesComponent; 4 | 5 | public class Config { 6 | 7 | private static Config config; 8 | 9 | private boolean key; 10 | 11 | private Config() { 12 | 13 | } 14 | 15 | public void save() { 16 | 17 | PropertiesComponent.getInstance().setValue("key", "value"); 18 | } 19 | 20 | public static Config getInstant() { 21 | 22 | if (config == null) { 23 | config = new Config(); 24 | config.key = PropertiesComponent.getInstance().getBoolean("fieldPrivateMode", true); 25 | } 26 | return config; 27 | } 28 | 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/top/wuhaojie/se/entity/FieldEntity.kt: -------------------------------------------------------------------------------- 1 | package top.wuhaojie.se.entity 2 | 3 | import org.jdesktop.swingx.ux.CellProvider 4 | import org.jdesktop.swingx.ux.Selector 5 | 6 | data class FieldEntity( 7 | 8 | var source: String = "", 9 | var result: String = "", 10 | var isSelected: Boolean = true, 11 | var resultSrc: String = "" 12 | 13 | ) : Selector, CellProvider { 14 | 15 | 16 | override fun setSelect(select: Boolean) { 17 | this.isSelected = select 18 | } 19 | 20 | 21 | override fun getCellTitle(index: Int): String { 22 | return when (index) { 23 | 0 -> source 24 | 1 -> result 25 | else -> "" 26 | } 27 | } 28 | 29 | 30 | override fun setValueAt(column: Int, text: String?) { 31 | when (column) { 32 | 0 -> source = text ?: "" 33 | 1 -> result = text ?: "" 34 | } 35 | } 36 | 37 | 38 | } -------------------------------------------------------------------------------- /src/top/wuhaojie/se/entity/TaskHolder.kt: -------------------------------------------------------------------------------- 1 | package top.wuhaojie.se.entity 2 | 3 | import com.intellij.ide.highlighter.JavaFileType 4 | import com.intellij.openapi.vfs.VirtualFile 5 | 6 | data class TaskHolder( 7 | var prefix: String = "", 8 | var fields: List = emptyList(), 9 | var currentFile: VirtualFile? = null, 10 | var desFile: VirtualFile? = null, 11 | var descTag: String = "START", 12 | var javaExtractTemplate: String = "getString(\$id)" 13 | ) { 14 | fun selectedFields(): List { 15 | return fields.filter { it.isSelected } 16 | } 17 | 18 | fun isJavaFile(): Boolean { 19 | return currentFile?.fileType is JavaFileType 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /src/top/wuhaojie/se/process/AbsWriter.kt: -------------------------------------------------------------------------------- 1 | package top.wuhaojie.se.process 2 | 3 | import com.intellij.openapi.fileEditor.FileDocumentManager 4 | import com.intellij.openapi.vfs.VirtualFile 5 | import java.io.BufferedReader 6 | import java.io.BufferedWriter 7 | import java.io.InputStreamReader 8 | import java.io.OutputStreamWriter 9 | 10 | open class AbsWriter { 11 | 12 | protected fun saveAllFile() { 13 | FileDocumentManager.getInstance().saveAllDocuments() 14 | } 15 | 16 | 17 | protected fun writeFileContent(file: VirtualFile, content: String) { 18 | val outputStream = file.getOutputStream(this) 19 | val writer = BufferedWriter(OutputStreamWriter(outputStream)) 20 | writer.write(content) 21 | writer.close() 22 | } 23 | 24 | protected fun readFileContent(file: VirtualFile): String { 25 | val inputStream = file.inputStream 26 | val reader = BufferedReader(InputStreamReader(inputStream)) 27 | var line: String 28 | val builder = StringBuilder() 29 | while (true) { 30 | line = reader.readLine() ?: break 31 | builder.append(line).appendln() 32 | } 33 | val content = builder.toString() 34 | reader.close() 35 | return content 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /src/top/wuhaojie/se/process/FileProcessor.kt: -------------------------------------------------------------------------------- 1 | package top.wuhaojie.se.process 2 | 3 | import com.intellij.openapi.project.Project 4 | import com.intellij.openapi.roots.ProjectRootManager 5 | import com.intellij.psi.PsiFile 6 | import top.wuhaojie.se.entity.TaskHolder 7 | 8 | object FileProcessor { 9 | 10 | 11 | // src/main/res/values/strings.xml 12 | private val defaultFilePath = "src/main/res/values/strings.xml" 13 | 14 | fun process(project: Project, psiFile: PsiFile, taskHolder: TaskHolder) { 15 | val virtualFile = psiFile.virtualFile 16 | taskHolder.currentFile = virtualFile 17 | val module = ProjectRootManager.getInstance(project).fileIndex.getModuleForFile(virtualFile) ?: return 18 | val moduleFile = module.moduleFile ?: return 19 | val desFile = moduleFile.parent.findFileByRelativePath(defaultFilePath) ?: return 20 | taskHolder.desFile = desFile 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /src/top/wuhaojie/se/process/JavaWriter.kt: -------------------------------------------------------------------------------- 1 | package top.wuhaojie.se.process 2 | 3 | import top.wuhaojie.se.entity.TaskHolder 4 | 5 | class JavaWriter : AbsWriter() { 6 | 7 | 8 | fun process(taskHolder: TaskHolder) { 9 | write(taskHolder) 10 | } 11 | 12 | private fun write(taskHolder: TaskHolder) { 13 | val file = taskHolder.currentFile ?: return 14 | var content = readFileContent(file) 15 | 16 | val extractTemplate = taskHolder.javaExtractTemplate 17 | for (field in taskHolder.selectedFields()) { 18 | val text = field.source 19 | val replace = extractTemplate.replace("\$id", "R.string.${field.result}") 20 | content = content.replace("\"$text\"", replace) 21 | } 22 | 23 | writeFileContent(file, content) 24 | } 25 | 26 | 27 | } -------------------------------------------------------------------------------- /src/top/wuhaojie/se/process/PrefixProcessor.kt: -------------------------------------------------------------------------------- 1 | package top.wuhaojie.se.process 2 | 3 | import com.intellij.openapi.project.Project 4 | import com.intellij.openapi.roots.ProjectRootManager 5 | import com.intellij.psi.PsiFile 6 | import top.wuhaojie.se.common.StringUtils 7 | import top.wuhaojie.se.entity.TaskHolder 8 | 9 | object PrefixProcessor { 10 | 11 | 12 | fun refreshDefaultPrefix(project: Project, psiFile: PsiFile, taskHolder: TaskHolder) { 13 | val builder = StringBuilder() 14 | val virtualFile = psiFile.virtualFile 15 | val module = ProjectRootManager.getInstance(project).fileIndex.getModuleForFile(virtualFile) 16 | val moduleName = if (module == null) "" else "${module.name.toLowerCase()}_" 17 | builder.append(moduleName) 18 | val name = virtualFile.name.split(".")[0] 19 | val componentName = "${formatComponentName(name)}_" 20 | builder.append(componentName) 21 | refreshPrefix(taskHolder, builder.toString()) 22 | } 23 | 24 | private fun formatComponentName(name: String): String { 25 | return StringUtils.underscoreString(name) 26 | } 27 | 28 | fun refreshPrefix(taskHolder: TaskHolder, prefix: String) { 29 | taskHolder.prefix = prefix 30 | val fields = taskHolder.fields 31 | for (i in IntRange(0, fields.size - 1)) { 32 | fields[i].result = prefix + fields[i].resultSrc 33 | } 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /src/top/wuhaojie/se/process/StringsWriter.kt: -------------------------------------------------------------------------------- 1 | package top.wuhaojie.se.process 2 | 3 | import com.intellij.lang.xml.XMLLanguage 4 | import com.intellij.openapi.project.Project 5 | import com.intellij.psi.PsiManager 6 | import com.intellij.psi.XmlElementFactory 7 | import com.intellij.psi.util.PsiTreeUtil 8 | import com.intellij.psi.xml.XmlComment 9 | import com.intellij.psi.xml.XmlFile 10 | import com.intellij.psi.xml.XmlTag 11 | import top.wuhaojie.se.entity.TaskHolder 12 | 13 | class StringsWriter( 14 | private val project: Project 15 | ) : AbsWriter() { 16 | 17 | 18 | private fun openStringsFile(taskHolder: TaskHolder): XmlFile? { 19 | val virtualFile = taskHolder.desFile ?: return null 20 | return PsiManager.getInstance(project).findFile(virtualFile) as? XmlFile ?: return null 21 | } 22 | 23 | private fun writeComment(rootTag: XmlTag, text: String) { 24 | val factory = XmlElementFactory.getInstance(project) 25 | val container = factory.createTagFromText("", XMLLanguage.INSTANCE) 26 | val xmlComment = PsiTreeUtil.getChildOfType(container, XmlComment::class.java) ?: return 27 | rootTag.add(xmlComment) 28 | } 29 | 30 | private fun writeContent(rootTag: XmlTag, taskHolder: TaskHolder) { 31 | val fields = taskHolder.selectedFields() 32 | for (field in fields) { 33 | val childTag = rootTag.createChildTag("string", "", field.source, false) 34 | childTag.setAttribute("name", field.result) 35 | rootTag.add(childTag) 36 | } 37 | } 38 | 39 | 40 | fun process(taskHolder: TaskHolder) { 41 | if (taskHolder.selectedFields().isEmpty()) { 42 | return 43 | } 44 | saveAllFile() 45 | val xmlFile = openStringsFile(taskHolder) ?: return 46 | val rootTag = xmlFile.rootTag ?: return 47 | writeComment(rootTag, taskHolder.descTag) 48 | writeContent(rootTag, taskHolder) 49 | saveAllFile() 50 | } 51 | 52 | } -------------------------------------------------------------------------------- /src/top/wuhaojie/se/process/TextFormatProcessor.kt: -------------------------------------------------------------------------------- 1 | package top.wuhaojie.se.process 2 | 3 | import top.wuhaojie.se.entity.TaskHolder 4 | 5 | object TextFormatProcessor { 6 | 7 | private val simpleWords = arrayOf( 8 | "a", "an", "the" 9 | ) 10 | 11 | 12 | private fun removeSimpleWord(src: String): String { 13 | var result = src 14 | for (simpleWord in simpleWords) { 15 | result = result.replace(" $simpleWord ", " ") 16 | } 17 | return result 18 | } 19 | 20 | private fun cleanText(src: String): String { 21 | var result = src.replace(',', ' ') 22 | result = result.replace('.', ' ') 23 | result = result.replace('!', ' ') 24 | result = result.replace('?', ' ') 25 | result = result.replace('-', ' ') 26 | result = result.replace('/', ' ') 27 | result = result.replace(Regex(" +"), " ") 28 | result = result.trim() 29 | result = result.toLowerCase() 30 | return result 31 | } 32 | 33 | private fun concat(src: String): String { 34 | return src.replace(' ', '_') 35 | } 36 | 37 | 38 | fun processText(src: String): String { 39 | val removeSimpleWord = removeSimpleWord(src) 40 | val cleanText = cleanText(removeSimpleWord) 41 | return concat(cleanText) 42 | } 43 | 44 | 45 | fun process(taskHolder: TaskHolder) { 46 | val fields = taskHolder.selectedFields() 47 | for (i in IntRange(0, fields.size - 1)) { 48 | val src = fields[i].result 49 | val processText = processText(src) 50 | fields[i].result = processText 51 | fields[i].resultSrc = processText 52 | } 53 | } 54 | 55 | } -------------------------------------------------------------------------------- /src/top/wuhaojie/se/process/TranslateProcessor.kt: -------------------------------------------------------------------------------- 1 | package top.wuhaojie.se.process 2 | 3 | import top.wuhaojie.se.entity.TaskHolder 4 | import top.wuhaojie.se.translate.Translator 5 | 6 | object TranslateProcessor { 7 | 8 | fun process(taskHolder: TaskHolder) { 9 | val fields = taskHolder.fields 10 | for (i in IntRange(0, fields.size - 1)) { 11 | val source = fields[i].source 12 | val english = Translator.toEnglish(source) 13 | fields[i].result = english 14 | fields[i].resultSrc = english 15 | } 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /src/top/wuhaojie/se/process/XmlWriter.kt: -------------------------------------------------------------------------------- 1 | package top.wuhaojie.se.process 2 | 3 | import top.wuhaojie.se.entity.TaskHolder 4 | 5 | class XmlWriter : AbsWriter() { 6 | 7 | fun process(taskHolder: TaskHolder) { 8 | val file = taskHolder.currentFile ?: return 9 | var content = readFileContent(file) 10 | 11 | for (field in taskHolder.selectedFields()) { 12 | val text = field.source 13 | content = content.replace("android:text=\"$text\"", "android:text=\"@string/${field.result}\"") 14 | } 15 | 16 | writeFileContent(file, content) 17 | } 18 | 19 | } -------------------------------------------------------------------------------- /src/top/wuhaojie/se/process/finder/AbsFieldFinder.kt: -------------------------------------------------------------------------------- 1 | package top.wuhaojie.se.process.finder 2 | 3 | import com.intellij.openapi.fileEditor.FileDocumentManager 4 | import com.intellij.psi.PsiFile 5 | import top.wuhaojie.se.entity.FieldEntity 6 | import top.wuhaojie.se.entity.TaskHolder 7 | import java.io.BufferedReader 8 | import java.io.InputStreamReader 9 | 10 | abstract class AbsFieldFinder { 11 | 12 | fun find(psiFile: PsiFile): TaskHolder { 13 | // save before operation 14 | FileDocumentManager.getInstance().saveAllDocuments() 15 | 16 | val taskHolder = TaskHolder() 17 | 18 | val virtualFile = psiFile.virtualFile ?: return taskHolder 19 | val inputStream = virtualFile.inputStream 20 | val reader = BufferedReader(InputStreamReader(inputStream)) 21 | 22 | val stringBuilder = StringBuilder() 23 | while (true) { 24 | val line = reader.readLine() ?: break 25 | stringBuilder.append(line).appendln() 26 | } 27 | reader.close() 28 | val content = stringBuilder.toString() 29 | 30 | val regex = Regex(regex()) 31 | val result = regex.findAll(content) 32 | 33 | taskHolder.fields = result 34 | .map { it.value } 35 | .map { transformToString(it) } 36 | .filter { isDefaultChecked(it) } 37 | .map { FieldEntity(it, "", true) } 38 | .toList() 39 | 40 | return taskHolder 41 | } 42 | 43 | abstract fun isDefaultChecked(it: String): Boolean 44 | 45 | abstract protected fun transformToString(it: String): String 46 | 47 | abstract protected fun regex(): String 48 | 49 | } -------------------------------------------------------------------------------- /src/top/wuhaojie/se/process/finder/JavaFieldFinder.kt: -------------------------------------------------------------------------------- 1 | package top.wuhaojie.se.process.finder 2 | 3 | class JavaFieldFinder : AbsFieldFinder() { 4 | 5 | override fun isDefaultChecked(it: String): Boolean { 6 | return it.isNotBlank() 7 | } 8 | 9 | override fun regex() = "\".*?\"" 10 | 11 | override fun transformToString(it: String) = it.replace("\"", "") 12 | 13 | 14 | // 判断一个字符是否是中文 15 | private fun isChinese(c: Char): Boolean { 16 | return c.toInt() in 0x4E00..0x9FA5// 根据字节码判断 17 | } 18 | 19 | // 判断一个字符串是否含有中文 20 | private fun isChinese(str: String?): Boolean { 21 | if (str == null) return false 22 | str.toCharArray().forEach { if (isChinese(it)) return true } 23 | return false 24 | } 25 | } -------------------------------------------------------------------------------- /src/top/wuhaojie/se/process/finder/LayoutXmlFieldFinder.kt: -------------------------------------------------------------------------------- 1 | package top.wuhaojie.se.process.finder 2 | 3 | class LayoutXmlFieldFinder : AbsFieldFinder() { 4 | 5 | override fun isDefaultChecked(it: String): Boolean { 6 | return !it.contains("@string/") 7 | } 8 | 9 | override fun transformToString(it: String): String { 10 | val result = it.replace("android:text=\"", "") 11 | return result.replace("\"", "") 12 | } 13 | 14 | override fun regex(): String = "android:text=\".*?\"" 15 | 16 | } -------------------------------------------------------------------------------- /src/top/wuhaojie/se/translate/HttpGet.java: -------------------------------------------------------------------------------- 1 | package top.wuhaojie.se.translate; 2 | 3 | import javax.net.ssl.HttpsURLConnection; 4 | import javax.net.ssl.SSLContext; 5 | import javax.net.ssl.TrustManager; 6 | import javax.net.ssl.X509TrustManager; 7 | import java.io.*; 8 | import java.net.HttpURLConnection; 9 | import java.net.URL; 10 | import java.net.URLEncoder; 11 | import java.security.KeyManagementException; 12 | import java.security.NoSuchAlgorithmException; 13 | import java.security.cert.CertificateException; 14 | import java.security.cert.X509Certificate; 15 | import java.util.Map; 16 | 17 | class HttpGet { 18 | 19 | private static final int SOCKET_TIMEOUT = 10000; // 10S 20 | private static final String GET = "GET"; 21 | 22 | public static String get(String host, Map params) { 23 | try { 24 | // 设置SSLContext 25 | SSLContext sslcontext = SSLContext.getInstance("TLS"); 26 | sslcontext.init(null, new TrustManager[] { myX509TrustManager }, null); 27 | 28 | String sendUrl = getUrlWithQueryString(host, params); 29 | 30 | // System.out.println("URL:" + sendUrl); 31 | 32 | URL uri = new URL(sendUrl); // 创建URL对象 33 | HttpURLConnection conn = (HttpURLConnection) uri.openConnection(); 34 | if (conn instanceof HttpsURLConnection) { 35 | ((HttpsURLConnection) conn).setSSLSocketFactory(sslcontext.getSocketFactory()); 36 | } 37 | 38 | conn.setConnectTimeout(SOCKET_TIMEOUT); // 设置相应超时 39 | conn.setRequestMethod(GET); 40 | int statusCode = conn.getResponseCode(); 41 | if (statusCode != HttpURLConnection.HTTP_OK) { 42 | System.out.println("Http错误码:" + statusCode); 43 | } 44 | 45 | // 读取服务器的数据 46 | InputStream is = conn.getInputStream(); 47 | BufferedReader br = new BufferedReader(new InputStreamReader(is)); 48 | StringBuilder builder = new StringBuilder(); 49 | String line = null; 50 | while ((line = br.readLine()) != null) { 51 | builder.append(line); 52 | } 53 | 54 | String text = builder.toString(); 55 | 56 | close(br); // 关闭数据流 57 | close(is); // 关闭数据流 58 | conn.disconnect(); // 断开连接 59 | 60 | return text; 61 | } catch (IOException | KeyManagementException | NoSuchAlgorithmException e) { 62 | e.printStackTrace(); 63 | } 64 | 65 | return null; 66 | } 67 | 68 | private static String getUrlWithQueryString(String url, Map params) { 69 | if (params == null) { 70 | return url; 71 | } 72 | 73 | StringBuilder builder = new StringBuilder(url); 74 | if (url.contains("?")) { 75 | builder.append("&"); 76 | } else { 77 | builder.append("?"); 78 | } 79 | 80 | int i = 0; 81 | for (String key : params.keySet()) { 82 | String value = params.get(key); 83 | if (value == null) { // 过滤空的key 84 | continue; 85 | } 86 | 87 | if (i != 0) { 88 | builder.append('&'); 89 | } 90 | 91 | builder.append(key); 92 | builder.append('='); 93 | builder.append(encode(value)); 94 | 95 | i++; 96 | } 97 | 98 | return builder.toString(); 99 | } 100 | 101 | private static void close(Closeable closeable) { 102 | if (closeable != null) { 103 | try { 104 | closeable.close(); 105 | } catch (IOException e) { 106 | e.printStackTrace(); 107 | } 108 | } 109 | } 110 | 111 | /** 112 | * 对输入的字符串进行URL编码, 即转换为%20这种形式 113 | * 114 | * @param input 原文 115 | * @return URL编码. 如果编码失败, 则返回原文 116 | */ 117 | private static String encode(String input) { 118 | if (input == null) { 119 | return ""; 120 | } 121 | 122 | try { 123 | return URLEncoder.encode(input, "utf-8"); 124 | } catch (UnsupportedEncodingException e) { 125 | e.printStackTrace(); 126 | } 127 | 128 | return input; 129 | } 130 | 131 | private static TrustManager myX509TrustManager = new X509TrustManager() { 132 | 133 | @Override 134 | public X509Certificate[] getAcceptedIssuers() { 135 | return null; 136 | } 137 | 138 | @Override 139 | public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { 140 | } 141 | 142 | @Override 143 | public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { 144 | } 145 | }; 146 | 147 | } 148 | -------------------------------------------------------------------------------- /src/top/wuhaojie/se/translate/JsonUtils.kt: -------------------------------------------------------------------------------- 1 | import com.google.gson.Gson 2 | import java.lang.reflect.Type 3 | 4 | object JsonUtils { 5 | 6 | private val gson = Gson() 7 | 8 | fun fromJson(json: String, classOfT: Class): T { 9 | return gson.fromJson(json, classOfT) 10 | } 11 | 12 | fun fromJson(json: String, typeOfT: Type): T { 13 | return gson.fromJson(json, typeOfT) 14 | } 15 | 16 | 17 | fun toJson(src: Any): String { 18 | return gson.toJson(src) 19 | } 20 | 21 | 22 | } -------------------------------------------------------------------------------- /src/top/wuhaojie/se/translate/MD5.java: -------------------------------------------------------------------------------- 1 | package top.wuhaojie.se.translate; 2 | 3 | import java.io.*; 4 | import java.security.MessageDigest; 5 | import java.security.NoSuchAlgorithmException; 6 | 7 | /** 8 | * MD5编码相关的类 9 | * 10 | * @author wangjingtao 11 | * 12 | */ 13 | public class MD5 { 14 | // 首先初始化一个字符数组,用来存放每个16进制字符 15 | private static final char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 16 | 'e', 'f' }; 17 | 18 | /** 19 | * 获得一个字符串的MD5值 20 | * 21 | * @param input 输入的字符串 22 | * @return 输入字符串的MD5值 23 | * 24 | */ 25 | public static String md5(String input) { 26 | if (input == null) 27 | return null; 28 | 29 | try { 30 | // 拿到一个MD5转换器(如果想要SHA1参数换成”SHA1”) 31 | MessageDigest messageDigest = MessageDigest.getInstance("MD5"); 32 | // 输入的字符串转换成字节数组 33 | byte[] inputByteArray = input.getBytes("utf-8"); 34 | // inputByteArray是输入字符串转换得到的字节数组 35 | messageDigest.update(inputByteArray); 36 | // 转换并返回结果,也是字节数组,包含16个元素 37 | byte[] resultByteArray = messageDigest.digest(); 38 | // 字符数组转换成字符串返回 39 | return byteArrayToHex(resultByteArray); 40 | } catch (Exception e) { 41 | return null; 42 | } 43 | } 44 | 45 | /** 46 | * 获取文件的MD5值 47 | * 48 | * @param file 49 | * @return 50 | */ 51 | public static String md5(File file) { 52 | try { 53 | if (!file.isFile()) { 54 | System.err.println("文件" + file.getAbsolutePath() + "不存在或者不是文件"); 55 | return null; 56 | } 57 | 58 | FileInputStream in = new FileInputStream(file); 59 | 60 | String result = md5(in); 61 | 62 | in.close(); 63 | 64 | return result; 65 | 66 | } catch (IOException e) { 67 | e.printStackTrace(); 68 | } 69 | 70 | return null; 71 | } 72 | 73 | private static String md5(InputStream in) { 74 | 75 | try { 76 | MessageDigest messagedigest = MessageDigest.getInstance("MD5"); 77 | 78 | byte[] buffer = new byte[1024]; 79 | int read = 0; 80 | while ((read = in.read(buffer)) != -1) { 81 | messagedigest.update(buffer, 0, read); 82 | } 83 | 84 | in.close(); 85 | 86 | return byteArrayToHex(messagedigest.digest()); 87 | } catch (NoSuchAlgorithmException | IOException e) { 88 | e.printStackTrace(); 89 | } 90 | 91 | return null; 92 | } 93 | 94 | private static String byteArrayToHex(byte[] byteArray) { 95 | // new一个字符数组,这个就是用来组成结果字符串的(解释一下:一个byte是八位二进制,也就是2位十六进制字符(2的8次方等于16的2次方)) 96 | char[] resultCharArray = new char[byteArray.length * 2]; 97 | // 遍历字节数组,通过位运算(位运算效率高),转换成字符放到字符数组中去 98 | int index = 0; 99 | for (byte b : byteArray) { 100 | resultCharArray[index++] = hexDigits[b >>> 4 & 0xf]; 101 | resultCharArray[index++] = hexDigits[b & 0xf]; 102 | } 103 | 104 | // 字符数组组合成字符串返回 105 | return new String(resultCharArray); 106 | 107 | } 108 | 109 | } -------------------------------------------------------------------------------- /src/top/wuhaojie/se/translate/TransApi.java: -------------------------------------------------------------------------------- 1 | package top.wuhaojie.se.translate; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public class TransApi { 7 | private static final String TRANS_API_HOST = "http://api.fanyi.baidu.com/api/trans/vip/translate"; 8 | 9 | private String appid; 10 | private String securityKey; 11 | 12 | public TransApi(String appid, String securityKey) { 13 | this.appid = appid; 14 | this.securityKey = securityKey; 15 | } 16 | 17 | public String getTransResult(String query, String from, String to) { 18 | Map params = buildParams(query, from, to); 19 | return HttpGet.get(TRANS_API_HOST, params); 20 | } 21 | 22 | private Map buildParams(String query, String from, String to) { 23 | Map params = new HashMap(); 24 | params.put("q", query); 25 | params.put("from", from); 26 | params.put("to", to); 27 | 28 | params.put("appid", appid); 29 | 30 | // 随机数 31 | String salt = String.valueOf(System.currentTimeMillis()); 32 | params.put("salt", salt); 33 | 34 | // 签名 35 | String src = appid + query + salt + securityKey; // 加密前的原文 36 | params.put("sign", MD5.md5(src)); 37 | 38 | return params; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/top/wuhaojie/se/translate/TransResponse.kt: -------------------------------------------------------------------------------- 1 | package top.wuhaojie.se.translate 2 | 3 | data class TransResponse( 4 | var from: String = "", 5 | var to: String = "", 6 | var trans_result: List = emptyList() 7 | ) -------------------------------------------------------------------------------- /src/top/wuhaojie/se/translate/TransResult.kt: -------------------------------------------------------------------------------- 1 | package top.wuhaojie.se.translate 2 | 3 | 4 | data class TransResult( 5 | var src: String = "", 6 | var dst: String = "" 7 | ) -------------------------------------------------------------------------------- /src/top/wuhaojie/se/translate/Translator.kt: -------------------------------------------------------------------------------- 1 | package top.wuhaojie.se.translate 2 | 3 | object Translator { 4 | 5 | private val appId = "20180502000152597" 6 | private val securityKey = "JcH2Q2NXL4AWLg_Nsxbi" 7 | private val api by lazy { 8 | TransApi(appId, securityKey) 9 | } 10 | 11 | fun toEnglish(src: String): String { 12 | val json = api.getTransResult(src, "zh", "en") 13 | val response = JsonUtils.fromJson(json, TransResponse::class.java) 14 | if (response.trans_result.isNotEmpty()) { 15 | return response.trans_result[0].dst 16 | } 17 | return "" 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /src/top/wuhaojie/se/ui/FieldsDialog.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 |
136 | -------------------------------------------------------------------------------- /src/top/wuhaojie/se/ui/FieldsDialog.java: -------------------------------------------------------------------------------- 1 | package top.wuhaojie.se.ui; 2 | 3 | import cn.vearn.checktreetable.FiledTreeTableModel; 4 | import com.intellij.openapi.command.WriteCommandAction; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.psi.PsiClass; 7 | import com.intellij.psi.PsiElementFactory; 8 | import com.intellij.psi.PsiFile; 9 | import com.intellij.ui.JBColor; 10 | import org.jdesktop.swingx.JXTreeTable; 11 | import org.jdesktop.swingx.treetable.DefaultMutableTreeTableNode; 12 | import org.jdesktop.swingx.ux.CheckTreeTableManager; 13 | import top.wuhaojie.se.action.DataWriter; 14 | import top.wuhaojie.se.entity.FieldEntity; 15 | import top.wuhaojie.se.entity.TaskHolder; 16 | import top.wuhaojie.se.process.PrefixProcessor; 17 | 18 | import javax.swing.*; 19 | import javax.swing.event.DocumentEvent; 20 | import javax.swing.event.DocumentListener; 21 | import java.awt.event.ActionEvent; 22 | import java.awt.event.KeyEvent; 23 | import java.awt.event.WindowAdapter; 24 | import java.awt.event.WindowEvent; 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | 28 | import static javax.swing.ListSelectionModel.SINGLE_SELECTION; 29 | 30 | public class FieldsDialog extends JFrame { 31 | 32 | private JPanel contentPane; 33 | private JButton buttonOK; 34 | private JButton buttonCancel; 35 | private JPanel filedPanel; 36 | private JScrollPane sp; 37 | private PsiClass psiClass; 38 | private PsiElementFactory factory; 39 | private PsiFile file; 40 | private Project project; 41 | private JLabel generateClass; 42 | private JTextField textPrefix; 43 | private JTextField etTemplate; 44 | private JLabel labelTemplate; 45 | private JLabel labelExample; 46 | private ArrayList defaultMutableTreeTableNodeList; 47 | 48 | private TaskHolder taskHolder; 49 | 50 | 51 | public FieldsDialog(PsiElementFactory factory, PsiClass psiClass, PsiFile file, Project project, TaskHolder taskHolder) { 52 | this.factory = factory; 53 | this.file = file; 54 | this.project = project; 55 | this.psiClass = psiClass; 56 | this.taskHolder = taskHolder; 57 | setContentPane(contentPane); 58 | setTitle("String Extractor"); 59 | getRootPane().setDefaultButton(buttonOK); 60 | this.setAlwaysOnTop(true); 61 | initListener(); 62 | } 63 | 64 | private void initListener() { 65 | defaultMutableTreeTableNodeList = new ArrayList<>(); 66 | 67 | JXTreeTable jxTreeTable = new JXTreeTable(new FiledTreeTableModel(createData())); 68 | CheckTreeTableManager manager = new CheckTreeTableManager(jxTreeTable); 69 | manager.getSelectionModel().addPathsByNodes(defaultMutableTreeTableNodeList); 70 | jxTreeTable.getColumnModel().getColumn(0).setPreferredWidth(150); 71 | 72 | 73 | jxTreeTable.expandAll(); 74 | jxTreeTable.setCellSelectionEnabled(false); 75 | final DefaultListSelectionModel defaultListSelectionModel = new DefaultListSelectionModel(); 76 | jxTreeTable.setSelectionModel(defaultListSelectionModel); 77 | 78 | defaultListSelectionModel.setSelectionMode(SINGLE_SELECTION); 79 | defaultListSelectionModel.addListSelectionListener(e -> defaultListSelectionModel.clearSelection()); 80 | defaultMutableTreeTableNodeList = null; 81 | jxTreeTable.setRowHeight(30); 82 | sp.setViewportView(jxTreeTable); 83 | 84 | 85 | buttonOK.addActionListener(e -> onOK()); 86 | buttonCancel.addActionListener(e -> onCancel()); 87 | 88 | contentPane.registerKeyboardAction(e -> onCancel(), KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); 89 | contentPane.registerKeyboardAction(e -> onOK(), KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); 90 | setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); 91 | addWindowListener(new WindowAdapter() { 92 | public void windowClosing(WindowEvent e) { 93 | onCancel(); 94 | } 95 | }); 96 | 97 | textPrefix.setText(taskHolder.getPrefix()); 98 | textPrefix.addActionListener(e -> { 99 | if (e.getID() == ActionEvent.ACTION_PERFORMED) { 100 | String text = e.getActionCommand(); 101 | PrefixProcessor.INSTANCE.refreshPrefix(taskHolder, text); 102 | jxTreeTable.updateUI(); 103 | } 104 | }); 105 | textPrefix.getDocument().addDocumentListener(new DocumentListener() { 106 | @Override 107 | public void insertUpdate(DocumentEvent e) { 108 | onChanged(); 109 | } 110 | 111 | @Override 112 | public void removeUpdate(DocumentEvent e) { 113 | onChanged(); 114 | } 115 | 116 | @Override 117 | public void changedUpdate(DocumentEvent e) { 118 | onChanged(); 119 | } 120 | 121 | private void onChanged() { 122 | String text = textPrefix.getText(); 123 | PrefixProcessor.INSTANCE.refreshPrefix(taskHolder, text); 124 | jxTreeTable.updateUI(); 125 | } 126 | }); 127 | 128 | labelTemplate.setVisible(taskHolder.isJavaFile()); 129 | etTemplate.setVisible(taskHolder.isJavaFile()); 130 | labelExample.setVisible(taskHolder.isJavaFile()); 131 | 132 | etTemplate.setText(taskHolder.getJavaExtractTemplate()); 133 | etTemplate.getDocument().addDocumentListener(new DocumentListener() { 134 | @Override 135 | public void insertUpdate(DocumentEvent e) { 136 | onChanged(); 137 | } 138 | 139 | @Override 140 | public void removeUpdate(DocumentEvent e) { 141 | onChanged(); 142 | } 143 | 144 | @Override 145 | public void changedUpdate(DocumentEvent e) { 146 | onChanged(); 147 | } 148 | 149 | private void onChanged() { 150 | checkTemplate(); 151 | } 152 | }); 153 | checkTemplate(); 154 | } 155 | 156 | private void checkTemplate() { 157 | String text = etTemplate.getText(); 158 | taskHolder.setJavaExtractTemplate(text); 159 | if (!text.contains("$id")) { 160 | labelExample.setForeground(JBColor.RED); 161 | labelExample.setText("must contains \"$id\""); 162 | return; 163 | } 164 | String templateEg = text.replace("$id", "R.string.simple_text"); 165 | labelExample.setForeground(JBColor.GRAY); 166 | labelExample.setText("eg: " + templateEg); 167 | } 168 | 169 | private void onOK() { 170 | this.setAlwaysOnTop(false); 171 | WriteCommandAction.runWriteCommandAction(project, () -> { 172 | setVisible(false); 173 | DataWriter dataWriter = new DataWriter(file, project, psiClass, taskHolder); 174 | dataWriter.go(); 175 | }); 176 | } 177 | 178 | private void onCancel() { 179 | dispose(); 180 | } 181 | 182 | private DefaultMutableTreeTableNode createData() { 183 | DefaultMutableTreeTableNode root = new DefaultMutableTreeTableNode(); 184 | createDataNode(root); 185 | return root; 186 | } 187 | 188 | private void createDataNode(DefaultMutableTreeTableNode root) { 189 | List fields = taskHolder.getFields(); 190 | for (FieldEntity field : fields) { 191 | DefaultMutableTreeTableNode node = new DefaultMutableTreeTableNode(field); 192 | root.add(node); 193 | defaultMutableTreeTableNodeList.add(node); 194 | } 195 | } 196 | 197 | } 198 | -------------------------------------------------------------------------------- /src/top/wuhaojie/se/ui/Toast.java: -------------------------------------------------------------------------------- 1 | package top.wuhaojie.se.ui; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.intellij.openapi.ui.MessageType; 5 | import com.intellij.openapi.ui.popup.Balloon; 6 | import com.intellij.openapi.ui.popup.JBPopupFactory; 7 | import com.intellij.openapi.wm.StatusBar; 8 | import com.intellij.openapi.wm.WindowManager; 9 | import com.intellij.ui.awt.RelativePoint; 10 | 11 | import javax.swing.*; 12 | 13 | /** 14 | * Created by dim on 15/8/24. 15 | */ 16 | public class Toast { 17 | 18 | /** 19 | * Display simple notification of given type 20 | * 21 | * @param project 22 | * @param type 23 | * @param text 24 | */ 25 | public static void make(Project project, JComponent jComponent, MessageType type, String text) { 26 | 27 | StatusBar statusBar = WindowManager.getInstance().getStatusBar(project); 28 | 29 | JBPopupFactory.getInstance() 30 | .createHtmlTextBalloonBuilder(text, type, null) 31 | .setFadeoutTime(7500) 32 | .createBalloon() 33 | .show(RelativePoint.getCenterOf(jComponent), Balloon.Position.above); 34 | } 35 | 36 | /** 37 | * Display simple notification of given type 38 | * 39 | * @param project 40 | * @param type 41 | * @param text 42 | */ 43 | public static void make(Project project, MessageType type, String text) { 44 | 45 | StatusBar statusBar = WindowManager.getInstance().getStatusBar(project); 46 | 47 | JBPopupFactory.getInstance() 48 | .createHtmlTextBalloonBuilder(text, type, null) 49 | .setFadeoutTime(7500) 50 | .createBalloon() 51 | .show(RelativePoint.getCenterOf(statusBar.getComponent()), Balloon.Position.atRight); 52 | } 53 | } 54 | --------------------------------------------------------------------------------