├── 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 | 
4 |
5 | 帮助 Android 开发者一键释放字符串资源的 Android Studio 插件,[最新版本 v1.0](https://github.com/a-voyager/StringExtractor/raw/master/release/StringExtractor.zip)
6 |
7 | 欢迎 Fork & Star
8 |
9 | 
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 | 
38 |
39 | (3)用法
40 |
41 | 弹窗出现后,可以看到默认生成的字符串资源 ID,之后检查并修改资源 ID 前缀。如果是释放 Java 代码中的字符串,需要再检查并修改生成 Java 代码的模板。最后点击 OK,即可在 对应模块的 strings.xml 中生成字符串资源
42 |
43 | 
44 |
45 | 来一个动图展示:
46 |
47 | 
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 |
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 |
--------------------------------------------------------------------------------