diff --git a/build.gradle b/build.gradle index d5686b7..2884908 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ plugins { } group 'cn.enaium' -version '0.9.0' +version '0.10.0' sourceCompatibility = targetCompatibility = JavaVersion.VERSION_1_8 diff --git a/src/main/java/cn/enaium/joe/JavaOctetEditor.java b/src/main/java/cn/enaium/joe/JavaOctetEditor.java index 4b32b97..0a8d696 100644 --- a/src/main/java/cn/enaium/joe/JavaOctetEditor.java +++ b/src/main/java/cn/enaium/joe/JavaOctetEditor.java @@ -17,7 +17,6 @@ package cn.enaium.joe; import cn.enaium.joe.config.ConfigManager; -import cn.enaium.joe.config.extend.CFRConfig; import cn.enaium.joe.gui.panel.BottomPanel; import cn.enaium.joe.gui.panel.LeftPanel; import cn.enaium.joe.gui.panel.file.tabbed.FileTabbedPanel; @@ -27,6 +26,7 @@ import cn.enaium.joe.gui.panel.menu.HelpMenu; import cn.enaium.joe.gui.panel.menu.SearchMenu; import cn.enaium.joe.jar.Jar; +import cn.enaium.joe.task.TaskManager; import cn.enaium.joe.util.LangUtil; import cn.enaium.joe.util.MessageUtil; import org.fife.ui.rsyntaxtextarea.AbstractTokenMakerFactory; @@ -55,13 +55,16 @@ public class JavaOctetEditor { public BottomPanel bottomPanel; - public ConfigManager configManager; + public ConfigManager config; + + public TaskManager task; public JavaOctetEditor() { instance = this; - configManager = new ConfigManager(); - configManager.load(); - Runtime.getRuntime().addShutdownHook(new Thread(configManager::save)); + config = new ConfigManager(); + config.load(); + task = new TaskManager(); + Runtime.getRuntime().addShutdownHook(new Thread(config::save)); fileTabbedPanel = new FileTabbedPanel(); fileTreePanel = new FileTreePanel(); bottomPanel = new BottomPanel(); diff --git a/src/main/java/cn/enaium/joe/annotation/Indeterminate.java b/src/main/java/cn/enaium/joe/annotation/Indeterminate.java new file mode 100644 index 0000000..2ef3f7c --- /dev/null +++ b/src/main/java/cn/enaium/joe/annotation/Indeterminate.java @@ -0,0 +1,31 @@ +/* + * Copyright 2022 Enaium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.enaium.joe.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author Enaium + * @since 0.9.0 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface Indeterminate { +} diff --git a/src/main/java/cn/enaium/joe/annotation/Repeatable.java b/src/main/java/cn/enaium/joe/annotation/Repeatable.java new file mode 100644 index 0000000..d994120 --- /dev/null +++ b/src/main/java/cn/enaium/joe/annotation/Repeatable.java @@ -0,0 +1,31 @@ +/* + * Copyright 2022 Enaium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.enaium.joe.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author Enaium + * @since 0.9.0 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface Repeatable { +} diff --git a/src/main/java/cn/enaium/joe/dialog/ConfigDialog.java b/src/main/java/cn/enaium/joe/dialog/ConfigDialog.java index b54ddb9..7ed1607 100644 --- a/src/main/java/cn/enaium/joe/dialog/ConfigDialog.java +++ b/src/main/java/cn/enaium/joe/dialog/ConfigDialog.java @@ -22,7 +22,6 @@ import cn.enaium.joe.config.value.*; import cn.enaium.joe.util.LangUtil; import cn.enaium.joe.util.MessageUtil; -import org.tinylog.Logger; import javax.swing.*; import javax.swing.event.DocumentEvent; @@ -122,7 +121,7 @@ public void changedUpdate(DocumentEvent e) { addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { - JavaOctetEditor.getInstance().configManager.save(); + JavaOctetEditor.getInstance().config.save(); } }); } diff --git a/src/main/java/cn/enaium/joe/dialog/SearchDialog.java b/src/main/java/cn/enaium/joe/dialog/SearchDialog.java index 61a6fc6..453b70b 100644 --- a/src/main/java/cn/enaium/joe/dialog/SearchDialog.java +++ b/src/main/java/cn/enaium/joe/dialog/SearchDialog.java @@ -17,52 +17,28 @@ package cn.enaium.joe.dialog; import cn.enaium.joe.JavaOctetEditor; -import cn.enaium.joe.gui.panel.search.ResultNode; -import cn.enaium.joe.gui.panel.search.ResultPanel; +import cn.enaium.joe.gui.component.ResultList; import cn.enaium.joe.jar.Jar; import cn.enaium.joe.util.LangUtil; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.LdcInsnNode; import org.objectweb.asm.tree.MethodNode; import javax.swing.*; import java.awt.*; import java.util.Map; import java.util.function.BiConsumer; -import java.util.function.Consumer; /** * @author Enaium */ public class SearchDialog extends Dialog { - public ResultPanel resultPanel = new ResultPanel(); + public ResultList resultList = new ResultList(); public SearchDialog() { super(LangUtil.i18n("menu.search")); setLayout(new BorderLayout()); setSize(700, 400); - add(resultPanel, BorderLayout.CENTER); - } - - public void searchInstruction(BiConsumer consumer) { - Jar jar = JavaOctetEditor.getInstance().jar; - float loaded = 0; - float total = 0; - for (Map.Entry stringClassNodeEntry : jar.classes.entrySet()) { - for (MethodNode method : stringClassNodeEntry.getValue().methods) { - total += method.instructions.size(); - } - } - - for (Map.Entry stringClassNodeEntry : jar.classes.entrySet()) { - for (MethodNode method : stringClassNodeEntry.getValue().methods) { - for (AbstractInsnNode instruction : method.instructions) { - consumer.accept(stringClassNodeEntry.getValue(), instruction); - JavaOctetEditor.getInstance().bottomPanel.setProcess((int) ((loaded++ / total) * 100f)); - } - } - } - JavaOctetEditor.getInstance().bottomPanel.setProcess(0); + add(new JScrollPane(resultList), BorderLayout.CENTER); } } diff --git a/src/main/java/cn/enaium/joe/dialog/search/SearchFieldDialog.java b/src/main/java/cn/enaium/joe/dialog/search/SearchFieldDialog.java index 9c2e5ae..3158879 100644 --- a/src/main/java/cn/enaium/joe/dialog/search/SearchFieldDialog.java +++ b/src/main/java/cn/enaium/joe/dialog/search/SearchFieldDialog.java @@ -19,18 +19,13 @@ import cn.enaium.joe.JavaOctetEditor; import cn.enaium.joe.dialog.SearchDialog; import cn.enaium.joe.gui.panel.search.ResultNode; -import cn.enaium.joe.jar.Jar; -import cn.enaium.joe.util.ASyncUtil; +import cn.enaium.joe.task.SearchFieldTask; import cn.enaium.joe.util.LangUtil; +import cn.enaium.joe.util.MessageUtil; import cn.enaium.joe.util.StringUtil; -import org.objectweb.asm.tree.FieldInsnNode; -import org.objectweb.asm.tree.AbstractInsnNode; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.MethodNode; import javax.swing.*; import java.awt.*; -import java.util.Map; /** * @author Enaium @@ -51,19 +46,21 @@ public SearchFieldDialog() { add(description); add(new JButton(LangUtil.i18n("button.search")) {{ addActionListener(e -> { - ASyncUtil.execute(() -> { - searchInstruction((classNode, instruction) -> { - if (instruction instanceof FieldInsnNode) { - FieldInsnNode fieldInsnNode = (FieldInsnNode) instruction; - if ((fieldInsnNode.owner.contains(owner.getText()) || StringUtil.isBlank(owner.getText())) && - (fieldInsnNode.name.contains(name.getText()) || StringUtil.isBlank(name.getText())) && - (fieldInsnNode.desc.contains(description.getText()) || StringUtil.isBlank(description.getText())) - ) { - ((DefaultListModel) resultPanel.getList().getModel()).addElement(new ResultNode(classNode, fieldInsnNode.name + ":" + fieldInsnNode.desc)); + + if (StringUtil.isBlank(owner.getText()) && StringUtil.isBlank(name.getText()) && StringUtil.isBlank(description.getText())) { + MessageUtil.warning("Please input"); + return; + } + + ((DefaultListModel) resultList.getModel()).clear(); + + JavaOctetEditor.getInstance().task + .submit(new SearchFieldTask(JavaOctetEditor.getInstance().jar, owner.getText(), name.getText(), description.getText())) + .thenAccept(it -> { + for (ResultNode resultNode : it) { + ((DefaultListModel) resultList.getModel()).addElement(resultNode); } - } - }); - }); + }); }); }}); }}, BorderLayout.SOUTH); diff --git a/src/main/java/cn/enaium/joe/dialog/search/SearchLdcDialog.java b/src/main/java/cn/enaium/joe/dialog/search/SearchLdcDialog.java index a79fddb..7349ca5 100644 --- a/src/main/java/cn/enaium/joe/dialog/search/SearchLdcDialog.java +++ b/src/main/java/cn/enaium/joe/dialog/search/SearchLdcDialog.java @@ -19,17 +19,14 @@ import cn.enaium.joe.JavaOctetEditor; import cn.enaium.joe.dialog.SearchDialog; import cn.enaium.joe.gui.panel.search.ResultNode; -import cn.enaium.joe.jar.Jar; +import cn.enaium.joe.task.SearchFieldTask; +import cn.enaium.joe.task.SearchLdcTask; import cn.enaium.joe.util.ASyncUtil; import cn.enaium.joe.util.LangUtil; -import org.objectweb.asm.tree.AbstractInsnNode; -import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.LdcInsnNode; -import org.objectweb.asm.tree.MethodNode; import javax.swing.*; import java.awt.*; -import java.util.Map; /** * @author Enaium @@ -42,17 +39,16 @@ public SearchLdcDialog() { add(text); add(new JButton(LangUtil.i18n("button.search")) {{ addActionListener(e -> { - if (!text.getText().replace(" ", "").isEmpty()) { - ASyncUtil.execute(() -> { - searchInstruction((classNode, instruction) -> { - if (instruction instanceof LdcInsnNode) { - String ldc = ((LdcInsnNode) instruction).cst.toString(); - if (ldc.contains(text.getText())) { - ((DefaultListModel) resultPanel.getList().getModel()).addElement(new ResultNode(classNode, ldc)); + if (!text.getText().isEmpty()) { + ((DefaultListModel) resultList.getModel()).clear(); + JavaOctetEditor.getInstance().task + .submit(new SearchLdcTask(JavaOctetEditor.getInstance().jar, text.getText())) + .thenAccept(it -> { + resultList.removeAll(); + for (ResultNode resultNode : it) { + ((DefaultListModel) resultList.getModel()).addElement(resultNode); } - } - }); - }); + }); } }); }}); diff --git a/src/main/java/cn/enaium/joe/dialog/search/SearchMethodDialog.java b/src/main/java/cn/enaium/joe/dialog/search/SearchMethodDialog.java index 3711d35..e4aa032 100644 --- a/src/main/java/cn/enaium/joe/dialog/search/SearchMethodDialog.java +++ b/src/main/java/cn/enaium/joe/dialog/search/SearchMethodDialog.java @@ -19,15 +19,16 @@ import cn.enaium.joe.JavaOctetEditor; import cn.enaium.joe.dialog.SearchDialog; import cn.enaium.joe.gui.panel.search.ResultNode; -import cn.enaium.joe.jar.Jar; +import cn.enaium.joe.task.SearchFieldTask; +import cn.enaium.joe.task.SearchMethodTask; import cn.enaium.joe.util.ASyncUtil; import cn.enaium.joe.util.LangUtil; +import cn.enaium.joe.util.MessageUtil; import cn.enaium.joe.util.StringUtil; import org.objectweb.asm.tree.*; import javax.swing.*; import java.awt.*; -import java.util.Map; /** * @author Enaium @@ -46,24 +47,26 @@ public SearchMethodDialog() { add(new JLabel(LangUtil.i18n("search.description"))); JTextField description = new JTextField(); add(description); - JCheckBox anInterface = new JCheckBox("Interface"); + JCheckBox anInterface = new JCheckBox(LangUtil.i18n("search.interface")); add(anInterface); add(new JButton(LangUtil.i18n("button.search")) {{ addActionListener(e -> { - ASyncUtil.execute(() -> { - searchInstruction((classNode, instruction) -> { - if (instruction instanceof MethodInsnNode) { - MethodInsnNode methodInsnNode = (MethodInsnNode) instruction; - if ((methodInsnNode.owner.contains(owner.getText()) || StringUtil.isBlank(owner.getText())) && - (methodInsnNode.name.contains(name.getText()) || StringUtil.isBlank(name.getText())) && - (methodInsnNode.desc.contains(description.getText()) || StringUtil.isBlank(description.getText())) && - (methodInsnNode.itf && anInterface.isSelected() || !anInterface.isSelected()) - ) { - ((DefaultListModel) resultPanel.getList().getModel()).addElement(new ResultNode(classNode, methodInsnNode.name + "#" + methodInsnNode.desc)); + + + if (StringUtil.isBlank(owner.getText()) && StringUtil.isBlank(name.getText()) && StringUtil.isBlank(description.getText()) && !anInterface.isSelected()) { + MessageUtil.warning("Please input"); + return; + } + + + JavaOctetEditor.getInstance().task + .submit(new SearchMethodTask(JavaOctetEditor.getInstance().jar, owner.getText(), name.getText(), description.getText(), anInterface.isSelected())) + .thenAccept(it -> { + ((DefaultListModel) resultList.getModel()).clear(); + for (ResultNode resultNode : it) { + ((DefaultListModel) resultList.getModel()).addElement(resultNode); } - } - }); - }); + }); }); }}); }}, BorderLayout.SOUTH); diff --git a/src/main/java/cn/enaium/joe/gui/panel/search/ResultPanel.java b/src/main/java/cn/enaium/joe/gui/component/ResultList.java similarity index 72% rename from src/main/java/cn/enaium/joe/gui/panel/search/ResultPanel.java rename to src/main/java/cn/enaium/joe/gui/component/ResultList.java index 8ecdd19..de0f715 100644 --- a/src/main/java/cn/enaium/joe/gui/panel/search/ResultPanel.java +++ b/src/main/java/cn/enaium/joe/gui/component/ResultList.java @@ -14,11 +14,12 @@ * limitations under the License. */ -package cn.enaium.joe.gui.panel.search; +package cn.enaium.joe.gui.component; import cn.enaium.joe.JavaOctetEditor; import cn.enaium.joe.gui.panel.file.tree.FileTreePanel; import cn.enaium.joe.gui.panel.file.tree.node.*; +import cn.enaium.joe.gui.panel.search.ResultNode; import cn.enaium.joe.util.ASyncUtil; import org.objectweb.asm.tree.ClassNode; @@ -26,30 +27,39 @@ import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreePath; import java.awt.*; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; /** * @author Enaium */ -public class ResultPanel extends JPanel { - - private final JList list; - - public ResultPanel() { - super(new BorderLayout()); - list = new JList<>(new DefaultListModel<>()); - add(new JScrollPane(list), BorderLayout.CENTER); +public class ResultList extends JList { + public ResultList() { + super(new DefaultListModel<>()); + //avoid repeat updateFixedCellSize + setPrototypeCellValue(new ResultNode()); + //fix auto wrap + setCellRenderer((list, value, index, isSelected, cellHasFocus) -> new JPanel(new BorderLayout()) {{ + if (isSelected) { + setBackground(list.getSelectionBackground()); + } else { + setBackground(list.getBackground()); + } + JLabel comp = new JLabel(value.toString()); + add(comp, BorderLayout.CENTER); + }}); JPopupMenu jPopupMenu = new JPopupMenu(); JMenuItem jMenuItem = new JMenuItem("Jump to Node"); jMenuItem.addActionListener(e -> { - if (list.getSelectedValue() != null) { + if (getSelectedValue() != null) { ASyncUtil.execute(() -> { SwingUtilities.invokeLater(() -> { FileTreePanel fileTreePanel = JavaOctetEditor.getInstance().fileTreePanel; DefaultTreeModel model = (DefaultTreeModel) fileTreePanel.getModel(); - if (selectEntry(fileTreePanel, list.getSelectedValue().getClassNode(), model, FileTreePanel.classesRoot)) { + if (selectEntry(fileTreePanel, getSelectedValue().getClassNode(), model, FileTreePanel.classesRoot)) { fileTreePanel.repaint(); } }); @@ -57,13 +67,13 @@ public ResultPanel() { } }); jPopupMenu.add(jMenuItem); - - list.addMouseListener(new MouseAdapter() { + ResultList resultList = this; + addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { if (SwingUtilities.isRightMouseButton(e)) { - if (list.getSelectedValue() != null) { - jPopupMenu.show(list, e.getX(), e.getY()); + if (getSelectedValue() != null) { + jPopupMenu.show(resultList, e.getX(), e.getY()); } } } @@ -93,8 +103,4 @@ public boolean selectEntry(JTree jTree, ClassNode classNode, DefaultTreeModel de } return false; } - - public JList getList() { - return list; - } } diff --git a/src/main/java/cn/enaium/joe/gui/panel/BottomPanel.java b/src/main/java/cn/enaium/joe/gui/panel/BottomPanel.java index 9555150..840545a 100644 --- a/src/main/java/cn/enaium/joe/gui/panel/BottomPanel.java +++ b/src/main/java/cn/enaium/joe/gui/panel/BottomPanel.java @@ -16,34 +16,67 @@ package cn.enaium.joe.gui.panel; +import cn.enaium.joe.JavaOctetEditor; +import cn.enaium.joe.annotation.Indeterminate; +import cn.enaium.joe.task.AbstractTask; +import cn.enaium.joe.task.DecompileTask; +import org.benf.cfr.reader.bytecode.analysis.parse.utils.Pair; + import javax.swing.*; import javax.swing.border.EmptyBorder; import java.awt.*; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; /** * @author Enaium */ public class BottomPanel extends JPanel { - private final JProgressBar jProgressBar = new JProgressBar(); - private final JLabel scale = new JLabel(); public BottomPanel() { super(new GridLayout(1, 4, 10, 10)); this.setBorder(new EmptyBorder(5, 5, 5, 5)); + + JProgressBar jProgressBar = new JProgressBar(); + add(jProgressBar); + ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1); + scheduledExecutorService.scheduleAtFixedRate(() -> { + List, CompletableFuture>> task = JavaOctetEditor.getInstance().task.getTask(); + if (task.isEmpty()) { + SwingUtilities.invokeLater(() -> { + jProgressBar.setValue(0); + jProgressBar.setStringPainted(true); + jProgressBar.setIndeterminate(false); + jProgressBar.setString(""); + jProgressBar.repaint(); + }); + } else { + Pair, CompletableFuture> classCompletableFuturePair = task.get(task.size() - 1); + SwingUtilities.invokeLater(() -> { + int progress = classCompletableFuturePair.getFirst().getProgress(); + if (!classCompletableFuturePair.getFirst().getClass().isAnnotationPresent(Indeterminate.class)) { + jProgressBar.setValue(progress); + jProgressBar.setStringPainted(true); + jProgressBar.setIndeterminate(false); + jProgressBar.setString(String.format("%s:%s", classCompletableFuturePair.getFirst().getName(), progress) + "%"); + } else { + jProgressBar.setString(classCompletableFuturePair.getFirst().getName()); + jProgressBar.setIndeterminate(true); + } + jProgressBar.repaint(); + }); + } + }, 1, 1, TimeUnit.MILLISECONDS); - add(scale); JLabel label = new JLabel("\u00A9 Enaium 2022"); label.setHorizontalAlignment(SwingConstants.RIGHT); add(label); } - - public void setProcess(int n) { - SwingUtilities.invokeLater(() -> { - jProgressBar.setValue(n); - scale.setText(n == 0 ? "" : n + "%"); - jProgressBar.repaint(); - }); - } } diff --git a/src/main/java/cn/enaium/joe/gui/panel/file/tabbed/tab/classes/ASMifierTablePanel.java b/src/main/java/cn/enaium/joe/gui/panel/file/tabbed/tab/classes/ASMifierTablePanel.java index eb25582..16753dd 100644 --- a/src/main/java/cn/enaium/joe/gui/panel/file/tabbed/tab/classes/ASMifierTablePanel.java +++ b/src/main/java/cn/enaium/joe/gui/panel/file/tabbed/tab/classes/ASMifierTablePanel.java @@ -100,6 +100,7 @@ public void keyReleased(KeyEvent e) { codeAreaPanel.getTextArea().setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA); codeAreaPanel.getTextArea().setEditable(true); StringWriter stringWriter = new StringWriter(); + ASyncUtil.execute(() -> { classNode.accept(new TraceClassVisitor(null, new ASMifier(), new PrintWriter(stringWriter))); }, () -> { diff --git a/src/main/java/cn/enaium/joe/gui/panel/file/tabbed/tab/classes/DecompileTabPanel.java b/src/main/java/cn/enaium/joe/gui/panel/file/tabbed/tab/classes/DecompileTabPanel.java index 31aa6bc..fa97d17 100644 --- a/src/main/java/cn/enaium/joe/gui/panel/file/tabbed/tab/classes/DecompileTabPanel.java +++ b/src/main/java/cn/enaium/joe/gui/panel/file/tabbed/tab/classes/DecompileTabPanel.java @@ -16,8 +16,10 @@ package cn.enaium.joe.gui.panel.file.tabbed.tab.classes; +import cn.enaium.joe.JavaOctetEditor; import cn.enaium.joe.service.DecompileService; import cn.enaium.joe.gui.panel.CodeAreaPanel; +import cn.enaium.joe.task.DecompileTask; import cn.enaium.joe.util.ASyncUtil; import org.objectweb.asm.tree.ClassNode; @@ -32,8 +34,8 @@ public DecompileTabPanel(ClassNode classNode) { setLayout(new BorderLayout()); CodeAreaPanel codeAreaPanel = new CodeAreaPanel(); codeAreaPanel.getTextArea().setSyntaxEditingStyle("text/java"); - ASyncUtil.execute(() -> { - codeAreaPanel.getTextArea().setText(DecompileService.getService().decompile(classNode)); + JavaOctetEditor.getInstance().task.submit(new DecompileTask(classNode)).thenAccept(it -> { + codeAreaPanel.getTextArea().setText(it); }); codeAreaPanel.getTextArea().setCaretPosition(0); add(codeAreaPanel); diff --git a/src/main/java/cn/enaium/joe/gui/panel/file/tabbed/tab/resources/HexTablePanel.java b/src/main/java/cn/enaium/joe/gui/panel/file/tabbed/tab/resources/HexTablePanel.java index be5bbff..5450131 100644 --- a/src/main/java/cn/enaium/joe/gui/panel/file/tabbed/tab/resources/HexTablePanel.java +++ b/src/main/java/cn/enaium/joe/gui/panel/file/tabbed/tab/resources/HexTablePanel.java @@ -17,6 +17,7 @@ package cn.enaium.joe.gui.panel.file.tabbed.tab.resources; import cn.enaium.joe.gui.panel.file.tree.node.FileTreeNode; +import cn.enaium.joe.util.ASyncUtil; import javax.swing.*; import javax.swing.table.DefaultTableModel; @@ -41,19 +42,21 @@ public boolean isCellEditable(int row, int column) { jTable.getTableHeader().setReorderingAllowed(false); add(new JScrollPane(jTable), BorderLayout.CENTER); - int row = 0; - Object[] array = new Object[16]; - byte[] data = fileTreeNode.getData(); - for (int i = 0; i < data.length; i++) { - byte b = data[i]; - array[row] = String.format("%02X", b); - if (row == 15 || i == fileTreeNode.getData().length - 1) { - defaultTableModel.addRow(array); - row = 0; - array = new Object[16]; - continue; + ASyncUtil.execute(() -> { + int row = 0; + Object[] array = new Object[16]; + byte[] data = fileTreeNode.getData(); + for (int i = 0; i < data.length; i++) { + byte b = data[i]; + array[row] = String.format("%02X", b); + if (row == 15 || i == fileTreeNode.getData().length - 1) { + defaultTableModel.addRow(array); + row = 0; + array = new Object[16]; + continue; + } + row++; } - row++; - } + }); } } diff --git a/src/main/java/cn/enaium/joe/gui/panel/file/tree/FileTreePanel.java b/src/main/java/cn/enaium/joe/gui/panel/file/tree/FileTreePanel.java index 2d78201..7f5a34f 100644 --- a/src/main/java/cn/enaium/joe/gui/panel/file/tree/FileTreePanel.java +++ b/src/main/java/cn/enaium/joe/gui/panel/file/tree/FileTreePanel.java @@ -20,12 +20,11 @@ import cn.enaium.joe.gui.panel.file.FileDropTarget; import cn.enaium.joe.gui.panel.file.tabbed.tab.classes.ClassTabPanel; import cn.enaium.joe.gui.panel.file.tabbed.tab.classes.FieldInfoPanel; -import cn.enaium.joe.gui.panel.file.tabbed.tab.classes.method.MethodInfoTabPanel; import cn.enaium.joe.gui.panel.file.tabbed.tab.classes.method.MethodTabPanel; import cn.enaium.joe.gui.panel.file.tabbed.tab.resources.HexTablePanel; import cn.enaium.joe.gui.panel.file.tree.node.*; import cn.enaium.joe.jar.Jar; -import cn.enaium.joe.util.ASyncUtil; +import cn.enaium.joe.task.InputJarTask; import cn.enaium.joe.util.JTreeUtil; import cn.enaium.joe.util.LangUtil; import org.objectweb.asm.tree.ClassNode; @@ -39,7 +38,6 @@ import java.awt.dnd.DropTarget; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; -import java.io.File; import java.util.*; /** @@ -90,12 +88,7 @@ public FileTreePanel() { new DropTarget(this, DnDConstants.ACTION_COPY_OR_MOVE, new FileDropTarget(".jar", files -> { if (!files.isEmpty()) { - File file = files.get(0); - ASyncUtil.execute(() -> { - Jar jar = new Jar(); - jar.load(file); - refresh(jar); - }); + JavaOctetEditor.getInstance().task.submit(new InputJarTask(files.get(0))).thenAccept(this::refresh); } }), true); diff --git a/src/main/java/cn/enaium/joe/gui/panel/instruction/LdcInstructionPanel.java b/src/main/java/cn/enaium/joe/gui/panel/instruction/LdcInstructionPanel.java index bfd1e69..90bc430 100644 --- a/src/main/java/cn/enaium/joe/gui/panel/instruction/LdcInstructionPanel.java +++ b/src/main/java/cn/enaium/joe/gui/panel/instruction/LdcInstructionPanel.java @@ -126,7 +126,6 @@ public void intervalRemoved(ListDataEvent e) { @Override public void contentsChanged(ListDataEvent e) { - System.out.println("Change"); handleLabel.setVisible(Objects.equals(jComboBox.getSelectedItem(), "Handle")); handleButton.setVisible(Objects.equals(jComboBox.getSelectedItem(), "Handle")); verLabel.setVisible(!Objects.equals(jComboBox.getSelectedItem(), "Handle")); diff --git a/src/main/java/cn/enaium/joe/gui/panel/menu/ConfigMenu.java b/src/main/java/cn/enaium/joe/gui/panel/menu/ConfigMenu.java index 432df90..1ca86e8 100644 --- a/src/main/java/cn/enaium/joe/gui/panel/menu/ConfigMenu.java +++ b/src/main/java/cn/enaium/joe/gui/panel/menu/ConfigMenu.java @@ -30,7 +30,7 @@ public class ConfigMenu extends JMenu { public ConfigMenu() { super(LangUtil.i18n("menu.config")); - for (Config value : JavaOctetEditor.getInstance().configManager.getConfig().values()) { + for (Config value : JavaOctetEditor.getInstance().config.getConfig().values()) { add(new JMenuItem(value.getName()) {{ addActionListener(e -> { new ConfigDialog(value).setVisible(true); diff --git a/src/main/java/cn/enaium/joe/gui/panel/menu/file/LoadMenuItem.java b/src/main/java/cn/enaium/joe/gui/panel/menu/file/LoadMenuItem.java index 105ec4c..3f90ec9 100644 --- a/src/main/java/cn/enaium/joe/gui/panel/menu/file/LoadMenuItem.java +++ b/src/main/java/cn/enaium/joe/gui/panel/menu/file/LoadMenuItem.java @@ -17,9 +17,7 @@ package cn.enaium.joe.gui.panel.menu.file; import cn.enaium.joe.JavaOctetEditor; -import cn.enaium.joe.config.extend.ApplicationConfig; -import cn.enaium.joe.jar.Jar; -import cn.enaium.joe.util.ASyncUtil; +import cn.enaium.joe.task.InputJarTask; import cn.enaium.joe.util.JFileChooserUtil; import cn.enaium.joe.util.LangUtil; @@ -35,11 +33,7 @@ public LoadMenuItem() { addActionListener(e -> { File show = JFileChooserUtil.show(JFileChooserUtil.Type.OPEN); if (show != null) { - ASyncUtil.execute(() -> { - Jar jar = new Jar(); - jar.load(show); - JavaOctetEditor.getInstance().fileTreePanel.refresh(jar); - }); + JavaOctetEditor.getInstance().task.submit(new InputJarTask(show)).thenAccept(it -> JavaOctetEditor.getInstance().fileTreePanel.refresh(it)); } }); } diff --git a/src/main/java/cn/enaium/joe/gui/panel/menu/file/LoadRecentMenu.java b/src/main/java/cn/enaium/joe/gui/panel/menu/file/LoadRecentMenu.java index 7588359..7525134 100644 --- a/src/main/java/cn/enaium/joe/gui/panel/menu/file/LoadRecentMenu.java +++ b/src/main/java/cn/enaium/joe/gui/panel/menu/file/LoadRecentMenu.java @@ -18,8 +18,7 @@ import cn.enaium.joe.JavaOctetEditor; import cn.enaium.joe.config.extend.ApplicationConfig; -import cn.enaium.joe.jar.Jar; -import cn.enaium.joe.util.ASyncUtil; +import cn.enaium.joe.task.InputJarTask; import cn.enaium.joe.util.LangUtil; import javax.swing.*; @@ -39,18 +38,13 @@ public LoadRecentMenu() { @Override public void mouseReleased(MouseEvent e) { JPopupMenu jPopupMenu = new JPopupMenu(); - Set loadRecent = JavaOctetEditor.getInstance().configManager.getByClass(ApplicationConfig.class).loadRecent.getValue(); + Set loadRecent = JavaOctetEditor.getInstance().config.getByClass(ApplicationConfig.class).loadRecent.getValue(); for (String s : loadRecent) { jPopupMenu.add(new JMenuItem(s) {{ addActionListener(e -> { - System.out.println(s); File file = new File(s); if (file.exists()) { - ASyncUtil.execute(() -> { - Jar jar = new Jar(); - jar.load(file); - JavaOctetEditor.getInstance().fileTreePanel.refresh(jar); - }); + JavaOctetEditor.getInstance().task.submit(new InputJarTask(file)).thenAccept(it -> JavaOctetEditor.getInstance().fileTreePanel.refresh(it)); } else { loadRecent.remove(s); } diff --git a/src/main/java/cn/enaium/joe/gui/panel/menu/file/SaveAllSourceMenuItem.java b/src/main/java/cn/enaium/joe/gui/panel/menu/file/SaveAllSourceMenuItem.java index 53f5cb3..7efcb69 100644 --- a/src/main/java/cn/enaium/joe/gui/panel/menu/file/SaveAllSourceMenuItem.java +++ b/src/main/java/cn/enaium/joe/gui/panel/menu/file/SaveAllSourceMenuItem.java @@ -19,6 +19,8 @@ import cn.enaium.joe.JavaOctetEditor; import cn.enaium.joe.service.decompiler.ProcyonDecompiler; import cn.enaium.joe.jar.Jar; +import cn.enaium.joe.task.OutputJarTask; +import cn.enaium.joe.task.SaveAllSourceTask; import cn.enaium.joe.util.ASyncUtil; import cn.enaium.joe.util.JFileChooserUtil; import cn.enaium.joe.util.LangUtil; @@ -48,35 +50,7 @@ public SaveAllSourceMenuItem() { File show = JFileChooserUtil.show(JFileChooserUtil.Type.SAVE); if (show != null) { - ASyncUtil.execute(() -> { - float loaded = 0; - float files = jar.classes.size() + jar.resources.size(); - try { - ZipOutputStream zipOutputStream = new ZipOutputStream(Files.newOutputStream(show.toPath())); - - for (Map.Entry stringClassNodeEntry : jar.classes.entrySet()) { - String name = stringClassNodeEntry.getKey().substring(0, stringClassNodeEntry.getKey().lastIndexOf(".")) + ".java"; - name = "src/main/java/" + name; - zipOutputStream.putNextEntry(new ZipEntry(name)); - zipOutputStream.write(new ProcyonDecompiler().decompile(stringClassNodeEntry.getValue()).getBytes(StandardCharsets.UTF_8)); - JavaOctetEditor.getInstance().bottomPanel.setProcess((int) ((loaded++ / files) * 100f)); - } - - for (Map.Entry stringEntry : jar.resources.entrySet()) { - String name = stringEntry.getKey(); - name = "src/main/resources/" + name; - zipOutputStream.putNextEntry(new JarEntry(name)); - zipOutputStream.write(stringEntry.getValue()); - JavaOctetEditor.getInstance().bottomPanel.setProcess((int) ((loaded++ / files) * 100f)); - } - zipOutputStream.closeEntry(); - zipOutputStream.close(); - JavaOctetEditor.getInstance().bottomPanel.setProcess(0); - } catch (IOException ex) { - throw new RuntimeException(ex); - } - }); - + JavaOctetEditor.getInstance().task.submit(new SaveAllSourceTask(jar, show)); } }); } diff --git a/src/main/java/cn/enaium/joe/gui/panel/menu/file/SaveMenuItem.java b/src/main/java/cn/enaium/joe/gui/panel/menu/file/SaveMenuItem.java index 9d95d0c..180ce87 100644 --- a/src/main/java/cn/enaium/joe/gui/panel/menu/file/SaveMenuItem.java +++ b/src/main/java/cn/enaium/joe/gui/panel/menu/file/SaveMenuItem.java @@ -18,6 +18,7 @@ import cn.enaium.joe.JavaOctetEditor; import cn.enaium.joe.jar.Jar; +import cn.enaium.joe.task.OutputJarTask; import cn.enaium.joe.util.ASyncUtil; import cn.enaium.joe.util.JFileChooserUtil; import cn.enaium.joe.util.LangUtil; @@ -45,32 +46,7 @@ public SaveMenuItem() { } File show = JFileChooserUtil.show(JFileChooserUtil.Type.SAVE); if (show != null) { - ASyncUtil.execute(() -> { - float loaded = 0; - float files = jar.classes.size() + jar.resources.size(); - - try { - ZipOutputStream zipOutputStream = new ZipOutputStream(Files.newOutputStream(show.toPath())); - for (Map.Entry stringClassNodeEntry : jar.classes.entrySet()) { - zipOutputStream.putNextEntry(new JarEntry(stringClassNodeEntry.getValue().name + ".class")); - ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS); - stringClassNodeEntry.getValue().accept(classWriter); - zipOutputStream.write(classWriter.toByteArray()); - JavaOctetEditor.getInstance().bottomPanel.setProcess((int) ((loaded++ / files) * 100f)); - } - - for (Map.Entry stringEntry : jar.resources.entrySet()) { - zipOutputStream.putNextEntry(new JarEntry(stringEntry.getKey())); - zipOutputStream.write(stringEntry.getValue()); - JavaOctetEditor.getInstance().bottomPanel.setProcess((int) ((loaded++ / files) * 100f)); - } - zipOutputStream.closeEntry(); - zipOutputStream.close(); - JavaOctetEditor.getInstance().bottomPanel.setProcess(0); - } catch (IOException ex) { - throw new RuntimeException(ex); - } - }); + JavaOctetEditor.getInstance().task.submit(new OutputJarTask(jar, show)); } }); } diff --git a/src/main/java/cn/enaium/joe/gui/panel/search/ResultNode.java b/src/main/java/cn/enaium/joe/gui/panel/search/ResultNode.java index c561f2f..22123ae 100644 --- a/src/main/java/cn/enaium/joe/gui/panel/search/ResultNode.java +++ b/src/main/java/cn/enaium/joe/gui/panel/search/ResultNode.java @@ -16,6 +16,7 @@ package cn.enaium.joe.gui.panel.search; +import cn.enaium.joe.util.ColorUtil; import cn.enaium.joe.util.HtmlUtil; import org.objectweb.asm.tree.ClassNode; @@ -25,24 +26,25 @@ * @author Enaium */ public class ResultNode { - private final ClassNode classNode; - private final String result; + private ClassNode classNode; + + private final String text; + + public ResultNode() { + text = " "; + } public ResultNode(ClassNode classNode, String result) { this.classNode = classNode; - this.result = result; + text = HtmlUtil.toHtml(HtmlUtil.setColor(classNode.name, Color.WHITE) + "#" + result); } public ClassNode getClassNode() { return classNode; } - public String getResult() { - return result; - } - @Override public String toString() { - return HtmlUtil.toHtml(HtmlUtil.setColor(classNode.name, new Color(255, 255, 255)) + "#" + HtmlUtil.setColor(result, new Color(151, 194, 120))); + return text; } } diff --git a/src/main/java/cn/enaium/joe/jar/Jar.java b/src/main/java/cn/enaium/joe/jar/Jar.java index 43f9f68..88e9503 100644 --- a/src/main/java/cn/enaium/joe/jar/Jar.java +++ b/src/main/java/cn/enaium/joe/jar/Jar.java @@ -39,50 +39,6 @@ public class Jar { public Map classes = new LinkedHashMap<>(); public Map resources = new LinkedHashMap<>(); - public void load(File file) { - JavaOctetEditor.getInstance().configManager.getByClass(ApplicationConfig.class).loadRecent.getValue().add(file.getAbsolutePath()); - JavaOctetEditor.getInstance().jar = this; - JavaOctetEditor.getInstance().window.setTitle(JavaOctetEditor.TITLE + "-" + file.getName()); - try { - JarFile jarFile = new JarFile(file); - float loaded = 0; - float files = Util.countFiles(jarFile); - - - Enumeration entries = jarFile.entries(); - while (entries.hasMoreElements()) { - JarEntry jarEntry = entries.nextElement(); - if (jarEntry.getName().endsWith(".class")) { - ClassReader classReader = new ClassReader(jarFile.getInputStream(new JarEntry(jarEntry.getName()))); - ClassNode classNode = new ClassNode(); - try { - classReader.accept(classNode, ClassReader.EXPAND_FRAMES); - } catch (Throwable throwable) { - classReader.accept(classNode, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); - } - classes.put(jarEntry.getName(), classNode); - } else if (!jarEntry.isDirectory()) { - ByteArrayOutputStream output = new ByteArrayOutputStream(); - byte[] buffer = new byte[1024 * 4]; - int n; - InputStream inputStream = jarFile.getInputStream(new JarEntry(jarEntry.getName())); - while (-1 != (n = inputStream.read(buffer))) { - output.write(buffer, 0, n); - } - resources.put(jarEntry.getName(), output.toByteArray()); - } - - JavaOctetEditor.getInstance().bottomPanel.setProcess((int) ((loaded++ / files) * 100f)); - } - jarFile.close(); - JavaOctetEditor.getInstance().bottomPanel.setProcess(0); - } catch (IOException e) { - throw new RuntimeException(e); - } - - JavaOctetEditor.getInstance().fileTreePanel.refresh(this); - } - public Jar copy() { Jar jar = new Jar(); jar.classes.putAll(classes); diff --git a/src/main/java/cn/enaium/joe/service/DecompileService.java b/src/main/java/cn/enaium/joe/service/DecompileService.java index 5b28057..08e6c3e 100644 --- a/src/main/java/cn/enaium/joe/service/DecompileService.java +++ b/src/main/java/cn/enaium/joe/service/DecompileService.java @@ -30,7 +30,7 @@ */ public class DecompileService { public static IDecompiler getService() { - ModeValue decompilerMode = JavaOctetEditor.getInstance().configManager.getByClass(ApplicationConfig.class).decompilerMode; + ModeValue decompilerMode = JavaOctetEditor.getInstance().config.getByClass(ApplicationConfig.class).decompilerMode; switch (decompilerMode.getValue()) { case "CFR": return new CFRDecompiler(); case "Procyon": return new ProcyonDecompiler(); diff --git a/src/main/java/cn/enaium/joe/service/decompiler/CFRDecompiler.java b/src/main/java/cn/enaium/joe/service/decompiler/CFRDecompiler.java index 76eb5b4..03c8269 100644 --- a/src/main/java/cn/enaium/joe/service/decompiler/CFRDecompiler.java +++ b/src/main/java/cn/enaium/joe/service/decompiler/CFRDecompiler.java @@ -26,7 +26,6 @@ import java.io.IOException; import java.util.Collection; -import java.util.HashMap; /** * @author Enaium @@ -61,6 +60,6 @@ public Collection addJar(String arg0) { throw new RuntimeException(); } }; - return new PluginRunner(JavaOctetEditor.getInstance().configManager.getConfigMap(CFRConfig.class), cfs).getDecompilationFor(classNode.name); + return new PluginRunner(JavaOctetEditor.getInstance().config.getConfigMap(CFRConfig.class), cfs).getDecompilationFor(classNode.name); } } diff --git a/src/main/java/cn/enaium/joe/task/AbstractTask.java b/src/main/java/cn/enaium/joe/task/AbstractTask.java new file mode 100644 index 0000000..601a5e5 --- /dev/null +++ b/src/main/java/cn/enaium/joe/task/AbstractTask.java @@ -0,0 +1,46 @@ +/* + * Copyright 2022 Enaium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.enaium.joe.task; + +import java.util.concurrent.Callable; +import java.util.function.Supplier; + +/** + * @author Enaium + * @since 0.10.0 + */ +public abstract class AbstractTask implements Supplier { + private final String name; + + private int progress; + + public AbstractTask(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public int getProgress() { + return progress; + } + + public void setProgress(int progress) { + this.progress = progress; + } +} diff --git a/src/main/java/cn/enaium/joe/task/DecompileTask.java b/src/main/java/cn/enaium/joe/task/DecompileTask.java new file mode 100644 index 0000000..4fcf970 --- /dev/null +++ b/src/main/java/cn/enaium/joe/task/DecompileTask.java @@ -0,0 +1,43 @@ +/* + * Copyright 2022 Enaium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.enaium.joe.task; + +import cn.enaium.joe.annotation.Repeatable; +import cn.enaium.joe.service.DecompileService; +import org.objectweb.asm.tree.ClassNode; +import org.tinylog.Logger; + +/** + * @author Enaium + * @since 0.10.0 + */ +@Repeatable +public class DecompileTask extends AbstractTask { + + private final ClassNode classNode; + + public DecompileTask(ClassNode classNode) { + super("Decompile"); + this.classNode = classNode; + } + + @Override + public String get() { + Logger.info("DECOMPILE:{}", classNode.name); + return DecompileService.getService().decompile(classNode); + } +} diff --git a/src/main/java/cn/enaium/joe/task/InputJarTask.java b/src/main/java/cn/enaium/joe/task/InputJarTask.java new file mode 100644 index 0000000..c183ddd --- /dev/null +++ b/src/main/java/cn/enaium/joe/task/InputJarTask.java @@ -0,0 +1,91 @@ +/* + * Copyright 2022 Enaium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.enaium.joe.task; + +import cn.enaium.joe.JavaOctetEditor; +import cn.enaium.joe.config.extend.ApplicationConfig; +import cn.enaium.joe.jar.Jar; +import cn.enaium.joe.util.Util; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.tree.ClassNode; +import org.tinylog.Logger; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.Enumeration; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +/** + * @author Enaium + * @since 0.10.0 + */ +public class InputJarTask extends AbstractTask { + private final File file; + + public InputJarTask(File file) { + super("InputJar"); + this.file = file; + } + + @Override + public Jar get() { + Logger.info("LOAD:{}", file.getAbsolutePath()); + Jar jar = new Jar(); + try { + JarFile jarFile = new JarFile(file); + float loaded = 0; + float files = Util.countFiles(jarFile); + + + Enumeration entries = jarFile.entries(); + while (entries.hasMoreElements()) { + JarEntry jarEntry = entries.nextElement(); + if (jarEntry.getName().endsWith(".class")) { + ClassReader classReader = new ClassReader(jarFile.getInputStream(new JarEntry(jarEntry.getName()))); + ClassNode classNode = new ClassNode(); + try { + classReader.accept(classNode, ClassReader.EXPAND_FRAMES); + } catch (Throwable throwable) { + classReader.accept(classNode, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); + } + jar.classes.put(jarEntry.getName(), classNode); + } else if (!jarEntry.isDirectory()) { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024 * 4]; + int n; + InputStream inputStream = jarFile.getInputStream(new JarEntry(jarEntry.getName())); + while (-1 != (n = inputStream.read(buffer))) { + output.write(buffer, 0, n); + } + jar.resources.put(jarEntry.getName(), output.toByteArray()); + } + setProgress((int) ((loaded++ / files) * 100f)); + } + jarFile.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + + JavaOctetEditor.getInstance().config.getByClass(ApplicationConfig.class).loadRecent.getValue().add(file.getAbsolutePath()); + JavaOctetEditor.getInstance().jar = jar; + JavaOctetEditor.getInstance().window.setTitle(JavaOctetEditor.TITLE + "-" + file.getName()); + return jar; + } +} diff --git a/src/main/java/cn/enaium/joe/task/OutputJarTask.java b/src/main/java/cn/enaium/joe/task/OutputJarTask.java new file mode 100644 index 0000000..6177624 --- /dev/null +++ b/src/main/java/cn/enaium/joe/task/OutputJarTask.java @@ -0,0 +1,74 @@ +/* + * Copyright 2022 Enaium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.enaium.joe.task; + +import cn.enaium.joe.JavaOctetEditor; +import cn.enaium.joe.jar.Jar; +import cn.enaium.joe.util.MessageUtil; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.tree.ClassNode; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.Map; +import java.util.jar.JarEntry; +import java.util.zip.ZipOutputStream; + +/** + * @author Enaium + * @since 0.10.0 + */ +public class OutputJarTask extends AbstractTask { + private final Jar jar; + private final File out; + + public OutputJarTask(Jar jar, File out) { + super("OutputJar"); + this.jar = jar; + this.out = out; + } + + @Override + public Boolean get() { + float loaded = 0; + float files = jar.classes.size() + jar.resources.size(); + + try { + ZipOutputStream zipOutputStream = new ZipOutputStream(Files.newOutputStream(out.toPath())); + for (Map.Entry stringClassNodeEntry : jar.classes.entrySet()) { + zipOutputStream.putNextEntry(new JarEntry(stringClassNodeEntry.getValue().name + ".class")); + ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS); + stringClassNodeEntry.getValue().accept(classWriter); + zipOutputStream.write(classWriter.toByteArray()); + setProgress((int) ((loaded++ / files) * 100f)); + } + + for (Map.Entry stringEntry : jar.resources.entrySet()) { + zipOutputStream.putNextEntry(new JarEntry(stringEntry.getKey())); + zipOutputStream.write(stringEntry.getValue()); + setProgress((int) ((loaded++ / files) * 100f)); + } + zipOutputStream.closeEntry(); + zipOutputStream.close(); + } catch (IOException ex) { + MessageUtil.error(ex); + return false; + } + return true; + } +} diff --git a/src/main/java/cn/enaium/joe/task/SaveAllSourceTask.java b/src/main/java/cn/enaium/joe/task/SaveAllSourceTask.java new file mode 100644 index 0000000..aeb54ae --- /dev/null +++ b/src/main/java/cn/enaium/joe/task/SaveAllSourceTask.java @@ -0,0 +1,79 @@ +/* + * Copyright 2022 Enaium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.enaium.joe.task; + +import cn.enaium.joe.JavaOctetEditor; +import cn.enaium.joe.jar.Jar; +import cn.enaium.joe.service.DecompileService; +import cn.enaium.joe.service.decompiler.ProcyonDecompiler; +import cn.enaium.joe.util.MessageUtil; +import org.objectweb.asm.tree.ClassNode; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.Map; +import java.util.jar.JarEntry; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +/** + * @author Enaium + * @since 0.10.0 + */ +public class SaveAllSourceTask extends AbstractTask { + private final Jar jar; + private final File out; + + public SaveAllSourceTask(Jar jar, File out) { + super("SaveAllSource"); + this.jar = jar; + this.out = out; + } + + @Override + public Boolean get() { + float loaded = 0; + float files = jar.classes.size() + jar.resources.size(); + try { + ZipOutputStream zipOutputStream = new ZipOutputStream(Files.newOutputStream(out.toPath())); + + for (Map.Entry stringClassNodeEntry : jar.classes.entrySet()) { + String name = stringClassNodeEntry.getKey().substring(0, stringClassNodeEntry.getKey().lastIndexOf(".")) + ".java"; + name = "src/main/java/" + name; + zipOutputStream.putNextEntry(new ZipEntry(name)); + zipOutputStream.write(DecompileService.getService().decompile(stringClassNodeEntry.getValue()).getBytes(StandardCharsets.UTF_8)); + setProgress((int) ((loaded++ / files) * 100f)); + } + + for (Map.Entry stringEntry : jar.resources.entrySet()) { + String name = stringEntry.getKey(); + name = "src/main/resources/" + name; + zipOutputStream.putNextEntry(new JarEntry(name)); + zipOutputStream.write(stringEntry.getValue()); + setProgress((int) ((loaded++ / files) * 100f)); + } + zipOutputStream.closeEntry(); + zipOutputStream.close(); + } catch (IOException ex) { + MessageUtil.error(ex); + return false; + } + return true; + } +} diff --git a/src/main/java/cn/enaium/joe/task/SearchFieldTask.java b/src/main/java/cn/enaium/joe/task/SearchFieldTask.java new file mode 100644 index 0000000..84fe2b5 --- /dev/null +++ b/src/main/java/cn/enaium/joe/task/SearchFieldTask.java @@ -0,0 +1,64 @@ +/* + * Copyright 2022 Enaium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.enaium.joe.task; + +import cn.enaium.joe.gui.panel.search.ResultNode; +import cn.enaium.joe.jar.Jar; +import cn.enaium.joe.util.ColorUtil; +import cn.enaium.joe.util.HtmlUtil; +import cn.enaium.joe.util.StringUtil; +import org.objectweb.asm.tree.FieldInsnNode; + +import java.awt.*; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Enaium + * @since 0.10.0 + */ +public class SearchFieldTask extends SearchInstructionTask> { + + private final String owner; + private final String name; + private final String description; + + + public SearchFieldTask(Jar jar, String owner, String name, String description) { + super("SearchField", jar); + this.owner = owner; + this.name = name; + this.description = description; + } + + @Override + public List get() { + List resultNodes = new ArrayList<>(); + searchInstruction((classNode, instruction) -> { + if (instruction instanceof FieldInsnNode) { + FieldInsnNode fieldInsnNode = (FieldInsnNode) instruction; + if ((fieldInsnNode.owner.contains(owner) || StringUtil.isBlank(owner)) && + (fieldInsnNode.name.contains(name) || StringUtil.isBlank(name)) && + (fieldInsnNode.desc.contains(description) || StringUtil.isBlank(description)) + ) { + resultNodes.add(new ResultNode(classNode, HtmlUtil.setColor(fieldInsnNode.name, ColorUtil.name) + HtmlUtil.setColor(":", ColorUtil.opcode) + HtmlUtil.setColor(fieldInsnNode.desc, ColorUtil.desc))); + } + } + }); + return resultNodes; + } +} diff --git a/src/main/java/cn/enaium/joe/task/SearchInstructionTask.java b/src/main/java/cn/enaium/joe/task/SearchInstructionTask.java new file mode 100644 index 0000000..2c2534a --- /dev/null +++ b/src/main/java/cn/enaium/joe/task/SearchInstructionTask.java @@ -0,0 +1,58 @@ +/* + * Copyright 2022 Enaium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.enaium.joe.task; + +import cn.enaium.joe.jar.Jar; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.MethodNode; + +import java.util.Map; +import java.util.function.BiConsumer; + +/** + * @author Enaium + * @since 0.10.0 + */ +public abstract class SearchInstructionTask extends AbstractTask { + + private final Jar jar; + + public SearchInstructionTask(String name, Jar jar) { + super(name); + this.jar = jar; + } + + public void searchInstruction(BiConsumer consumer) { + float loaded = 0; + float total = 0; + for (Map.Entry stringClassNodeEntry : jar.classes.entrySet()) { + for (MethodNode method : stringClassNodeEntry.getValue().methods) { + total += method.instructions.size(); + } + } + + for (Map.Entry stringClassNodeEntry : jar.classes.entrySet()) { + for (MethodNode method : stringClassNodeEntry.getValue().methods) { + for (AbstractInsnNode instruction : method.instructions) { + consumer.accept(stringClassNodeEntry.getValue(), instruction); + setProgress((int) ((loaded++ / total) * 100f)); + } + } + } + } +} diff --git a/src/main/java/cn/enaium/joe/task/SearchLdcTask.java b/src/main/java/cn/enaium/joe/task/SearchLdcTask.java new file mode 100644 index 0000000..5c4b1f1 --- /dev/null +++ b/src/main/java/cn/enaium/joe/task/SearchLdcTask.java @@ -0,0 +1,55 @@ +/* + * Copyright 2022 Enaium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.enaium.joe.task; + +import cn.enaium.joe.gui.panel.search.ResultNode; +import cn.enaium.joe.jar.Jar; +import cn.enaium.joe.util.ColorUtil; +import cn.enaium.joe.util.HtmlUtil; +import org.objectweb.asm.tree.LdcInsnNode; + +import javax.swing.*; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Enaium + * @since 0.10.0 + */ +public class SearchLdcTask extends SearchInstructionTask> { + + private final Object ldc; + + public SearchLdcTask(Jar jar, Object ldc) { + super("SearchLdc", jar); + this.ldc = ldc; + } + + @Override + public List get() { + List resultNodes = new ArrayList<>(); + searchInstruction((classNode, instruction) -> { + if (instruction instanceof LdcInsnNode) { + String ldc = ((LdcInsnNode) instruction).cst.toString(); + if (ldc.contains(this.ldc.toString())) { + resultNodes.add(new ResultNode(classNode, HtmlUtil.setColor(ldc, ColorUtil.string))); + } + } + }); + return resultNodes; + } +} diff --git a/src/main/java/cn/enaium/joe/task/SearchMethodTask.java b/src/main/java/cn/enaium/joe/task/SearchMethodTask.java new file mode 100644 index 0000000..77e19f3 --- /dev/null +++ b/src/main/java/cn/enaium/joe/task/SearchMethodTask.java @@ -0,0 +1,71 @@ +/* + * Copyright 2022 Enaium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.enaium.joe.task; + +import cn.enaium.joe.gui.panel.search.ResultNode; +import cn.enaium.joe.jar.Jar; +import cn.enaium.joe.util.ColorUtil; +import cn.enaium.joe.util.HtmlUtil; +import cn.enaium.joe.util.StringUtil; +import org.objectweb.asm.tree.FieldInsnNode; +import org.objectweb.asm.tree.MethodInsnNode; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Enaium + * @since 0.10.0 + */ +public class SearchMethodTask extends SearchInstructionTask> { + + private final String owner; + private final String name; + private final String description; + + private final boolean itf; + + + public SearchMethodTask(Jar jar, String owner, String name, String description, boolean itf) { + super("SearchField", jar); + this.owner = owner; + this.name = name; + this.description = description; + this.itf = itf; + } + + @Override + public List get() { + List resultNodes = new ArrayList<>(); + searchInstruction((classNode, instruction) -> { + if (instruction instanceof MethodInsnNode) { + MethodInsnNode methodInsnNode = (MethodInsnNode) instruction; + if ((methodInsnNode.owner.contains(owner) || StringUtil.isBlank(owner)) && + (methodInsnNode.name.contains(name) || StringUtil.isBlank(name)) && + (methodInsnNode.desc.contains(description) || StringUtil.isBlank(description)) && + (methodInsnNode.itf || !itf) + ) { + resultNodes.add(new ResultNode(classNode, HtmlUtil.setColor(methodInsnNode.name, ColorUtil.name) + + HtmlUtil.setColor(":", ColorUtil.opcode) + + HtmlUtil.setColor(methodInsnNode.desc, ColorUtil.desc) + + HtmlUtil.setColor(String.valueOf(methodInsnNode.itf), ColorUtil.bool))); + } + } + }); + return resultNodes; + } +} diff --git a/src/main/java/cn/enaium/joe/task/TaskManager.java b/src/main/java/cn/enaium/joe/task/TaskManager.java new file mode 100644 index 0000000..07e7ec8 --- /dev/null +++ b/src/main/java/cn/enaium/joe/task/TaskManager.java @@ -0,0 +1,62 @@ +/* + * Copyright 2022 Enaium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.enaium.joe.task; + +import cn.enaium.joe.annotation.Repeatable; +import cn.enaium.joe.util.MessageUtil; +import org.benf.cfr.reader.bytecode.analysis.parse.utils.Pair; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.logging.Logger; + +/** + * @author Enaium + * @since 0.10.0 + */ +public class TaskManager { + private final ExecutorService executorService = Executors.newCachedThreadPool(); + private final List, CompletableFuture>> task = new ArrayList<>(); + + public CompletableFuture submit(AbstractTask abstractTask) { + for (Pair, CompletableFuture> classCompletableFuturePair : task) { + if (classCompletableFuturePair.getFirst().getClass().equals(abstractTask.getClass()) && !classCompletableFuturePair.getFirst().getClass().isAnnotationPresent(Repeatable.class)) { + MessageUtil.warning("task already exists"); + throw new RuntimeException("task already exists"); + } + } + CompletableFuture completableFuture = CompletableFuture.supplyAsync(abstractTask, executorService); + completableFuture.whenComplete((t, throwable) -> { + task.removeIf(it -> it.getSecond().isDone()); + if (throwable != null) { + MessageUtil.error(throwable); + } + }); + + task.add(new Pair<>(abstractTask, completableFuture)); + return completableFuture; + } + + public List, CompletableFuture>> getTask() { + return task; + } +} diff --git a/src/main/java/cn/enaium/joe/util/ColorUtil.java b/src/main/java/cn/enaium/joe/util/ColorUtil.java new file mode 100644 index 0000000..a7cbfbc --- /dev/null +++ b/src/main/java/cn/enaium/joe/util/ColorUtil.java @@ -0,0 +1,33 @@ +/* + * Copyright 2022 Enaium + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.enaium.joe.util; + +import java.awt.*; + +/** + * @author Enaium + * @since 0.10.0 + */ +public interface ColorUtil { + Color opcode = new Color(196, 118, 215); + Color name = new Color(222, 107, 116); + Color desc = new Color(227, 191, 122); + Color base = new Color(208, 153, 102); + Color string = new Color(150, 193, 115); + Color bool = new Color(196, 118, 215); + Color other = new Color(62, 137, 236); +} diff --git a/src/main/java/cn/enaium/joe/util/LangUtil.java b/src/main/java/cn/enaium/joe/util/LangUtil.java index 24f748a..0a49cd2 100644 --- a/src/main/java/cn/enaium/joe/util/LangUtil.java +++ b/src/main/java/cn/enaium/joe/util/LangUtil.java @@ -23,12 +23,8 @@ import com.google.gson.JsonObject; import org.tinylog.Logger; -import java.io.BufferedReader; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; import java.net.URL; -import java.nio.charset.StandardCharsets; import java.util.Locale; public class LangUtil { @@ -36,7 +32,7 @@ public class LangUtil { public static String i18n(String key, Object... args) { Locale locale = Locale.getDefault(); String lang = locale.getLanguage() + "_" + locale.getCountry(); - ModeValue language = JavaOctetEditor.getInstance().configManager.getByClass(ApplicationConfig.class).language; + ModeValue language = JavaOctetEditor.getInstance().config.getByClass(ApplicationConfig.class).language; if (!language.getValue().equals("System")) { lang = language.getValue(); } diff --git a/src/main/java/cn/enaium/joe/util/MessageUtil.java b/src/main/java/cn/enaium/joe/util/MessageUtil.java index 0853acc..8f5d004 100644 --- a/src/main/java/cn/enaium/joe/util/MessageUtil.java +++ b/src/main/java/cn/enaium/joe/util/MessageUtil.java @@ -29,7 +29,7 @@ public class MessageUtil { public static void error(Throwable e) { Logger.error(e); - JOptionPane.showMessageDialog(null, e.getMessage(), LangUtil.i18n("error"), JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(null, e.toString(), LangUtil.i18n("error"), JOptionPane.ERROR_MESSAGE); } public static void confirm(Object message, String title, Runnable yes, Runnable no) { @@ -59,10 +59,12 @@ public static void confirm(Object message, String title, Runnable yes) { public static void info(String message) { - JOptionPane.showMessageDialog(null, message, LangUtil.i18n("info"), JOptionPane.INFORMATION_MESSAGE); + JOptionPane.showMessageDialog(null, message, LangUtil.i18n("info"), JOptionPane.INFORMATION_MESSAGE); + Logger.info(message); } public static void warning(String message) { - JOptionPane.showMessageDialog(null, message, LangUtil.i18n("warning"), JOptionPane.WARNING_MESSAGE); + JOptionPane.showMessageDialog(null, message, LangUtil.i18n("warning"), JOptionPane.WARNING_MESSAGE); + Logger.warn(message); } } diff --git a/src/main/java/cn/enaium/joe/wrapper/InstructionWrapper.java b/src/main/java/cn/enaium/joe/wrapper/InstructionWrapper.java index 75c4c9a..2d2ae51 100644 --- a/src/main/java/cn/enaium/joe/wrapper/InstructionWrapper.java +++ b/src/main/java/cn/enaium/joe/wrapper/InstructionWrapper.java @@ -16,6 +16,7 @@ package cn.enaium.joe.wrapper; +import cn.enaium.joe.util.ColorUtil; import cn.enaium.joe.util.HtmlUtil; import cn.enaium.joe.util.OpcodeUtil; import org.objectweb.asm.Handle; @@ -41,7 +42,7 @@ public String toString() { stringBuilder.append(""); if (getWrapper().getOpcode() != -1) { - stringBuilder.append(HtmlUtil.setColor(OpcodeUtil.OPCODE.get(getWrapper().getOpcode()), FontColor.opcode)); + stringBuilder.append(HtmlUtil.setColor(OpcodeUtil.OPCODE.get(getWrapper().getOpcode()), ColorUtil.opcode)); } switch (getWrapper().getType()) { @@ -50,29 +51,29 @@ public String toString() { break; case AbstractInsnNode.INT_INSN: IntInsnNode intInsnNode = (IntInsnNode) getWrapper(); - append(stringBuilder, HtmlUtil.setColor(String.valueOf(intInsnNode.operand), FontColor.base)); + append(stringBuilder, HtmlUtil.setColor(String.valueOf(intInsnNode.operand), ColorUtil.base)); break; case AbstractInsnNode.VAR_INSN: - append(stringBuilder, HtmlUtil.setColor(String.valueOf(((VarInsnNode) getWrapper()).var), FontColor.base)); + append(stringBuilder, HtmlUtil.setColor(String.valueOf(((VarInsnNode) getWrapper()).var), ColorUtil.base)); break; case AbstractInsnNode.TYPE_INSN: - append(stringBuilder, HtmlUtil.setColor(((TypeInsnNode) getWrapper()).desc, FontColor.desc)); + append(stringBuilder, HtmlUtil.setColor(((TypeInsnNode) getWrapper()).desc, ColorUtil.desc)); break; case AbstractInsnNode.FIELD_INSN: FieldInsnNode fieldInsnNode = (FieldInsnNode) getWrapper(); - append(stringBuilder, HtmlUtil.setColor(fieldInsnNode.name, FontColor.name)); - append(stringBuilder, HtmlUtil.setColor(fieldInsnNode.desc, FontColor.desc)); + append(stringBuilder, HtmlUtil.setColor(fieldInsnNode.name, ColorUtil.name)); + append(stringBuilder, HtmlUtil.setColor(fieldInsnNode.desc, ColorUtil.desc)); break; case AbstractInsnNode.METHOD_INSN: MethodInsnNode methodInsnNode = (MethodInsnNode) getWrapper(); - append(stringBuilder, HtmlUtil.setColor(methodInsnNode.name, FontColor.name)); - append(stringBuilder, HtmlUtil.setColor(methodInsnNode.desc, FontColor.desc)); - append(stringBuilder, HtmlUtil.setColor(String.valueOf(methodInsnNode.itf), FontColor.bool)); + append(stringBuilder, HtmlUtil.setColor(methodInsnNode.name, ColorUtil.name)); + append(stringBuilder, HtmlUtil.setColor(methodInsnNode.desc, ColorUtil.desc)); + append(stringBuilder, HtmlUtil.setColor(String.valueOf(methodInsnNode.itf), ColorUtil.bool)); break; case AbstractInsnNode.INVOKE_DYNAMIC_INSN: InvokeDynamicInsnNode invokeDynamicInsnNode = (InvokeDynamicInsnNode) getWrapper(); - append(stringBuilder, HtmlUtil.setColor(invokeDynamicInsnNode.name, FontColor.name)); - append(stringBuilder, HtmlUtil.setColor(invokeDynamicInsnNode.desc, FontColor.desc)); + append(stringBuilder, HtmlUtil.setColor(invokeDynamicInsnNode.name, ColorUtil.name)); + append(stringBuilder, HtmlUtil.setColor(invokeDynamicInsnNode.desc, ColorUtil.desc)); append(stringBuilder, "\n"); append(stringBuilder, handle(invokeDynamicInsnNode.bsm)); String[] strings = Arrays.stream(invokeDynamicInsnNode.bsmArgs).map(it -> { @@ -82,63 +83,63 @@ public String toString() { return it.toString(); } }).toArray(String[]::new); - append(stringBuilder, HtmlUtil.setColor(Arrays.toString(strings), FontColor.desc)); + append(stringBuilder, HtmlUtil.setColor(Arrays.toString(strings), ColorUtil.desc)); break; case AbstractInsnNode.JUMP_INSN: - append(stringBuilder, HtmlUtil.setColor("L" + OpcodeUtil.getLabelIndex(((JumpInsnNode) getWrapper()).label), FontColor.base)); + append(stringBuilder, HtmlUtil.setColor("L" + OpcodeUtil.getLabelIndex(((JumpInsnNode) getWrapper()).label), ColorUtil.base)); break; case AbstractInsnNode.LABEL: - append(stringBuilder, HtmlUtil.setColor("L", FontColor.opcode)); - append(stringBuilder, HtmlUtil.setColor(String.valueOf(OpcodeUtil.getLabelIndex(getWrapper())), FontColor.base)); + append(stringBuilder, HtmlUtil.setColor("L", ColorUtil.opcode)); + append(stringBuilder, HtmlUtil.setColor(String.valueOf(OpcodeUtil.getLabelIndex(getWrapper())), ColorUtil.base)); break; case AbstractInsnNode.LDC_INSN: LdcInsnNode ldcInsnNode = (LdcInsnNode) getWrapper(); if (ldcInsnNode.cst instanceof String) { - append(stringBuilder, HtmlUtil.setColor("\"" + ldcInsnNode.cst + "\"", FontColor.string)); + append(stringBuilder, HtmlUtil.setColor("\"" + ldcInsnNode.cst + "\"", ColorUtil.string)); } else if (ldcInsnNode.cst instanceof Boolean) { - append(stringBuilder, HtmlUtil.setColor(ldcInsnNode.cst.toString(), FontColor.bool)); + append(stringBuilder, HtmlUtil.setColor(ldcInsnNode.cst.toString(), ColorUtil.bool)); } else { - append(stringBuilder, HtmlUtil.setColor(ldcInsnNode.cst.toString(), FontColor.base)); + append(stringBuilder, HtmlUtil.setColor(ldcInsnNode.cst.toString(), ColorUtil.base)); } break; case AbstractInsnNode.IINC_INSN: IincInsnNode iincInsnNode = (IincInsnNode) getWrapper(); - append(stringBuilder, HtmlUtil.setColor(String.valueOf(iincInsnNode.var), FontColor.base)); - append(stringBuilder, HtmlUtil.setColor(String.valueOf(iincInsnNode.incr), FontColor.base)); + append(stringBuilder, HtmlUtil.setColor(String.valueOf(iincInsnNode.var), ColorUtil.base)); + append(stringBuilder, HtmlUtil.setColor(String.valueOf(iincInsnNode.incr), ColorUtil.base)); break; case AbstractInsnNode.TABLESWITCH_INSN: TableSwitchInsnNode tableSwitchInsnNode = (TableSwitchInsnNode) getWrapper(); - append(stringBuilder, HtmlUtil.setColor(String.format("range[%s:%s]", tableSwitchInsnNode.min, tableSwitchInsnNode.max), FontColor.desc)); - tableSwitchInsnNode.labels.stream().map(OpcodeUtil::getLabelIndex).forEach(it -> append(stringBuilder, HtmlUtil.setColor("L" + it, FontColor.base))); + append(stringBuilder, HtmlUtil.setColor(String.format("range[%s:%s]", tableSwitchInsnNode.min, tableSwitchInsnNode.max), ColorUtil.desc)); + tableSwitchInsnNode.labels.stream().map(OpcodeUtil::getLabelIndex).forEach(it -> append(stringBuilder, HtmlUtil.setColor("L" + it, ColorUtil.base))); if (tableSwitchInsnNode.dflt != null) { - append(stringBuilder, HtmlUtil.setColor("default", FontColor.other) + ":" + HtmlUtil.setColor("L" + OpcodeUtil.getLabelIndex(tableSwitchInsnNode.dflt), FontColor.base)); + append(stringBuilder, HtmlUtil.setColor("default", ColorUtil.other) + ":" + HtmlUtil.setColor("L" + OpcodeUtil.getLabelIndex(tableSwitchInsnNode.dflt), ColorUtil.base)); } break; case AbstractInsnNode.LOOKUPSWITCH_INSN: LookupSwitchInsnNode lookupSwitchInsnNode = (LookupSwitchInsnNode) getWrapper(); for (int i = 0; i < lookupSwitchInsnNode.keys.size(); i++) { - append(stringBuilder, HtmlUtil.setColor(String.valueOf(lookupSwitchInsnNode.keys.get(i)), FontColor.base) + ":" + HtmlUtil.setColor("L" + OpcodeUtil.getLabelIndex(lookupSwitchInsnNode.labels.get(i)), FontColor.base)); + append(stringBuilder, HtmlUtil.setColor(String.valueOf(lookupSwitchInsnNode.keys.get(i)), ColorUtil.base) + ":" + HtmlUtil.setColor("L" + OpcodeUtil.getLabelIndex(lookupSwitchInsnNode.labels.get(i)), ColorUtil.base)); } if (lookupSwitchInsnNode.dflt != null) { - append(stringBuilder, HtmlUtil.setColor("default", FontColor.other) + ":" + HtmlUtil.setColor("L" + OpcodeUtil.getLabelIndex(lookupSwitchInsnNode.dflt), FontColor.base)); + append(stringBuilder, HtmlUtil.setColor("default", ColorUtil.other) + ":" + HtmlUtil.setColor("L" + OpcodeUtil.getLabelIndex(lookupSwitchInsnNode.dflt), ColorUtil.base)); } break; case AbstractInsnNode.MULTIANEWARRAY_INSN: MultiANewArrayInsnNode multiANewArrayInsnNode = (MultiANewArrayInsnNode) getWrapper(); - append(stringBuilder, HtmlUtil.setColor(multiANewArrayInsnNode.desc, FontColor.desc)); - append(stringBuilder, HtmlUtil.setColor(String.valueOf(multiANewArrayInsnNode.dims), FontColor.base)); + append(stringBuilder, HtmlUtil.setColor(multiANewArrayInsnNode.desc, ColorUtil.desc)); + append(stringBuilder, HtmlUtil.setColor(String.valueOf(multiANewArrayInsnNode.dims), ColorUtil.base)); break; case AbstractInsnNode.FRAME: FrameNode frameNode = (FrameNode) getWrapper(); - append(stringBuilder, HtmlUtil.setColor(OpcodeUtil.FRAME.get(frameNode.type), FontColor.opcode)); - append(stringBuilder, HtmlUtil.setColor(String.valueOf(frameNode.local), FontColor.desc)); - append(stringBuilder, HtmlUtil.setColor(String.valueOf(frameNode.stack), FontColor.desc)); + append(stringBuilder, HtmlUtil.setColor(OpcodeUtil.FRAME.get(frameNode.type), ColorUtil.opcode)); + append(stringBuilder, HtmlUtil.setColor(String.valueOf(frameNode.local), ColorUtil.desc)); + append(stringBuilder, HtmlUtil.setColor(String.valueOf(frameNode.stack), ColorUtil.desc)); break; case AbstractInsnNode.LINE: LineNumberNode lineNumberNode = (LineNumberNode) getWrapper(); - append(stringBuilder, HtmlUtil.setColor("LINE", FontColor.opcode)); - append(stringBuilder, HtmlUtil.setColor(String.valueOf(lineNumberNode.line), FontColor.base)); - append(stringBuilder, HtmlUtil.setColor("L" + OpcodeUtil.getLabelIndex(lineNumberNode.start), FontColor.base)); + append(stringBuilder, HtmlUtil.setColor("LINE", ColorUtil.opcode)); + append(stringBuilder, HtmlUtil.setColor(String.valueOf(lineNumberNode.line), ColorUtil.base)); + append(stringBuilder, HtmlUtil.setColor("L" + OpcodeUtil.getLabelIndex(lineNumberNode.start), ColorUtil.base)); break; } @@ -151,23 +152,12 @@ private void append(StringBuilder stringBuilder, String str) { } private String handle(Handle handle) { - return HtmlUtil.setColor("handle[", FontColor.other) + - HtmlUtil.setColor(OpcodeUtil.HANDLE.get(handle.getTag()), FontColor.opcode) + ":" + - HtmlUtil.setColor(handle.getOwner(), FontColor.desc) + "." + - HtmlUtil.setColor(handle.getName(), FontColor.name) + - HtmlUtil.setColor(handle.getDesc(), FontColor.desc) + - HtmlUtil.setColor(String.valueOf(handle.isInterface()), FontColor.bool) + - HtmlUtil.setColor("]", FontColor.other); - } - - - interface FontColor { - Color opcode = new Color(196, 118, 215); - Color name = new Color(222, 107, 116); - Color desc = new Color(227, 191, 122); - Color base = new Color(208, 153, 102); - Color string = new Color(150, 193, 115); - Color bool = new Color(196, 118, 215); - Color other = new Color(62, 137, 236); + return HtmlUtil.setColor("handle[", ColorUtil.other) + + HtmlUtil.setColor(OpcodeUtil.HANDLE.get(handle.getTag()), ColorUtil.opcode) + ":" + + HtmlUtil.setColor(handle.getOwner(), ColorUtil.desc) + "." + + HtmlUtil.setColor(handle.getName(), ColorUtil.name) + + HtmlUtil.setColor(handle.getDesc(), ColorUtil.desc) + + HtmlUtil.setColor(String.valueOf(handle.isInterface()), ColorUtil.bool) + + HtmlUtil.setColor("]", ColorUtil.other); } } diff --git a/src/main/resources/lang/en_US.json b/src/main/resources/lang/en_US.json index 7b361b6..ff5dd4b 100644 --- a/src/main/resources/lang/en_US.json +++ b/src/main/resources/lang/en_US.json @@ -5,6 +5,7 @@ "search.owner": "Owner:", "search.name": "Name:", "search.description": "Description:", + "search.interface": "Interface:", "about.system": "System", "about.system.description": "Information about the OS", "about.system.name": "Name", diff --git a/src/main/resources/lang/zh_CN.json b/src/main/resources/lang/zh_CN.json index b1c9245..4ddb6eb 100644 --- a/src/main/resources/lang/zh_CN.json +++ b/src/main/resources/lang/zh_CN.json @@ -1,9 +1,11 @@ { - "search.field.title": "搜索字段", "search.ldc.title": "搜索常量", + "search.field.title": "搜索字段", + "search.method.title": "搜索方法", "search.owner": "拥有者:", "search.name": "名称:", "search.description": "描述:", + "search.interface": "接口:", "about.system": "系统", "about.system.description": "有关操作系统的信息", "about.system.name": "名字",