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.compilers.Compilers;
|
||||
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.extras.RunOptions;
|
||||
import the.bytecode.club.bytecodeviewer.gui.SearchBoxPane;
|
||||
|
@ -104,7 +104,7 @@ public class BytecodeViewer
|
|||
public static Refactorer refactorer = new Refactorer();
|
||||
public static List<FileContainer> files = new ArrayList<>(); //all of BCV's loaded files/classes/etc
|
||||
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
|
||||
|
|
|
@ -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.plugins.MaliciousCodeScannerOptions;
|
||||
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.RenameFields;
|
||||
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 ren = new JCheckBoxMenuItem("Rename ambiguous classes and class elements");
|
||||
//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 excludeNestedTypes = new JCheckBoxMenuItem("Exclude Nested Types");
|
||||
public final JCheckBoxMenuItem showDebugLineNumbers = new JCheckBoxMenuItem("Show Debug Line Numbers");
|
||||
|
@ -426,21 +426,21 @@ public class MainViewerGUI extends JFrame {
|
|||
visualSettings.add(showClassMethods);
|
||||
|
||||
//PROCYON SETTINGS
|
||||
settingsMainMenu.add(proyconSettingsSecondaryMenu);
|
||||
proyconSettingsSecondaryMenu.add(alwaysGenerateExceptionVars);
|
||||
proyconSettingsSecondaryMenu.add(excludeNestedTypes);
|
||||
proyconSettingsSecondaryMenu.add(showDebugLineNumbers);
|
||||
proyconSettingsSecondaryMenu.add(includeLineNumbersInBytecode);
|
||||
proyconSettingsSecondaryMenu.add(includeErrorDiagnostics);
|
||||
proyconSettingsSecondaryMenu.add(showSyntheticMembers);
|
||||
proyconSettingsSecondaryMenu.add(simplifyMemberReferences);
|
||||
proyconSettingsSecondaryMenu.add(mergeVariables);
|
||||
proyconSettingsSecondaryMenu.add(forceExplicitTypeArguments);
|
||||
proyconSettingsSecondaryMenu.add(forceExplicitImports);
|
||||
proyconSettingsSecondaryMenu.add(flattenSwitchBlocks);
|
||||
proyconSettingsSecondaryMenu.add(retainPointlessSwitches);
|
||||
proyconSettingsSecondaryMenu.add(retainRedunantCasts);
|
||||
proyconSettingsSecondaryMenu.add(unicodeOutputEnabled);
|
||||
settingsMainMenu.add(procyonSettingsSecondaryMenu);
|
||||
procyonSettingsSecondaryMenu.add(alwaysGenerateExceptionVars);
|
||||
procyonSettingsSecondaryMenu.add(excludeNestedTypes);
|
||||
procyonSettingsSecondaryMenu.add(showDebugLineNumbers);
|
||||
procyonSettingsSecondaryMenu.add(includeLineNumbersInBytecode);
|
||||
procyonSettingsSecondaryMenu.add(includeErrorDiagnostics);
|
||||
procyonSettingsSecondaryMenu.add(showSyntheticMembers);
|
||||
procyonSettingsSecondaryMenu.add(simplifyMemberReferences);
|
||||
procyonSettingsSecondaryMenu.add(mergeVariables);
|
||||
procyonSettingsSecondaryMenu.add(forceExplicitTypeArguments);
|
||||
procyonSettingsSecondaryMenu.add(forceExplicitImports);
|
||||
procyonSettingsSecondaryMenu.add(flattenSwitchBlocks);
|
||||
procyonSettingsSecondaryMenu.add(retainPointlessSwitches);
|
||||
procyonSettingsSecondaryMenu.add(retainRedunantCasts);
|
||||
procyonSettingsSecondaryMenu.add(unicodeOutputEnabled);
|
||||
|
||||
//CFR SETTINGS
|
||||
settingsMainMenu.add(cfrSettingsSecondaryMenu);
|
||||
|
|
|
@ -18,7 +18,7 @@ import javax.swing.tree.DefaultMutableTreeNode;
|
|||
import javax.swing.tree.TreePath;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
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.FieldCallSearch;
|
||||
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 SearchType[] SEARCH_TYPES = SearchType.values();
|
||||
|
||||
JCheckBox exact = new JCheckBox("Exact");
|
||||
DefaultMutableTreeNode treeRoot = new DefaultMutableTreeNode("Results");
|
||||
JTree tree;
|
||||
JComboBox typeBox;
|
||||
public final JCheckBox exact = new JCheckBox("Exact");
|
||||
public final DefaultMutableTreeNode treeRoot = new DefaultMutableTreeNode("Results");
|
||||
public final JTree tree;
|
||||
public final JComboBox typeBox;
|
||||
|
||||
SearchType searchType = null;
|
||||
JComboBox searchRadiusBox;
|
||||
public SearchType searchType = null;
|
||||
public final JComboBox searchRadiusBox;
|
||||
|
||||
public JButton search = new JButton("Search");
|
||||
BackgroundSearchThread t = new BackgroundSearchThread(true) {
|
||||
|
@ -75,7 +75,6 @@ public class SearchBoxPane extends VisibleComponent {
|
|||
public void doSearch() {
|
||||
// empty
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
@ -83,9 +82,7 @@ public class SearchBoxPane extends VisibleComponent {
|
|||
super("Search");
|
||||
|
||||
final JPanel optionPanel = new JPanel(new BorderLayout());
|
||||
|
||||
final JPanel searchRadiusOpt = new JPanel(new BorderLayout());
|
||||
|
||||
final JPanel searchOpts = new JPanel(new GridLayout(2, 1));
|
||||
|
||||
searchRadiusOpt.add(new JLabel("Search from "), BorderLayout.WEST);
|
||||
|
@ -96,15 +93,12 @@ public class SearchBoxPane extends VisibleComponent {
|
|||
}
|
||||
|
||||
searchRadiusBox = new JComboBox(model);
|
||||
|
||||
searchRadiusOpt.add(searchRadiusBox, BorderLayout.CENTER);
|
||||
|
||||
searchOpts.add(searchRadiusOpt);
|
||||
|
||||
model = new DefaultComboBoxModel();
|
||||
for (final SearchType st : SEARCH_TYPES) {
|
||||
for (final SearchType st : SEARCH_TYPES)
|
||||
model.addElement(st);
|
||||
}
|
||||
|
||||
typeBox = new JComboBox(model);
|
||||
final JPanel searchOptPanel = new JPanel();
|
||||
|
@ -160,8 +154,7 @@ public class SearchBoxPane extends VisibleComponent {
|
|||
final ClassNode fN = Objects.requireNonNull(container).getClassNode(className);
|
||||
|
||||
if (fN != null) {
|
||||
Objects.requireNonNull(MainViewerGUI.getComponent(ResourceListPane.class))
|
||||
.openClassFileToWorkSpace(container, className + ".class", fN);
|
||||
BytecodeViewer.viewer.openClassFile(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);
|
||||
// add more space between the label and the button
|
||||
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() {
|
||||
@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) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
@ -24,9 +24,9 @@ public class ImageRenderer extends DefaultTreeCellRenderer
|
|||
|
||||
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();
|
||||
|
||||
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 the.bytecode.club.bytecodeviewer.BytecodeViewer;
|
||||
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 java.io.File;
|
||||
|
|
Loading…
Reference in a new issue