Code Cleanup
This commit is contained in:
parent
b0f3e5832a
commit
5581a343f5
13 changed files with 835 additions and 694 deletions
|
@ -24,7 +24,7 @@ import the.bytecode.club.bootloader.Boot;
|
||||||
import the.bytecode.club.bytecodeviewer.api.ClassNodeLoader;
|
import the.bytecode.club.bytecodeviewer.api.ClassNodeLoader;
|
||||||
import the.bytecode.club.bytecodeviewer.compilers.Compilers;
|
import the.bytecode.club.bytecodeviewer.compilers.Compilers;
|
||||||
import the.bytecode.club.bytecodeviewer.gui.ClassViewer;
|
import the.bytecode.club.bytecodeviewer.gui.ClassViewer;
|
||||||
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.ResourceListPane;
|
import the.bytecode.club.bytecodeviewer.gui.resourcelist.ResourceListPane;
|
||||||
import the.bytecode.club.bytecodeviewer.gui.MainViewerGUI;
|
import the.bytecode.club.bytecodeviewer.gui.MainViewerGUI;
|
||||||
import the.bytecode.club.bytecodeviewer.gui.extras.RunOptions;
|
import the.bytecode.club.bytecodeviewer.gui.extras.RunOptions;
|
||||||
import the.bytecode.club.bytecodeviewer.gui.SearchBoxPane;
|
import the.bytecode.club.bytecodeviewer.gui.SearchBoxPane;
|
||||||
|
@ -104,7 +104,7 @@ public class BytecodeViewer
|
||||||
public static Refactorer refactorer = new Refactorer();
|
public static Refactorer refactorer = new Refactorer();
|
||||||
public static List<FileContainer> files = new ArrayList<>(); //all of BCV's loaded files/classes/etc
|
public static List<FileContainer> files = new ArrayList<>(); //all of BCV's loaded files/classes/etc
|
||||||
public static List<Process> createdProcesses = new ArrayList<>();
|
public static List<Process> createdProcesses = new ArrayList<>();
|
||||||
public static final boolean EXPERIMENTAL_DRAG_TABS = false;
|
public static final boolean EXPERIMENTAL_TAB_CODE = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The version checker thread
|
* The version checker thread
|
||||||
|
|
|
@ -32,7 +32,7 @@ import the.bytecode.club.bytecodeviewer.gui.extras.AboutWindow;
|
||||||
import the.bytecode.club.bytecodeviewer.gui.extras.RunOptions;
|
import the.bytecode.club.bytecodeviewer.gui.extras.RunOptions;
|
||||||
import the.bytecode.club.bytecodeviewer.gui.plugins.MaliciousCodeScannerOptions;
|
import the.bytecode.club.bytecodeviewer.gui.plugins.MaliciousCodeScannerOptions;
|
||||||
import the.bytecode.club.bytecodeviewer.gui.plugins.ReplaceStringsOptions;
|
import the.bytecode.club.bytecodeviewer.gui.plugins.ReplaceStringsOptions;
|
||||||
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.ResourceListPane;
|
import the.bytecode.club.bytecodeviewer.gui.resourcelist.ResourceListPane;
|
||||||
import the.bytecode.club.bytecodeviewer.obfuscators.rename.RenameClasses;
|
import the.bytecode.club.bytecodeviewer.obfuscators.rename.RenameClasses;
|
||||||
import the.bytecode.club.bytecodeviewer.obfuscators.rename.RenameFields;
|
import the.bytecode.club.bytecodeviewer.obfuscators.rename.RenameFields;
|
||||||
import the.bytecode.club.bytecodeviewer.obfuscators.rename.RenameMethods;
|
import the.bytecode.club.bytecodeviewer.obfuscators.rename.RenameMethods;
|
||||||
|
@ -177,7 +177,7 @@ public class MainViewerGUI extends JFrame {
|
||||||
public JCheckBoxMenuItem asc = new JCheckBoxMenuItem("Allow only ASCII characters in strings");
|
public JCheckBoxMenuItem asc = new JCheckBoxMenuItem("Allow only ASCII characters in strings");
|
||||||
public JCheckBoxMenuItem ren = new JCheckBoxMenuItem("Rename ambiguous classes and class elements");
|
public JCheckBoxMenuItem ren = new JCheckBoxMenuItem("Rename ambiguous classes and class elements");
|
||||||
//Proycon
|
//Proycon
|
||||||
public final JMenu proyconSettingsSecondaryMenu = new JMenu("Procyon");
|
public final JMenu procyonSettingsSecondaryMenu = new JMenu("Procyon");
|
||||||
public final JCheckBoxMenuItem alwaysGenerateExceptionVars = new JCheckBoxMenuItem("Always Generate Exception Variable For Catch Blocks");
|
public final JCheckBoxMenuItem alwaysGenerateExceptionVars = new JCheckBoxMenuItem("Always Generate Exception Variable For Catch Blocks");
|
||||||
public final JCheckBoxMenuItem excludeNestedTypes = new JCheckBoxMenuItem("Exclude Nested Types");
|
public final JCheckBoxMenuItem excludeNestedTypes = new JCheckBoxMenuItem("Exclude Nested Types");
|
||||||
public final JCheckBoxMenuItem showDebugLineNumbers = new JCheckBoxMenuItem("Show Debug Line Numbers");
|
public final JCheckBoxMenuItem showDebugLineNumbers = new JCheckBoxMenuItem("Show Debug Line Numbers");
|
||||||
|
@ -426,21 +426,21 @@ public class MainViewerGUI extends JFrame {
|
||||||
visualSettings.add(showClassMethods);
|
visualSettings.add(showClassMethods);
|
||||||
|
|
||||||
//PROCYON SETTINGS
|
//PROCYON SETTINGS
|
||||||
settingsMainMenu.add(proyconSettingsSecondaryMenu);
|
settingsMainMenu.add(procyonSettingsSecondaryMenu);
|
||||||
proyconSettingsSecondaryMenu.add(alwaysGenerateExceptionVars);
|
procyonSettingsSecondaryMenu.add(alwaysGenerateExceptionVars);
|
||||||
proyconSettingsSecondaryMenu.add(excludeNestedTypes);
|
procyonSettingsSecondaryMenu.add(excludeNestedTypes);
|
||||||
proyconSettingsSecondaryMenu.add(showDebugLineNumbers);
|
procyonSettingsSecondaryMenu.add(showDebugLineNumbers);
|
||||||
proyconSettingsSecondaryMenu.add(includeLineNumbersInBytecode);
|
procyonSettingsSecondaryMenu.add(includeLineNumbersInBytecode);
|
||||||
proyconSettingsSecondaryMenu.add(includeErrorDiagnostics);
|
procyonSettingsSecondaryMenu.add(includeErrorDiagnostics);
|
||||||
proyconSettingsSecondaryMenu.add(showSyntheticMembers);
|
procyonSettingsSecondaryMenu.add(showSyntheticMembers);
|
||||||
proyconSettingsSecondaryMenu.add(simplifyMemberReferences);
|
procyonSettingsSecondaryMenu.add(simplifyMemberReferences);
|
||||||
proyconSettingsSecondaryMenu.add(mergeVariables);
|
procyonSettingsSecondaryMenu.add(mergeVariables);
|
||||||
proyconSettingsSecondaryMenu.add(forceExplicitTypeArguments);
|
procyonSettingsSecondaryMenu.add(forceExplicitTypeArguments);
|
||||||
proyconSettingsSecondaryMenu.add(forceExplicitImports);
|
procyonSettingsSecondaryMenu.add(forceExplicitImports);
|
||||||
proyconSettingsSecondaryMenu.add(flattenSwitchBlocks);
|
procyonSettingsSecondaryMenu.add(flattenSwitchBlocks);
|
||||||
proyconSettingsSecondaryMenu.add(retainPointlessSwitches);
|
procyonSettingsSecondaryMenu.add(retainPointlessSwitches);
|
||||||
proyconSettingsSecondaryMenu.add(retainRedunantCasts);
|
procyonSettingsSecondaryMenu.add(retainRedunantCasts);
|
||||||
proyconSettingsSecondaryMenu.add(unicodeOutputEnabled);
|
procyonSettingsSecondaryMenu.add(unicodeOutputEnabled);
|
||||||
|
|
||||||
//CFR SETTINGS
|
//CFR SETTINGS
|
||||||
settingsMainMenu.add(cfrSettingsSecondaryMenu);
|
settingsMainMenu.add(cfrSettingsSecondaryMenu);
|
||||||
|
|
|
@ -18,7 +18,7 @@ import javax.swing.tree.DefaultMutableTreeNode;
|
||||||
import javax.swing.tree.TreePath;
|
import javax.swing.tree.TreePath;
|
||||||
import org.objectweb.asm.tree.ClassNode;
|
import org.objectweb.asm.tree.ClassNode;
|
||||||
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
|
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
|
||||||
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.ResourceListPane;
|
import the.bytecode.club.bytecodeviewer.gui.resourcelist.ResourceListPane;
|
||||||
import the.bytecode.club.bytecodeviewer.searching.BackgroundSearchThread;
|
import the.bytecode.club.bytecodeviewer.searching.BackgroundSearchThread;
|
||||||
import the.bytecode.club.bytecodeviewer.searching.FieldCallSearch;
|
import the.bytecode.club.bytecodeviewer.searching.FieldCallSearch;
|
||||||
import the.bytecode.club.bytecodeviewer.searching.LDCSearch;
|
import the.bytecode.club.bytecodeviewer.searching.LDCSearch;
|
||||||
|
@ -61,13 +61,13 @@ public class SearchBoxPane extends VisibleComponent {
|
||||||
public static final SearchRadius[] SEARCH_RADII = SearchRadius.values();
|
public static final SearchRadius[] SEARCH_RADII = SearchRadius.values();
|
||||||
public static final SearchType[] SEARCH_TYPES = SearchType.values();
|
public static final SearchType[] SEARCH_TYPES = SearchType.values();
|
||||||
|
|
||||||
JCheckBox exact = new JCheckBox("Exact");
|
public final JCheckBox exact = new JCheckBox("Exact");
|
||||||
DefaultMutableTreeNode treeRoot = new DefaultMutableTreeNode("Results");
|
public final DefaultMutableTreeNode treeRoot = new DefaultMutableTreeNode("Results");
|
||||||
JTree tree;
|
public final JTree tree;
|
||||||
JComboBox typeBox;
|
public final JComboBox typeBox;
|
||||||
|
|
||||||
SearchType searchType = null;
|
public SearchType searchType = null;
|
||||||
JComboBox searchRadiusBox;
|
public final JComboBox searchRadiusBox;
|
||||||
|
|
||||||
public JButton search = new JButton("Search");
|
public JButton search = new JButton("Search");
|
||||||
BackgroundSearchThread t = new BackgroundSearchThread(true) {
|
BackgroundSearchThread t = new BackgroundSearchThread(true) {
|
||||||
|
@ -75,7 +75,6 @@ public class SearchBoxPane extends VisibleComponent {
|
||||||
public void doSearch() {
|
public void doSearch() {
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
@ -83,9 +82,7 @@ public class SearchBoxPane extends VisibleComponent {
|
||||||
super("Search");
|
super("Search");
|
||||||
|
|
||||||
final JPanel optionPanel = new JPanel(new BorderLayout());
|
final JPanel optionPanel = new JPanel(new BorderLayout());
|
||||||
|
|
||||||
final JPanel searchRadiusOpt = new JPanel(new BorderLayout());
|
final JPanel searchRadiusOpt = new JPanel(new BorderLayout());
|
||||||
|
|
||||||
final JPanel searchOpts = new JPanel(new GridLayout(2, 1));
|
final JPanel searchOpts = new JPanel(new GridLayout(2, 1));
|
||||||
|
|
||||||
searchRadiusOpt.add(new JLabel("Search from "), BorderLayout.WEST);
|
searchRadiusOpt.add(new JLabel("Search from "), BorderLayout.WEST);
|
||||||
|
@ -96,15 +93,12 @@ public class SearchBoxPane extends VisibleComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
searchRadiusBox = new JComboBox(model);
|
searchRadiusBox = new JComboBox(model);
|
||||||
|
|
||||||
searchRadiusOpt.add(searchRadiusBox, BorderLayout.CENTER);
|
searchRadiusOpt.add(searchRadiusBox, BorderLayout.CENTER);
|
||||||
|
|
||||||
searchOpts.add(searchRadiusOpt);
|
searchOpts.add(searchRadiusOpt);
|
||||||
|
|
||||||
model = new DefaultComboBoxModel();
|
model = new DefaultComboBoxModel();
|
||||||
for (final SearchType st : SEARCH_TYPES) {
|
for (final SearchType st : SEARCH_TYPES)
|
||||||
model.addElement(st);
|
model.addElement(st);
|
||||||
}
|
|
||||||
|
|
||||||
typeBox = new JComboBox(model);
|
typeBox = new JComboBox(model);
|
||||||
final JPanel searchOptPanel = new JPanel();
|
final JPanel searchOptPanel = new JPanel();
|
||||||
|
@ -160,8 +154,7 @@ public class SearchBoxPane extends VisibleComponent {
|
||||||
final ClassNode fN = Objects.requireNonNull(container).getClassNode(className);
|
final ClassNode fN = Objects.requireNonNull(container).getClassNode(className);
|
||||||
|
|
||||||
if (fN != null) {
|
if (fN != null) {
|
||||||
Objects.requireNonNull(MainViewerGUI.getComponent(ResourceListPane.class))
|
BytecodeViewer.viewer.openClassFile(container, className + ".class", fN);
|
||||||
.openClassFileToWorkSpace(container, className + ".class", fN);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -89,18 +89,6 @@ public class TabbedPane extends JPanel {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.addMouseListener(new MouseAdapter() {
|
|
||||||
@Override
|
|
||||||
public void mouseReleased(MouseEvent e) {
|
|
||||||
if (e.getButton() == MouseEvent.BUTTON2) {
|
|
||||||
final int i = pane.indexOfTabComponent(TabbedPane.this);
|
|
||||||
if (i != -1) {
|
|
||||||
pane.remove(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.add(label);
|
this.add(label);
|
||||||
// add more space between the label and the button
|
// add more space between the label and the button
|
||||||
label.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5));
|
label.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5));
|
||||||
|
@ -167,7 +155,8 @@ public class TabbedPane extends JPanel {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if(BytecodeViewer.EXPERIMENTAL_DRAG_TABS)
|
//tab dragging
|
||||||
|
if(BytecodeViewer.EXPERIMENTAL_TAB_CODE)
|
||||||
{
|
{
|
||||||
/*label.addMouseListener(new MouseListener() {
|
/*label.addMouseListener(new MouseListener() {
|
||||||
@Override public void mouseClicked(MouseEvent e) {}
|
@Override public void mouseClicked(MouseEvent e) {}
|
||||||
|
@ -197,6 +186,22 @@ public class TabbedPane extends JPanel {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//middle click close
|
||||||
|
if(BytecodeViewer.EXPERIMENTAL_TAB_CODE)
|
||||||
|
{
|
||||||
|
this.addMouseListener(new MouseAdapter() {
|
||||||
|
@Override
|
||||||
|
public void mouseReleased(MouseEvent e) {
|
||||||
|
if (e.getButton() == MouseEvent.BUTTON2) {
|
||||||
|
final int i = pane.indexOfTabComponent(TabbedPane.this);
|
||||||
|
if (i != -1) {
|
||||||
|
pane.remove(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void stopDragging(int mouseX, int mouseY) {
|
private void stopDragging(int mouseX, int mouseY) {
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
package the.bytecode.club.bytecodeviewer.gui.extras;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.font.FontRenderContext;
|
||||||
|
import java.awt.geom.Rectangle2D;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author http://stackoverflow.com/a/18450804
|
||||||
|
*/
|
||||||
|
public class StringMetricsUtil
|
||||||
|
{
|
||||||
|
Font font;
|
||||||
|
FontRenderContext context;
|
||||||
|
|
||||||
|
public StringMetricsUtil(Graphics2D g2)
|
||||||
|
{
|
||||||
|
font = g2.getFont();
|
||||||
|
context = g2.getFontRenderContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rectangle2D getBounds(String message)
|
||||||
|
{
|
||||||
|
return font.getStringBounds(message, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getWidth(String message)
|
||||||
|
{
|
||||||
|
Rectangle2D bounds = getBounds(message);
|
||||||
|
return bounds.getWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getHeight(String message)
|
||||||
|
{
|
||||||
|
Rectangle2D bounds = getBounds(message);
|
||||||
|
return bounds.getHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package the.bytecode.club.bytecodeviewer.gui.resourceviewer;
|
package the.bytecode.club.bytecodeviewer.gui.resourcelist;
|
||||||
|
|
||||||
import the.bytecode.club.bytecodeviewer.Resources;
|
import the.bytecode.club.bytecodeviewer.Resources;
|
||||||
|
|
||||||
|
@ -24,9 +24,9 @@ public class ImageRenderer extends DefaultTreeCellRenderer
|
||||||
|
|
||||||
Component ret = super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
|
Component ret = super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
|
||||||
|
|
||||||
if (value instanceof ResourceListPane.MyTreeNode)
|
if (value instanceof ResourceTreeNode)
|
||||||
{
|
{
|
||||||
ResourceListPane.MyTreeNode node = (ResourceListPane.MyTreeNode) value;
|
ResourceTreeNode node = (ResourceTreeNode) value;
|
||||||
String name = node.toString().toLowerCase();
|
String name = node.toString().toLowerCase();
|
||||||
|
|
||||||
if (name.endsWith(".jar") || name.endsWith(".war"))
|
if (name.endsWith(".jar") || name.endsWith(".war"))
|
|
@ -0,0 +1,435 @@
|
||||||
|
package the.bytecode.club.bytecodeviewer.gui.resourcelist;
|
||||||
|
|
||||||
|
//TODO fix for Java 9+
|
||||||
|
import com.sun.java.swing.plaf.windows.WindowsTreeUI;
|
||||||
|
import java.awt.BorderLayout;
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.FocusEvent;
|
||||||
|
import java.awt.event.FocusListener;
|
||||||
|
import java.awt.event.KeyAdapter;
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
import java.awt.event.KeyListener;
|
||||||
|
import java.awt.event.MouseAdapter;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Objects;
|
||||||
|
import javax.swing.AbstractAction;
|
||||||
|
import javax.swing.JButton;
|
||||||
|
import javax.swing.JCheckBox;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JPopupMenu;
|
||||||
|
import javax.swing.JScrollPane;
|
||||||
|
import javax.swing.JTextField;
|
||||||
|
import javax.swing.JTree;
|
||||||
|
import javax.swing.tree.DefaultMutableTreeNode;
|
||||||
|
import javax.swing.tree.TreeNode;
|
||||||
|
import javax.swing.tree.TreePath;
|
||||||
|
import org.objectweb.asm.tree.ClassNode;
|
||||||
|
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
|
||||||
|
import the.bytecode.club.bytecodeviewer.gui.VisibleComponent;
|
||||||
|
import the.bytecode.club.bytecodeviewer.util.FileContainer;
|
||||||
|
import the.bytecode.club.bytecodeviewer.util.FileDrop;
|
||||||
|
import the.bytecode.club.bytecodeviewer.util.LazyNameUtil;
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
* Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
|
||||||
|
* Copyright (C) 2014 Kalen 'Konloch' Kinloch - http://bytecodeviewer.com *
|
||||||
|
* *
|
||||||
|
* This program is free software: you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or *
|
||||||
|
* (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The file navigation pane.
|
||||||
|
*
|
||||||
|
* @author Konloch
|
||||||
|
* @author WaterWolf
|
||||||
|
* @author afffsdd
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class ResourceListPane extends VisibleComponent implements FileDrop.Listener
|
||||||
|
{
|
||||||
|
public final JPopupMenu rightClickMenu = new JPopupMenu();
|
||||||
|
public final JCheckBox exact = new JCheckBox("Exact");
|
||||||
|
public final JButton open = new JButton("+");
|
||||||
|
public final JButton close = new JButton("-");
|
||||||
|
public final ResourceTreeNode treeRoot = new ResourceTreeNode("Loaded Files:");
|
||||||
|
public final ResourceTree tree = new ResourceTree(treeRoot);
|
||||||
|
public final String quickSearchText = "Quick file search (no file extension)";
|
||||||
|
public final JTextField quickSearch = new JTextField(quickSearchText);
|
||||||
|
public final FileDrop fileDrop;
|
||||||
|
public boolean cancel = false;
|
||||||
|
|
||||||
|
public final KeyAdapter search = new SearchKeyAdapter(this);
|
||||||
|
|
||||||
|
private void showPopMenu(ResourceTree tree, TreePath selPath, int x, int y) {
|
||||||
|
if (selPath == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
rightClickMenu.removeAll();
|
||||||
|
|
||||||
|
rightClickMenu.add(new ResourceListRightClickRemove(this, x, y, tree));
|
||||||
|
|
||||||
|
//TODO fix for Java 9+
|
||||||
|
// You can replace this with any Icon loaded from disk,
|
||||||
|
// they could be re-used for the plus and minus symbols on the resource list too
|
||||||
|
rightClickMenu.add(new AbstractAction("Expand", WindowsTreeUI.ExpandedIcon.createExpandedIcon()) {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
TreePath selPath = ResourceListPane.this.tree.getPathForLocation(x, y);
|
||||||
|
expandAll(tree, Objects.requireNonNull(selPath), true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
rightClickMenu.add(new AbstractAction("Collapse", WindowsTreeUI.CollapsedIcon.createCollapsedIcon()) {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
TreePath selPath = ResourceListPane.this.tree.getPathForLocation(x, y);
|
||||||
|
expandAll(tree, Objects.requireNonNull(selPath), false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
rightClickMenu.show(this.tree, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
//used to remove resources from the resource list
|
||||||
|
public void removeFile(FileContainer fileContainer)
|
||||||
|
{
|
||||||
|
BytecodeViewer.files.remove(fileContainer);
|
||||||
|
LazyNameUtil.removeName(fileContainer.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResourceListPane()
|
||||||
|
{
|
||||||
|
super("ClassNavigation");
|
||||||
|
tree.setRootVisible(false);
|
||||||
|
tree.setShowsRootHandles(true);
|
||||||
|
quickSearch.setForeground(Color.gray);
|
||||||
|
setTitle("Files");
|
||||||
|
|
||||||
|
attachTreeListeners();
|
||||||
|
attachQuickSearchListeners();
|
||||||
|
|
||||||
|
getContentPane().setLayout(new BorderLayout());
|
||||||
|
getContentPane().add(new JScrollPane(tree), BorderLayout.CENTER);
|
||||||
|
|
||||||
|
JPanel exactPanel = new JPanel(new BorderLayout());
|
||||||
|
JPanel quickSearchPanel = new JPanel();
|
||||||
|
JPanel buttonPanel = new JPanel(new BorderLayout());
|
||||||
|
|
||||||
|
quickSearchPanel.setLayout(new BorderLayout());
|
||||||
|
quickSearchPanel.add(quickSearch, BorderLayout.NORTH);
|
||||||
|
exactPanel.add(exact, BorderLayout.WEST);
|
||||||
|
buttonPanel.add(open, BorderLayout.EAST);
|
||||||
|
buttonPanel.add(close, BorderLayout.WEST);
|
||||||
|
exactPanel.add(buttonPanel, BorderLayout.EAST);
|
||||||
|
quickSearchPanel.add(exactPanel, BorderLayout.SOUTH);
|
||||||
|
|
||||||
|
getContentPane().add(quickSearchPanel, BorderLayout.SOUTH);
|
||||||
|
|
||||||
|
this.setVisible(true);
|
||||||
|
fileDrop = new FileDrop(this, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void filesDropped(final File[] files)
|
||||||
|
{
|
||||||
|
if (files.length < 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
BytecodeViewer.openFiles(files, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateTree()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
treeRoot.removeAllChildren();
|
||||||
|
for (FileContainer container : BytecodeViewer.files)
|
||||||
|
{
|
||||||
|
ResourceTreeNode root = new ResourceTreeNode(container.name);
|
||||||
|
treeRoot.add(root);
|
||||||
|
|
||||||
|
ImageRenderer renderer = new ImageRenderer();
|
||||||
|
tree.setCellRenderer(renderer);
|
||||||
|
|
||||||
|
if (!container.classes.isEmpty())
|
||||||
|
{
|
||||||
|
for (ClassNode c : container.classes)
|
||||||
|
{
|
||||||
|
String name = c.name;
|
||||||
|
final String[] spl = name.split("/");
|
||||||
|
if (spl.length < 2)
|
||||||
|
{
|
||||||
|
root.add(new ResourceTreeNode(name + ".class"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ResourceTreeNode parent = root;
|
||||||
|
for (int i1 = 0; i1 < spl.length; i1++)
|
||||||
|
{
|
||||||
|
String s = spl[i1];
|
||||||
|
|
||||||
|
if (i1 == spl.length - 1)
|
||||||
|
s += ".class";
|
||||||
|
|
||||||
|
ResourceTreeNode child = null;
|
||||||
|
for (int i = 0; i < parent.getChildCount(); i++)
|
||||||
|
{
|
||||||
|
if (((ResourceTreeNode) parent.getChildAt(i)).getUserObject().equals(s))
|
||||||
|
{
|
||||||
|
child = (ResourceTreeNode) parent.getChildAt(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (child == null)
|
||||||
|
{
|
||||||
|
child = new ResourceTreeNode(s);
|
||||||
|
parent.add(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
parent = child;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!container.files.isEmpty())
|
||||||
|
{
|
||||||
|
for (final Entry<String, byte[]> entry : container.files.entrySet())
|
||||||
|
{
|
||||||
|
String name = entry.getKey();
|
||||||
|
final String[] spl = name.split("/");
|
||||||
|
if (spl.length < 2)
|
||||||
|
{
|
||||||
|
root.add(new ResourceTreeNode(name));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ResourceTreeNode parent = root;
|
||||||
|
for (final String s : spl)
|
||||||
|
{
|
||||||
|
ResourceTreeNode child = null;
|
||||||
|
for (int i = 0; i < parent.getChildCount(); i++)
|
||||||
|
{
|
||||||
|
if (((ResourceTreeNode) parent.getChildAt(i)).getUserObject().equals(s))
|
||||||
|
{
|
||||||
|
child = (ResourceTreeNode) parent.getChildAt(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (child == null)
|
||||||
|
{
|
||||||
|
child = new ResourceTreeNode(s);
|
||||||
|
parent.add(child);
|
||||||
|
}
|
||||||
|
parent = child;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
treeRoot.sort();
|
||||||
|
tree.expandPath(new TreePath(tree.getModel().getRoot()));
|
||||||
|
tree.updateUI();
|
||||||
|
} catch (java.util.ConcurrentModificationException e) {
|
||||||
|
//ignore, the last file will reset everything
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO add a setting for this
|
||||||
|
// expandAll(tree, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
private void expandAll(final JTree tree, final TreePath parent,
|
||||||
|
final boolean expand) {
|
||||||
|
// Traverse children
|
||||||
|
final TreeNode node = (TreeNode) parent.getLastPathComponent();
|
||||||
|
if (node.getChildCount() >= 0) {
|
||||||
|
for (final Enumeration e = node.children(); e.hasMoreElements(); ) {
|
||||||
|
final TreeNode n = (TreeNode) e.nextElement();
|
||||||
|
final TreePath path = parent.pathByAddingChild(n);
|
||||||
|
expandAll(tree, path, expand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expansion or collapse must be done bottom-up
|
||||||
|
if (expand) {
|
||||||
|
tree.expandPath(parent);
|
||||||
|
} else {
|
||||||
|
tree.collapsePath(parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetWorkspace()
|
||||||
|
{
|
||||||
|
treeRoot.removeAllChildren();
|
||||||
|
tree.repaint();
|
||||||
|
tree.updateUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void openPath(TreePath path)
|
||||||
|
{
|
||||||
|
if (path == null || path.getPathCount() == 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
final StringBuilder nameBuffer = new StringBuilder();
|
||||||
|
for (int i = 2; i < path.getPathCount(); i++)
|
||||||
|
{
|
||||||
|
nameBuffer.append(path.getPathComponent(i));
|
||||||
|
if (i < path.getPathCount() - 1)
|
||||||
|
nameBuffer.append("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
String cheapHax = path.getPathComponent(1).toString();
|
||||||
|
FileContainer container = null;
|
||||||
|
|
||||||
|
for (FileContainer c : BytecodeViewer.files)
|
||||||
|
{
|
||||||
|
if (c.name.equals(cheapHax))
|
||||||
|
container = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
String name = nameBuffer.toString();
|
||||||
|
if (name.endsWith(".class"))
|
||||||
|
{
|
||||||
|
final ClassNode cn = BytecodeViewer.getClassNode(Objects.requireNonNull(container),
|
||||||
|
name.substring(0, name.length() - ".class".length()));
|
||||||
|
|
||||||
|
if (cn != null)
|
||||||
|
BytecodeViewer.viewer.openClassFile(container, nameBuffer.toString(), cn);
|
||||||
|
else
|
||||||
|
BytecodeViewer.viewer.openFile(container, nameBuffer.toString(),
|
||||||
|
BytecodeViewer.getFileContents(nameBuffer.toString()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
BytecodeViewer.viewer.openFile(container, nameBuffer.toString(),
|
||||||
|
BytecodeViewer.getFileContents(nameBuffer.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void attachTreeListeners()
|
||||||
|
{
|
||||||
|
tree.addMouseListener(new MouseAdapter()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void mouseReleased(MouseEvent e)
|
||||||
|
{
|
||||||
|
if (e.isMetaDown())
|
||||||
|
{
|
||||||
|
ResourceTree tree = (ResourceTree) e.getSource();
|
||||||
|
TreePath selPath = ResourceListPane.this.tree.getPathForLocation(e.getX(), e.getY());
|
||||||
|
if (selPath == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
DefaultMutableTreeNode selectNode = (DefaultMutableTreeNode) selPath.getLastPathComponent();
|
||||||
|
Enumeration<?> enumeration = treeRoot.children();
|
||||||
|
while (enumeration.hasMoreElements())
|
||||||
|
{
|
||||||
|
DefaultMutableTreeNode node = (DefaultMutableTreeNode) enumeration.nextElement();
|
||||||
|
if (node.isNodeAncestor(selectNode))
|
||||||
|
{
|
||||||
|
//rightClickMenu.show(tree, e.getX(), e.getY());
|
||||||
|
showPopMenu(tree, selPath, e.getX(), e.getY());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.open.addActionListener(e -> {
|
||||||
|
final TreeNode root = (TreeNode) tree.getModel().getRoot();
|
||||||
|
expandAll(tree, new TreePath(root), true);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.close.addActionListener(e -> {
|
||||||
|
final TreeNode root = (TreeNode) tree.getModel().getRoot();
|
||||||
|
final TreePath path = new TreePath(root);
|
||||||
|
expandAll(tree, path, false);
|
||||||
|
tree.expandPath(path);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.tree.addMouseListener(new MouseAdapter() {
|
||||||
|
@Override
|
||||||
|
public void mousePressed(MouseEvent e) {
|
||||||
|
openPath(tree.getPathForLocation(e.getX(), e.getY()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.tree.addTreeSelectionListener(arg0 -> {
|
||||||
|
if (cancel) {
|
||||||
|
cancel = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
openPath(arg0.getPath());
|
||||||
|
});
|
||||||
|
|
||||||
|
this.tree.addKeyListener(new KeyListener()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void keyReleased(KeyEvent e) { }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void keyTyped(KeyEvent e) { }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void keyPressed(KeyEvent e) {
|
||||||
|
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
|
||||||
|
if (e.getSource() instanceof ResourceTree) {
|
||||||
|
ResourceTree tree = (ResourceTree) e.getSource();
|
||||||
|
openPath(tree.getSelectionPath());
|
||||||
|
}
|
||||||
|
} else if ((int) e.getKeyChar() != 0 && (int) e.getKeyChar() != 8 && (int) e.getKeyChar() != 127 && (int) e.getKeyChar() != 65535 && !e.isControlDown() && !e.isAltDown()) {
|
||||||
|
quickSearch.grabFocus();
|
||||||
|
quickSearch.setText("" + e.getKeyChar());
|
||||||
|
cancel = true;
|
||||||
|
} else if (e.isControlDown() && (int) e.getKeyChar() == 6) //ctrl + f
|
||||||
|
{
|
||||||
|
quickSearch.grabFocus();
|
||||||
|
} else {
|
||||||
|
cancel = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void attachQuickSearchListeners()
|
||||||
|
{
|
||||||
|
quickSearch.addKeyListener(search);
|
||||||
|
quickSearch.addFocusListener(new FocusListener() {
|
||||||
|
@Override
|
||||||
|
public void focusGained(final FocusEvent arg0) {
|
||||||
|
if (quickSearch.getText().equals(quickSearchText)) {
|
||||||
|
quickSearch.setText("");
|
||||||
|
quickSearch.setForeground(Color.black);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void focusLost(final FocusEvent arg0) {
|
||||||
|
if (quickSearch.getText().isEmpty()) {
|
||||||
|
quickSearch.setText(quickSearchText);
|
||||||
|
quickSearch.setForeground(Color.gray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
package the.bytecode.club.bytecodeviewer.gui.resourcelist;
|
||||||
|
|
||||||
|
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
|
||||||
|
import the.bytecode.club.bytecodeviewer.util.FileContainer;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.tree.DefaultMutableTreeNode;
|
||||||
|
import javax.swing.tree.TreePath;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Konloch
|
||||||
|
* @since 6/22/2021
|
||||||
|
*/
|
||||||
|
public class ResourceListRightClickRemove extends AbstractAction
|
||||||
|
{
|
||||||
|
private final ResourceListPane resourceListPane;
|
||||||
|
private final int x;
|
||||||
|
private final int y;
|
||||||
|
private final ResourceTree tree;
|
||||||
|
|
||||||
|
public ResourceListRightClickRemove(ResourceListPane resourceListPane, int x, int y, ResourceTree tree)
|
||||||
|
{
|
||||||
|
super("Remove");
|
||||||
|
this.resourceListPane = resourceListPane;
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.tree = tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e)
|
||||||
|
{
|
||||||
|
TreePath selPath = resourceListPane.tree.getPathForLocation(x, y);
|
||||||
|
DefaultMutableTreeNode selectNode = (DefaultMutableTreeNode) Objects.requireNonNull(selPath).getLastPathComponent();
|
||||||
|
Enumeration<?> enumeration = resourceListPane.treeRoot.children();
|
||||||
|
while (enumeration.hasMoreElements())
|
||||||
|
{
|
||||||
|
DefaultMutableTreeNode node = (DefaultMutableTreeNode) enumeration.nextElement();
|
||||||
|
if (node.isNodeAncestor(selectNode))
|
||||||
|
{
|
||||||
|
DefaultMutableTreeNode root = (DefaultMutableTreeNode) tree.getModel().getRoot();
|
||||||
|
root.remove(node);
|
||||||
|
|
||||||
|
for (FileContainer fileContainer : BytecodeViewer.files)
|
||||||
|
{
|
||||||
|
if (fileContainer.name.equals(selectNode.toString()))
|
||||||
|
{
|
||||||
|
resourceListPane.removeFile(fileContainer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resourceListPane.updateTree();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
package the.bytecode.club.bytecodeviewer.gui.resourcelist;
|
||||||
|
|
||||||
|
import the.bytecode.club.bytecodeviewer.gui.extras.StringMetricsUtil;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.tree.DefaultMutableTreeNode;
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Konloch
|
||||||
|
* @since 6/22/2021
|
||||||
|
*/
|
||||||
|
public class ResourceTree extends JTree
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = -2355167326094772096L;
|
||||||
|
DefaultMutableTreeNode treeRoot;
|
||||||
|
|
||||||
|
public ResourceTree(final DefaultMutableTreeNode treeRoot)
|
||||||
|
{
|
||||||
|
super(treeRoot);
|
||||||
|
this.treeRoot = treeRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringMetricsUtil m = null;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paint(final Graphics g)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
super.paint(g);
|
||||||
|
if (m == null)
|
||||||
|
{
|
||||||
|
m = new StringMetricsUtil((Graphics2D) g);
|
||||||
|
}
|
||||||
|
if (treeRoot.getChildCount() < 1)
|
||||||
|
{
|
||||||
|
g.setColor(new Color(0, 0, 0, 100));
|
||||||
|
g.fillRect(0, 0, getWidth(), getHeight());
|
||||||
|
g.setColor(Color.white);
|
||||||
|
String s = "Drag class/jar/zip/APK/DEX here";
|
||||||
|
g.drawString(s,
|
||||||
|
((int) ((getWidth() / 2) - (m.getWidth(s) / 2))),
|
||||||
|
getHeight() / 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (InternalError | NullPointerException ignored)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
package the.bytecode.club.bytecodeviewer.gui.resourcelist;
|
||||||
|
|
||||||
|
import javax.swing.tree.DefaultMutableTreeNode;
|
||||||
|
import javax.swing.tree.MutableTreeNode;
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Konloch
|
||||||
|
* @since 6/22/2021
|
||||||
|
*/
|
||||||
|
public class ResourceTreeNode extends DefaultMutableTreeNode
|
||||||
|
{
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -8817777566176729571L;
|
||||||
|
|
||||||
|
public ResourceTreeNode(final Object o)
|
||||||
|
{
|
||||||
|
super(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void insert(final MutableTreeNode newChild, final int childIndex)
|
||||||
|
{
|
||||||
|
super.insert(newChild, childIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sort()
|
||||||
|
{
|
||||||
|
recursiveSort(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private void recursiveSort(final ResourceTreeNode node)
|
||||||
|
{
|
||||||
|
node.children.sort(nodeComparator);
|
||||||
|
for (ResourceTreeNode nextNode : (Iterable<ResourceTreeNode>) node.children)
|
||||||
|
{
|
||||||
|
if (nextNode.getChildCount() > 0)
|
||||||
|
{
|
||||||
|
recursiveSort(nextNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Comparator<ResourceTreeNode> nodeComparator = new Comparator<ResourceTreeNode>()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public int compare(final ResourceTreeNode o1, final ResourceTreeNode o2)
|
||||||
|
{
|
||||||
|
// To make sure nodes with children are always on top
|
||||||
|
final int firstOffset = o1.getChildCount() > 0 ? -1000 : 0;
|
||||||
|
final int secondOffset = o2.getChildCount() > 0 ? 1000 : 0;
|
||||||
|
return o1.toString().compareToIgnoreCase(o2.toString())
|
||||||
|
+ firstOffset + secondOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object obj)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode()
|
||||||
|
{
|
||||||
|
return 7;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,129 @@
|
||||||
|
package the.bytecode.club.bytecodeviewer.gui.resourcelist;
|
||||||
|
|
||||||
|
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
|
||||||
|
|
||||||
|
import javax.swing.tree.TreeNode;
|
||||||
|
import javax.swing.tree.TreePath;
|
||||||
|
import java.awt.event.KeyAdapter;
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Konloch
|
||||||
|
* @since 6/22/2021
|
||||||
|
*/
|
||||||
|
public class SearchKeyAdapter extends KeyAdapter
|
||||||
|
{
|
||||||
|
private final ResourceListPane resourceListPane;
|
||||||
|
|
||||||
|
public SearchKeyAdapter(ResourceListPane resourceListPane) {this.resourceListPane = resourceListPane;}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void keyPressed(final KeyEvent ke)
|
||||||
|
{
|
||||||
|
//only trigger on enter
|
||||||
|
if (ke.getKeyCode() != KeyEvent.VK_ENTER)
|
||||||
|
return;
|
||||||
|
|
||||||
|
final String qt = resourceListPane.quickSearch.getText();
|
||||||
|
resourceListPane.quickSearch.setText("");
|
||||||
|
|
||||||
|
if (qt.isEmpty()) //NOPE
|
||||||
|
return;
|
||||||
|
|
||||||
|
String[] path;
|
||||||
|
int found = 0;
|
||||||
|
|
||||||
|
if (qt.contains("."))
|
||||||
|
{
|
||||||
|
path = qt.split("\\.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
path = new String[]{qt};
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceTreeNode curNode = resourceListPane.treeRoot;
|
||||||
|
if (resourceListPane.exact.isSelected())
|
||||||
|
{
|
||||||
|
pathLoop:
|
||||||
|
for (int i = 0; i < path.length; i++)
|
||||||
|
{
|
||||||
|
final String pathName = path[i];
|
||||||
|
final boolean isLast = i == path.length - 1;
|
||||||
|
|
||||||
|
for (int c = 0; c < curNode.getChildCount(); c++)
|
||||||
|
{
|
||||||
|
final ResourceTreeNode child = (ResourceTreeNode) curNode.getChildAt(c);
|
||||||
|
System.out.println(pathName + ":" + child.getUserObject());
|
||||||
|
|
||||||
|
if (child.getUserObject().equals(pathName))
|
||||||
|
{
|
||||||
|
curNode = child;
|
||||||
|
if (isLast)
|
||||||
|
{
|
||||||
|
System.out.println("Found! " + curNode);
|
||||||
|
found++;
|
||||||
|
final TreePath pathn = new TreePath(curNode.getPath());
|
||||||
|
resourceListPane.tree.setSelectionPath(pathn);
|
||||||
|
resourceListPane.tree.makeVisible(pathn);
|
||||||
|
resourceListPane.tree.scrollPathToVisible(pathn);
|
||||||
|
resourceListPane.openPath(pathn); //auto open
|
||||||
|
break pathLoop;
|
||||||
|
}
|
||||||
|
continue pathLoop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("Could not find " + pathName);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Enumeration<ResourceTreeNode> enums = curNode.depthFirstEnumeration();
|
||||||
|
while (enums != null && enums.hasMoreElements())
|
||||||
|
{
|
||||||
|
ResourceTreeNode node = enums.nextElement();
|
||||||
|
if (node.isLeaf())
|
||||||
|
{
|
||||||
|
if (((String) (node.getUserObject())).toLowerCase().contains(path[path.length - 1].toLowerCase()))
|
||||||
|
{
|
||||||
|
TreeNode[] pathArray = node.getPath();
|
||||||
|
int k = 0;
|
||||||
|
StringBuilder fullPath = new StringBuilder();
|
||||||
|
while (pathArray != null
|
||||||
|
&& k < pathArray.length)
|
||||||
|
{
|
||||||
|
ResourceTreeNode n = (ResourceTreeNode) pathArray[k];
|
||||||
|
String s = (String) (n.getUserObject());
|
||||||
|
fullPath.append(s);
|
||||||
|
if (k++ != pathArray.length - 1)
|
||||||
|
{
|
||||||
|
fullPath.append(".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String fullPathString = fullPath.toString();
|
||||||
|
if (fullPathString.toLowerCase().contains(qt.toLowerCase()))
|
||||||
|
{
|
||||||
|
System.out.println("Found! " + node);
|
||||||
|
found++;
|
||||||
|
if (found >= 30)
|
||||||
|
{ //TODO probably make this a setting, no real reason it's 30
|
||||||
|
BytecodeViewer.showMessage("Uh oh, there could be more results but you've"
|
||||||
|
+ " triggered the 30 classes at once limit. Try refining your search.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final TreePath pathn = new TreePath(node.getPath());
|
||||||
|
resourceListPane.tree.setSelectionPath(pathn.getParentPath());
|
||||||
|
resourceListPane.tree.setSelectionPath(pathn);
|
||||||
|
resourceListPane.tree.makeVisible(pathn);
|
||||||
|
resourceListPane.tree.scrollPathToVisible(pathn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,641 +0,0 @@
|
||||||
package the.bytecode.club.bytecodeviewer.gui.resourceviewer;
|
|
||||||
|
|
||||||
//TODO fix for Java 9+
|
|
||||||
import com.sun.java.swing.plaf.windows.WindowsTreeUI;
|
|
||||||
import java.awt.BorderLayout;
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.awt.Font;
|
|
||||||
import java.awt.Graphics;
|
|
||||||
import java.awt.Graphics2D;
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.FocusEvent;
|
|
||||||
import java.awt.event.FocusListener;
|
|
||||||
import java.awt.event.KeyAdapter;
|
|
||||||
import java.awt.event.KeyEvent;
|
|
||||||
import java.awt.event.KeyListener;
|
|
||||||
import java.awt.event.MouseAdapter;
|
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
import java.awt.font.FontRenderContext;
|
|
||||||
import java.awt.geom.Rectangle2D;
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.Enumeration;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
import java.util.Objects;
|
|
||||||
import javax.swing.AbstractAction;
|
|
||||||
import javax.swing.JButton;
|
|
||||||
import javax.swing.JCheckBox;
|
|
||||||
import javax.swing.JPanel;
|
|
||||||
import javax.swing.JPopupMenu;
|
|
||||||
import javax.swing.JScrollPane;
|
|
||||||
import javax.swing.JTextField;
|
|
||||||
import javax.swing.JTree;
|
|
||||||
import javax.swing.tree.DefaultMutableTreeNode;
|
|
||||||
import javax.swing.tree.MutableTreeNode;
|
|
||||||
import javax.swing.tree.TreeNode;
|
|
||||||
import javax.swing.tree.TreePath;
|
|
||||||
import org.objectweb.asm.tree.ClassNode;
|
|
||||||
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
|
|
||||||
import the.bytecode.club.bytecodeviewer.gui.VisibleComponent;
|
|
||||||
import the.bytecode.club.bytecodeviewer.util.FileContainer;
|
|
||||||
import the.bytecode.club.bytecodeviewer.util.FileDrop;
|
|
||||||
import the.bytecode.club.bytecodeviewer.util.LazyNameUtil;
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
* Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
|
|
||||||
* Copyright (C) 2014 Kalen 'Konloch' Kinloch - http://bytecodeviewer.com *
|
|
||||||
* *
|
|
||||||
* This program is free software: you can redistribute it and/or modify *
|
|
||||||
* it under the terms of the GNU General Public License as published by *
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or *
|
|
||||||
* (at your option) any later version. *
|
|
||||||
* *
|
|
||||||
* This program is distributed in the hope that it will be useful, *
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
||||||
* GNU General Public License for more details. *
|
|
||||||
* *
|
|
||||||
* You should have received a copy of the GNU General Public License *
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The file navigation pane.
|
|
||||||
*
|
|
||||||
* @author Konloch
|
|
||||||
* @author WaterWolf
|
|
||||||
* @author afffsdd
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class ResourceListPane extends VisibleComponent implements FileDrop.Listener
|
|
||||||
{
|
|
||||||
JCheckBox exact = new JCheckBox("Exact");
|
|
||||||
JButton open = new JButton("+");
|
|
||||||
JButton close = new JButton("-");
|
|
||||||
|
|
||||||
MyTreeNode treeRoot = new MyTreeNode("Loaded Files:");
|
|
||||||
public MyTree tree = new MyTree(treeRoot);
|
|
||||||
final String quickSearchText = "Quick file search (no file extension)";
|
|
||||||
final JTextField quickSearch = new JTextField(quickSearchText);
|
|
||||||
boolean cancel = false;
|
|
||||||
|
|
||||||
public KeyAdapter search = new KeyAdapter() {
|
|
||||||
@Override
|
|
||||||
public void keyPressed(final KeyEvent ke) {
|
|
||||||
if (ke.getKeyCode() == KeyEvent.VK_ENTER) {
|
|
||||||
|
|
||||||
final String qt = quickSearch.getText();
|
|
||||||
quickSearch.setText("");
|
|
||||||
|
|
||||||
if (qt.isEmpty()) //NOPE
|
|
||||||
return;
|
|
||||||
|
|
||||||
|
|
||||||
String[] path;
|
|
||||||
int found = 0;
|
|
||||||
|
|
||||||
if (qt.contains(".")) {
|
|
||||||
path = qt.split("\\.");
|
|
||||||
/*String[] path2 = new String[path.length];
|
|
||||||
for (int i = 0; i < path.length; i++) {
|
|
||||||
path2[i] = path[i];
|
|
||||||
if (i + 2 == path.length) {
|
|
||||||
path2[i + 1] = "." + path[i + 1];
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
} else {
|
|
||||||
path = new String[]{qt};
|
|
||||||
}
|
|
||||||
|
|
||||||
MyTreeNode curNode = treeRoot;
|
|
||||||
if (exact.isSelected()) {
|
|
||||||
pathLoop:
|
|
||||||
for (int i = 0; i < path.length; i++) {
|
|
||||||
final String pathName = path[i];
|
|
||||||
final boolean isLast = i == path.length - 1;
|
|
||||||
|
|
||||||
for (int c = 0; c < curNode.getChildCount(); c++) {
|
|
||||||
final MyTreeNode child = (MyTreeNode) curNode.getChildAt(c);
|
|
||||||
System.out.println(pathName + ":" + child.getUserObject());
|
|
||||||
|
|
||||||
if (child.getUserObject().equals(pathName)) {
|
|
||||||
curNode = child;
|
|
||||||
if (isLast) {
|
|
||||||
System.out.println("Found! " + curNode);
|
|
||||||
found++;
|
|
||||||
final TreePath pathn = new TreePath(curNode.getPath());
|
|
||||||
tree.setSelectionPath(pathn);
|
|
||||||
tree.makeVisible(pathn);
|
|
||||||
tree.scrollPathToVisible(pathn);
|
|
||||||
openPath(pathn); //auto open
|
|
||||||
break pathLoop;
|
|
||||||
}
|
|
||||||
continue pathLoop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println("Could not find " + pathName);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
{
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
Enumeration<MyTreeNode> enums = curNode.depthFirstEnumeration();
|
|
||||||
while (enums != null && enums.hasMoreElements()) {
|
|
||||||
|
|
||||||
MyTreeNode node = enums.nextElement();
|
|
||||||
if (node.isLeaf()) {
|
|
||||||
if (((String) (node.getUserObject())).toLowerCase().contains(path[path.length - 1].toLowerCase())) {
|
|
||||||
TreeNode[] pathArray = node.getPath();
|
|
||||||
int k = 0;
|
|
||||||
StringBuilder fullPath = new StringBuilder();
|
|
||||||
while (pathArray != null
|
|
||||||
&& k < pathArray.length) {
|
|
||||||
MyTreeNode n = (MyTreeNode) pathArray[k];
|
|
||||||
String s = (String) (n.getUserObject());
|
|
||||||
fullPath.append(s);
|
|
||||||
if (k++ != pathArray.length - 1) {
|
|
||||||
fullPath.append(".");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
String fullPathString = fullPath.toString();
|
|
||||||
if (fullPathString.toLowerCase().contains(qt.toLowerCase())) {
|
|
||||||
System.out.println("Found! " + node);
|
|
||||||
found++;
|
|
||||||
if (found >= 30) {
|
|
||||||
BytecodeViewer.showMessage("Uh oh, there could be more results but you've"
|
|
||||||
+ " triggered the 30 classes at once limit. Try refining your "
|
|
||||||
+ "search.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final TreePath pathn = new TreePath(node.getPath());
|
|
||||||
tree.setSelectionPath(pathn.getParentPath());
|
|
||||||
tree.setSelectionPath(pathn);
|
|
||||||
tree.makeVisible(pathn);
|
|
||||||
tree.scrollPathToVisible(pathn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private void showPopMenu(MyTree tree, TreePath selPath, int x, int y) {
|
|
||||||
if (selPath == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final JPopupMenu pop = new JPopupMenu();
|
|
||||||
pop.add(new AbstractAction("Remove") {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
TreePath selPath = ResourceListPane.this.tree.getPathForLocation(x, y);
|
|
||||||
DefaultMutableTreeNode selectNode =
|
|
||||||
(DefaultMutableTreeNode) Objects.requireNonNull(selPath).getLastPathComponent();
|
|
||||||
Enumeration<?> enumeration = treeRoot.children();
|
|
||||||
while (enumeration.hasMoreElements()) {
|
|
||||||
DefaultMutableTreeNode node = (DefaultMutableTreeNode) enumeration.nextElement();
|
|
||||||
if (node.isNodeAncestor(selectNode)) {
|
|
||||||
DefaultMutableTreeNode root = (DefaultMutableTreeNode) tree.getModel().getRoot();
|
|
||||||
root.remove(node);
|
|
||||||
for (FileContainer fileContainer : BytecodeViewer.files) {
|
|
||||||
if (fileContainer.name.equals(selectNode.toString())) {
|
|
||||||
removeFile(fileContainer);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
updateTree();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//TODO fix for Java 9+
|
|
||||||
pop.add(new AbstractAction("Expand", WindowsTreeUI.ExpandedIcon.createExpandedIcon()) {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
TreePath selPath = ResourceListPane.this.tree.getPathForLocation(x, y);
|
|
||||||
expandAll(tree, Objects.requireNonNull(selPath), true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
pop.add(new AbstractAction("Collapse", WindowsTreeUI.CollapsedIcon.createCollapsedIcon()) {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
TreePath selPath = ResourceListPane.this.tree.getPathForLocation(x, y);
|
|
||||||
expandAll(tree, Objects.requireNonNull(selPath), false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
pop.show(this.tree, x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void removeFile(FileContainer fileContainer) {
|
|
||||||
BytecodeViewer.files.remove(fileContainer);
|
|
||||||
LazyNameUtil.removeName(fileContainer.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ResourceListPane() {
|
|
||||||
super("ClassNavigation");
|
|
||||||
tree.setRootVisible(false);
|
|
||||||
tree.setShowsRootHandles(true);
|
|
||||||
quickSearch.setForeground(Color.gray);
|
|
||||||
setTitle("Files");
|
|
||||||
|
|
||||||
tree.addMouseListener(new MouseAdapter() {
|
|
||||||
@Override
|
|
||||||
public void mouseReleased(MouseEvent e) {
|
|
||||||
if (e.isMetaDown()) {
|
|
||||||
MyTree tree = (MyTree) e.getSource();
|
|
||||||
TreePath selPath = ResourceListPane.this.tree.getPathForLocation(e.getX(), e.getY());
|
|
||||||
if (selPath == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DefaultMutableTreeNode selectNode = (DefaultMutableTreeNode) selPath.getLastPathComponent();
|
|
||||||
Enumeration<?> enumeration = treeRoot.children();
|
|
||||||
while (enumeration.hasMoreElements()) {
|
|
||||||
DefaultMutableTreeNode node = (DefaultMutableTreeNode) enumeration.nextElement();
|
|
||||||
if (node.isNodeAncestor(selectNode)) {
|
|
||||||
// pop.show(tree, e.getX(), e.getY());
|
|
||||||
showPopMenu(tree, selPath, e.getX(), e.getY());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.open.addActionListener(e -> {
|
|
||||||
final TreeNode root = (TreeNode) tree.getModel().getRoot();
|
|
||||||
expandAll(tree, new TreePath(root), true);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.close.addActionListener(e -> {
|
|
||||||
final TreeNode root = (TreeNode) tree.getModel().getRoot();
|
|
||||||
final TreePath path = new TreePath(root);
|
|
||||||
expandAll(tree, path, false);
|
|
||||||
tree.expandPath(path);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.tree.addMouseListener(new MouseAdapter() {
|
|
||||||
@Override
|
|
||||||
public void mousePressed(MouseEvent e) {
|
|
||||||
openPath(tree.getPathForLocation(e.getX(), e.getY()));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.tree.addTreeSelectionListener(arg0 -> {
|
|
||||||
if (cancel) {
|
|
||||||
cancel = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
openPath(arg0.getPath());
|
|
||||||
});
|
|
||||||
|
|
||||||
this.tree.addKeyListener(new KeyListener() {
|
|
||||||
@Override
|
|
||||||
public void keyReleased(KeyEvent e) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void keyTyped(KeyEvent e) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void keyPressed(KeyEvent e) {
|
|
||||||
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
|
|
||||||
if (e.getSource() instanceof MyTree) {
|
|
||||||
MyTree tree = (MyTree) e.getSource();
|
|
||||||
openPath(tree.getSelectionPath());
|
|
||||||
}
|
|
||||||
} else if ((int) e.getKeyChar() != 0 && (int) e.getKeyChar() != 8 && (int) e.getKeyChar() != 127 && (int) e.getKeyChar() != 65535 && !e.isControlDown() && !e.isAltDown()) {
|
|
||||||
quickSearch.grabFocus();
|
|
||||||
quickSearch.setText("" + e.getKeyChar());
|
|
||||||
cancel = true;
|
|
||||||
} else if (e.isControlDown() && (int) e.getKeyChar() == 6) //ctrl + f
|
|
||||||
{
|
|
||||||
quickSearch.grabFocus();
|
|
||||||
} else {
|
|
||||||
cancel = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
quickSearch.addKeyListener(search);
|
|
||||||
|
|
||||||
quickSearch.addFocusListener(new FocusListener() {
|
|
||||||
@Override
|
|
||||||
public void focusGained(final FocusEvent arg0) {
|
|
||||||
if (quickSearch.getText().equals(quickSearchText)) {
|
|
||||||
quickSearch.setText("");
|
|
||||||
quickSearch.setForeground(Color.black);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void focusLost(final FocusEvent arg0) {
|
|
||||||
if (quickSearch.getText().isEmpty()) {
|
|
||||||
quickSearch.setText(quickSearchText);
|
|
||||||
quickSearch.setForeground(Color.gray);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
getContentPane().setLayout(new BorderLayout());
|
|
||||||
getContentPane().add(new JScrollPane(tree), BorderLayout.CENTER);
|
|
||||||
|
|
||||||
JPanel p2 = new JPanel();
|
|
||||||
p2.setLayout(new BorderLayout());
|
|
||||||
p2.add(quickSearch, BorderLayout.NORTH);
|
|
||||||
JPanel p3 = new JPanel(new BorderLayout());
|
|
||||||
p3.add(exact, BorderLayout.WEST);
|
|
||||||
JPanel p4 = new JPanel(new BorderLayout());
|
|
||||||
p4.add(open, BorderLayout.EAST);
|
|
||||||
p4.add(close, BorderLayout.WEST);
|
|
||||||
p3.add(p4, BorderLayout.EAST);
|
|
||||||
p2.add(p3, BorderLayout.SOUTH);
|
|
||||||
|
|
||||||
getContentPane().add(p2, BorderLayout.SOUTH);
|
|
||||||
|
|
||||||
this.setVisible(true);
|
|
||||||
new FileDrop(this, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void openClassFileToWorkSpace(final FileContainer container, final String name, final ClassNode node) {
|
|
||||||
BytecodeViewer.viewer.openClassFile(container, name, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void openFileToWorkSpace(final FileContainer container, String name, byte[] contents) {
|
|
||||||
BytecodeViewer.viewer.openFile(container, name, contents);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void filesDropped(final File[] files) {
|
|
||||||
if (files.length < 1)
|
|
||||||
return;
|
|
||||||
BytecodeViewer.openFiles(files, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateTree() {
|
|
||||||
try {
|
|
||||||
treeRoot.removeAllChildren();
|
|
||||||
for (FileContainer container : BytecodeViewer.files) {
|
|
||||||
MyTreeNode root = new MyTreeNode(container.name);
|
|
||||||
treeRoot.add(root);
|
|
||||||
ImageRenderer renderer = new ImageRenderer();
|
|
||||||
tree.setCellRenderer(renderer);
|
|
||||||
|
|
||||||
if (!container.classes.isEmpty()) {
|
|
||||||
for (ClassNode c : container.classes) {
|
|
||||||
String name = c.name;
|
|
||||||
final String[] spl = name.split("/");
|
|
||||||
if (spl.length < 2) {
|
|
||||||
root.add(new MyTreeNode(name + ".class"));
|
|
||||||
} else {
|
|
||||||
MyTreeNode parent = root;
|
|
||||||
for (int i1 = 0; i1 < spl.length; i1++) {
|
|
||||||
String s = spl[i1];
|
|
||||||
if (i1 == spl.length - 1)
|
|
||||||
s += ".class";
|
|
||||||
MyTreeNode child = null;
|
|
||||||
for (int i = 0; i < parent.getChildCount(); i++) {
|
|
||||||
if (((MyTreeNode) parent.getChildAt(i)).getUserObject()
|
|
||||||
.equals(s)) {
|
|
||||||
child = (MyTreeNode) parent.getChildAt(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (child == null) {
|
|
||||||
child = new MyTreeNode(s);
|
|
||||||
parent.add(child);
|
|
||||||
}
|
|
||||||
parent = child;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!container.files.isEmpty()) {
|
|
||||||
for (final Entry<String, byte[]> entry : container.files.entrySet()) {
|
|
||||||
String name = entry.getKey();
|
|
||||||
final String[] spl = name.split("/");
|
|
||||||
if (spl.length < 2) {
|
|
||||||
root.add(new MyTreeNode(name));
|
|
||||||
} else {
|
|
||||||
MyTreeNode parent = root;
|
|
||||||
for (final String s : spl) {
|
|
||||||
MyTreeNode child = null;
|
|
||||||
for (int i = 0; i < parent.getChildCount(); i++) {
|
|
||||||
if (((MyTreeNode) parent.getChildAt(i)).getUserObject()
|
|
||||||
.equals(s)) {
|
|
||||||
child = (MyTreeNode) parent.getChildAt(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (child == null) {
|
|
||||||
child = new MyTreeNode(s);
|
|
||||||
parent.add(child);
|
|
||||||
}
|
|
||||||
parent = child;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
treeRoot.sort();
|
|
||||||
tree.expandPath(new TreePath(tree.getModel().getRoot()));
|
|
||||||
tree.updateUI();
|
|
||||||
} catch (java.util.ConcurrentModificationException e) {
|
|
||||||
//ignore, the last file will reset everything
|
|
||||||
}
|
|
||||||
// expandAll(tree, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
private void expandAll(final JTree tree, final TreePath parent,
|
|
||||||
final boolean expand) {
|
|
||||||
// Traverse children
|
|
||||||
final TreeNode node = (TreeNode) parent.getLastPathComponent();
|
|
||||||
if (node.getChildCount() >= 0) {
|
|
||||||
for (final Enumeration e = node.children(); e.hasMoreElements(); ) {
|
|
||||||
final TreeNode n = (TreeNode) e.nextElement();
|
|
||||||
final TreePath path = parent.pathByAddingChild(n);
|
|
||||||
expandAll(tree, path, expand);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expansion or collapse must be done bottom-up
|
|
||||||
if (expand) {
|
|
||||||
tree.expandPath(parent);
|
|
||||||
} else {
|
|
||||||
tree.collapsePath(parent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class MyTree extends JTree {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = -2355167326094772096L;
|
|
||||||
DefaultMutableTreeNode treeRoot;
|
|
||||||
|
|
||||||
public MyTree(final DefaultMutableTreeNode treeRoot) {
|
|
||||||
super(treeRoot);
|
|
||||||
this.treeRoot = treeRoot;
|
|
||||||
}
|
|
||||||
|
|
||||||
StringMetrics m = null;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void paint(final Graphics g) {
|
|
||||||
try {
|
|
||||||
super.paint(g);
|
|
||||||
if (m == null) {
|
|
||||||
m = new StringMetrics((Graphics2D) g);
|
|
||||||
}
|
|
||||||
if (treeRoot.getChildCount() < 1) {
|
|
||||||
g.setColor(new Color(0, 0, 0, 100));
|
|
||||||
g.fillRect(0, 0, getWidth(), getHeight());
|
|
||||||
g.setColor(Color.white);
|
|
||||||
String s = "Drag class/jar/zip/APK/DEX here";
|
|
||||||
g.drawString(s,
|
|
||||||
((int) ((getWidth() / 2) - (m.getWidth(s) / 2))),
|
|
||||||
getHeight() / 2);
|
|
||||||
}
|
|
||||||
} catch (java.lang.InternalError | java.lang.NullPointerException ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class MyTreeNode extends DefaultMutableTreeNode {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = -8817777566176729571L;
|
|
||||||
|
|
||||||
public MyTreeNode(final Object o) {
|
|
||||||
super(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void insert(final MutableTreeNode newChild, final int childIndex) {
|
|
||||||
super.insert(newChild, childIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sort() {
|
|
||||||
recursiveSort(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private void recursiveSort(final MyTreeNode node) {
|
|
||||||
node.children.sort(nodeComparator);
|
|
||||||
for (MyTreeNode nextNode : (Iterable<MyTreeNode>) node.children) {
|
|
||||||
if (nextNode.getChildCount() > 0) {
|
|
||||||
recursiveSort(nextNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Comparator<MyTreeNode> nodeComparator = new Comparator<MyTreeNode>() {
|
|
||||||
@Override
|
|
||||||
public int compare(final MyTreeNode o1, final MyTreeNode o2) {
|
|
||||||
// To make sure nodes with children are always on top
|
|
||||||
final int firstOffset = o1.getChildCount() > 0 ? -1000 : 0;
|
|
||||||
final int secondOffset = o2.getChildCount() > 0 ? 1000 : 0;
|
|
||||||
return o1.toString().compareToIgnoreCase(o2.toString())
|
|
||||||
+ firstOffset + secondOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(final Object obj) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return 7;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author http://stackoverflow.com/a/18450804
|
|
||||||
*/
|
|
||||||
static class StringMetrics {
|
|
||||||
|
|
||||||
Font font;
|
|
||||||
FontRenderContext context;
|
|
||||||
|
|
||||||
public StringMetrics(Graphics2D g2) {
|
|
||||||
|
|
||||||
font = g2.getFont();
|
|
||||||
context = g2.getFontRenderContext();
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle2D getBounds(String message) {
|
|
||||||
|
|
||||||
return font.getStringBounds(message, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
double getWidth(String message) {
|
|
||||||
|
|
||||||
Rectangle2D bounds = getBounds(message);
|
|
||||||
return bounds.getWidth();
|
|
||||||
}
|
|
||||||
|
|
||||||
double getHeight(String message) {
|
|
||||||
|
|
||||||
Rectangle2D bounds = getBounds(message);
|
|
||||||
return bounds.getHeight();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void resetWorkspace() {
|
|
||||||
treeRoot.removeAllChildren();
|
|
||||||
tree.repaint();
|
|
||||||
tree.updateUI();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void openPath(TreePath path) {
|
|
||||||
if (path == null || path.getPathCount() == 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final StringBuilder nameBuffer = new StringBuilder();
|
|
||||||
for (int i = 2; i < path.getPathCount(); i++) {
|
|
||||||
nameBuffer.append(path.getPathComponent(i));
|
|
||||||
if (i < path.getPathCount() - 1) {
|
|
||||||
nameBuffer.append("/");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String cheapHax = path.getPathComponent(1).toString();
|
|
||||||
FileContainer container = null;
|
|
||||||
|
|
||||||
for (FileContainer c : BytecodeViewer.files) {
|
|
||||||
if (c.name.equals(cheapHax))
|
|
||||||
container = c;
|
|
||||||
}
|
|
||||||
|
|
||||||
String name = nameBuffer.toString();
|
|
||||||
if (name.endsWith(".class")) {
|
|
||||||
|
|
||||||
final ClassNode cn = BytecodeViewer.getClassNode(Objects.requireNonNull(container), name.substring(0,
|
|
||||||
name.length() - ".class".length()));
|
|
||||||
if (cn != null) {
|
|
||||||
openClassFileToWorkSpace(container, nameBuffer.toString(), cn);
|
|
||||||
} else {
|
|
||||||
openFileToWorkSpace(container, nameBuffer.toString(),
|
|
||||||
BytecodeViewer.getFileContents(nameBuffer.toString()));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
openFileToWorkSpace(container, nameBuffer.toString(),
|
|
||||||
BytecodeViewer.getFileContents(nameBuffer.toString()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,7 +4,7 @@ import org.apache.commons.io.FileUtils;
|
||||||
import org.objectweb.asm.tree.ClassNode;
|
import org.objectweb.asm.tree.ClassNode;
|
||||||
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
|
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
|
||||||
import the.bytecode.club.bytecodeviewer.api.ExceptionUI;
|
import the.bytecode.club.bytecodeviewer.api.ExceptionUI;
|
||||||
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.ResourceListPane;
|
import the.bytecode.club.bytecodeviewer.gui.resourcelist.ResourceListPane;
|
||||||
import the.bytecode.club.bytecodeviewer.gui.MainViewerGUI;
|
import the.bytecode.club.bytecodeviewer.gui.MainViewerGUI;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue