From 15e5e76033f15f25a4ce63e2ee73fe7fb93b2521 Mon Sep 17 00:00:00 2001 From: jochen Date: Fri, 13 Aug 1999 09:09:47 +0000 Subject: [PATCH] swingui: class list toggable between class hierarchy and package hierarchy git-svn-id: https://svn.code.sf.net/p/jode/code/trunk@1148 379699f6-c40d-0410-875b-85095c16579e --- jode/TODO | 2 +- jode/configure.in | 1 + jode/jode/swingui/HierarchyTreeModel.java.in | 270 +++++++++++++++++++ jode/jode/swingui/Main.java.in | 93 ++++++- jode/jode/swingui/Makefile.am | 1 + jode/jode/swingui/PackagesTreeModel.java.in | 43 ++- 6 files changed, 399 insertions(+), 11 deletions(-) create mode 100644 jode/jode/swingui/HierarchyTreeModel.java.in diff --git a/jode/TODO b/jode/TODO index 870726d..26abebd 100644 --- a/jode/TODO +++ b/jode/TODO @@ -21,7 +21,7 @@ DeObfuscator: User Interface: - make a nice user interface: - - list classnames: toggable between class hierarchie/package hierarchie. + ~ list classnames: toggable between class hierarchie/package hierarchie. - list fields/method of selected class. - show decompilation of selected method. - show usage of method/fields. diff --git a/jode/configure.in b/jode/configure.in index f3bfadd..93434d3 100644 --- a/jode/configure.in +++ b/jode/configure.in @@ -202,6 +202,7 @@ jode/obfuscator/UniqueRenamer.java jode/obfuscator/WildCard.java jode/swingui/Main.java jode/swingui/PackagesTreeModel.java +jode/swingui/HierarchyTreeModel.java jode/type/Type.java jode/util/SimpleSet.java jode/util/SimpleMap.java diff --git a/jode/jode/swingui/HierarchyTreeModel.java.in b/jode/jode/swingui/HierarchyTreeModel.java.in new file mode 100644 index 0000000..4aff870 --- /dev/null +++ b/jode/jode/swingui/HierarchyTreeModel.java.in @@ -0,0 +1,270 @@ +/* HierarchyTreeModel Copyright (C) 1999 Jochen Hoenicke. + * + * 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 2, 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; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id$ + */ + +package jode.swingui; +import jode.Decompiler; +import jode.bytecode.ClassInfo; + +import @JAVAX_SWING@.JProgressBar; +import @JAVAX_SWING@.tree.TreeModel; +import @JAVAX_SWING@.tree.TreePath; +import @JAVAX_SWING@.event.TreeModelListener; +import @JAVAX_SWING@.event.TreeModelEvent; + +import @COLLECTIONEXTRA@.Comparable; +import @COLLECTIONS@.TreeSet; +import @COLLECTIONS@.Collection; +import @COLLECTIONS@.Iterator; +import @COLLECTIONS@.HashMap; +import @COLLECTIONS@.Set; +import @COLLECTIONS@.HashSet; + + +import java.util.Enumeration; +import java.util.NoSuchElementException; + +public class HierarchyTreeModel implements TreeModel, Runnable { + static final int MAX_PACKAGE_LEVEL = 10; + TreeElement root = new TreeElement(""); + Set listeners = new HashSet(); + JProgressBar progressBar; + Main main; + + class TreeElement implements Comparable { + String fullName; + TreeSet childs; + boolean inClassPath = false; + + public TreeElement(String fqn) { + this.fullName = fqn; + childs = new TreeSet(); + } + + public String getFullName() { + return fullName; + } + + public void addChild(TreeElement child) { + childs.add(child); + } + + public Collection getChilds() { + return childs; + } + + public String toString() { + return fullName; + } + + public int compareTo(Object o) { + TreeElement other = (TreeElement) o; + return fullName.compareTo(other.fullName); + } + + public boolean equals(Object o) { + return (o instanceof TreeElement) + && fullName.equals(((TreeElement)o).fullName); + } + + public int hashCode() { + return fullName.hashCode(); + } + } + + private TreeElement handleClass(HashMap classes, ClassInfo clazz) { + if (clazz == null) + return root; + TreeElement elem = (TreeElement) classes.get(clazz); + if (elem != null) + return elem; + + elem = new TreeElement(clazz.getName()); + classes.put(clazz, elem); + + if (!clazz.isInterface()) { + ClassInfo superClazz = clazz.getSuperclass(); + handleClass(classes, superClazz).addChild(elem); + } + ClassInfo[] ifaces = clazz.getInterfaces(); + for (int i=0; i < ifaces.length; i++) + handleClass(classes, ifaces[i]).addChild(elem); + if (ifaces.length == 0 && clazz.isInterface()) + root.addChild(elem); + return elem; + } + + public int readPackage(int depth, HashMap classes, String packageName, + int count) { + if (depth++ >= MAX_PACKAGE_LEVEL) + return count; + String prefix = packageName.length() == 0 ? "" : packageName + "."; + Enumeration enum = + ClassInfo.getClassesAndPackages(packageName); + while (enum.hasMoreElements()) { + //insert sorted and remove double elements; + String name = (String)enum.nextElement(); + String fqn = prefix + name; + if (ClassInfo.isPackage(fqn)) { + count = readPackage(depth, classes, fqn, count); + } else { + TreeElement elem = handleClass(classes, + ClassInfo.forName(fqn)); + if (progressBar != null) + progressBar.setValue(++count); + + elem.inClassPath = true; + } + } + return count; + } + + public int countClasses(int depth, String packageName) { + if (depth++ >= MAX_PACKAGE_LEVEL) + return 0; + int number = 0; + String prefix = packageName.length() == 0 ? "" : packageName + "."; + Enumeration enum = + ClassInfo.getClassesAndPackages(packageName); + while (enum.hasMoreElements()) { + //insert sorted and remove double elements; + String name = (String)enum.nextElement(); + String fqn = prefix + name; + if (ClassInfo.isPackage(fqn)) { + number += countClasses(depth, fqn); + } else { + number++; + } + } + return number; + } + + public HierarchyTreeModel(Main main) { + this.main = main; + this.progressBar = null; + rebuild(); + } + + public HierarchyTreeModel(Main main, JProgressBar progressBar) { + this.main = main; + this.progressBar = progressBar; + rebuild(); + } + + public void rebuild() { + Thread t = new Thread(this); + t.setPriority(Thread.MIN_PRIORITY); + t.start(); + } + + public void run() { + if (progressBar != null) { + progressBar.setMinimum(0); + progressBar.setMaximum(countClasses(0, "")); + } + readPackage(0, new HashMap(), "", 0); + + TreeModelListener[] ls; + synchronized (listeners) { + ls = (TreeModelListener[]) + listeners.toArray(new TreeModelListener[listeners.size()]); + } + TreeModelEvent ev = new TreeModelEvent(this, new Object[] { root }); + for (int i=0; i< ls.length; i++) + ls[i].treeStructureChanged(ev); + + main.reselect(); + } + + public void addTreeModelListener(TreeModelListener l) { + listeners.add(l); + } + public void removeTreeModelListener(TreeModelListener l) { + listeners.remove(l); + } + public void valueForPathChanged(TreePath path, Object newValue) { + // we don't allow values + } + + public Object getChild(Object parent, int index) { + Iterator iter = ((TreeElement) parent).getChilds().iterator(); + for (int i=0; i< index; i++) + iter.next(); + return iter.next(); + } + + public int getChildCount(Object parent) { + return ((TreeElement) parent).getChilds().size(); + } + + public int getIndexOfChild(Object parent, Object child) { + Iterator iter = ((TreeElement) parent).getChilds().iterator(); + int i=0; + while (iter.next() != child) + i++; + return i; + } + + public Object getRoot() { + return root; + } + + public boolean isLeaf(Object node) { + return ((TreeElement) node).getChilds().isEmpty(); + } + + public boolean isValidClass(Object node) { + return ((TreeElement) node).inClassPath; + } + + public String getFullName(Object node) { + return ((TreeElement) node).getFullName(); + } + + public TreePath getPath(String fullName) { + if (fullName == null || fullName.length() == 0) + return new TreePath(root); + + int length = 2; + ClassInfo ci = ClassInfo.forName(fullName); + while (ci.getSuperclass() != null) { + length++; + ci = ci.getSuperclass(); + } + + TreeElement[] path = new TreeElement[length]; + path[0] = root; + int nr = 0; + next_component: + while (nr < length-1) { + ci = ClassInfo.forName(fullName); + for (int i=2; i < length - nr; i++) + ci = ci.getSuperclass(); + Iterator iter = path[nr].getChilds().iterator(); + while (iter.hasNext()) { + TreeElement te = (TreeElement) iter.next(); + if (te.getFullName().equals(ci.getName())) { + path[++nr] = te; + continue next_component; + } + } + return null; + } + return new TreePath(path); + } +} diff --git a/jode/jode/swingui/Main.java.in b/jode/jode/swingui/Main.java.in index 077a4f4..5b120c6 100644 --- a/jode/jode/swingui/Main.java.in +++ b/jode/jode/swingui/Main.java.in @@ -35,11 +35,17 @@ public class Main implements ActionListener, Runnable, TreeSelectionListener { JFrame frame; JTree classTree; - PackagesTreeModel classModel; + JPanel statusLine; + PackagesTreeModel packModel; + HierarchyTreeModel hierModel; JTextArea sourcecodeArea, errorArea; Thread decompileThread; String currentClassPath, lastClassName; + JProgressBar progressBar; + + boolean hierarchyTree; + public Main(String classpath) { setClasspath(classpath); frame = new JFrame(GlobalOptions.copyright); @@ -59,9 +65,12 @@ public class Main } public void fillContentPane(Container contentPane) { + statusLine = new JPanel(); + hierarchyTree = false; + packModel = new PackagesTreeModel(this); + hierModel = null; Font monospaced = new Font("monospaced", Font.PLAIN, 12); - classModel = new PackagesTreeModel(); - classTree = new JTree(classModel); + classTree = new JTree(packModel); classTree.setRootVisible(false); DefaultTreeSelectionModel selModel = new DefaultTreeSelectionModel(); selModel.setSelectionMode(selModel.SINGLE_TREE_SELECTION); @@ -79,12 +88,17 @@ public class Main spText, spError); JSplitPane allPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, spClassTree, rightPane); - contentPane.add(allPane); + contentPane.setLayout(new BorderLayout()); + contentPane.add(allPane, BorderLayout.CENTER); + contentPane.add(statusLine, BorderLayout.SOUTH); + progressBar = new JProgressBar(); + statusLine.add(progressBar); rightPane.setDividerLocation(300); rightPane.setDividerSize(4); allPane.setDividerLocation(200); allPane.setDividerSize(4); - GlobalOptions.err = new PrintWriter(new AreaWriter(errorArea), true); + GlobalOptions.err = new PrintWriter + (new BufferedWriter(new AreaWriter(errorArea)), true); } public synchronized void valueChanged(TreeSelectionEvent e) { @@ -94,8 +108,14 @@ public class Main if (path == null) return; Object node = path.getLastPathComponent(); - if (node != null && classModel.isLeaf(node)) { - lastClassName = classModel.getFullName(node); + if (node != null) { + if (hierarchyTree && hierModel.isValidClass(node)) + lastClassName = hierModel.getFullName(node); + else if (!hierarchyTree && packModel.isValidClass(node)) + lastClassName = packModel.getFullName(node); + else + return; + decompileThread = new Thread(this); decompileThread.setPriority(Thread.MIN_PRIORITY); sourcecodeArea.setText("Please wait, while decompiling...\n"); @@ -226,6 +246,28 @@ public class Main menu.add(item); bar.add(menu); menu = new JMenu("Options"); + final JCheckBoxMenuItem hierItem + = new JCheckBoxMenuItem("Class hierarchy", hierarchyTree); + hierItem.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent ev) { + hierarchyTree = hierItem.isSelected(); + if (hierarchyTree && hierModel == null) { + hierModel = new HierarchyTreeModel(Main.this, progressBar); + reselect(); + } + classTree.setModel(hierarchyTree + ? (TreeModel) hierModel : packModel); + if (lastClassName != null) { + TreePath lastPath = (hierarchyTree + ? hierModel.getPath(lastClassName) + : packModel.getPath(lastClassName)); + classTree.setSelectionPath(lastPath); + classTree.scrollPathToVisible(lastPath); + } + } + }); + menu.add(hierItem); + menu.add(new JSeparator()); item = new JMenuItem("Set classpath..."); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ev) { @@ -256,9 +298,42 @@ public class Main classpath = "."; currentClassPath = classpath; ClassInfo.setClassPath(classpath); - if (classModel != null) { + if (classTree != null) classTree.clearSelection(); - classModel.rebuild(); + if (packModel != null) + packModel.rebuild(); + if (hierModel != null && hierarchyTree) { + hierModel.rebuild(); + } else { + hierModel = null; + } + } + + public void treeNodesChanged(TreeModelEvent e) { + reselect(); + } + + public void treeNodesInserted(TreeModelEvent e) { + reselect(); + } + + public void treeNodesRemoved(TreeModelEvent e) { + reselect(); + } + + public void treeStructureChanged(TreeModelEvent e) { + reselect(); + } + + public void reselect() { + if (lastClassName != null) { + TreePath lastPath = (hierarchyTree + ? hierModel.getPath(lastClassName) + : packModel.getPath(lastClassName)); + if (lastPath != null) { + classTree.setSelectionPath(lastPath); + classTree.scrollPathToVisible(lastPath); + } } } diff --git a/jode/jode/swingui/Makefile.am b/jode/jode/swingui/Makefile.am index 3e0ac43..e5e7cad 100644 --- a/jode/jode/swingui/Makefile.am +++ b/jode/jode/swingui/Makefile.am @@ -11,6 +11,7 @@ BUILD_CLASSPATH = $(top_srcdir):$(top_builddir):$(CLASSPATH):$(CLASSLIB) MY_JAVA_FILES = \ Main.java \ + HierarchyTreeModel.java \ PackagesTreeModel.java noinst_DATA = $(MY_JAVA_FILES:.java=.class) diff --git a/jode/jode/swingui/PackagesTreeModel.java.in b/jode/jode/swingui/PackagesTreeModel.java.in index c791b22..6ea673c 100644 --- a/jode/jode/swingui/PackagesTreeModel.java.in +++ b/jode/jode/swingui/PackagesTreeModel.java.in @@ -40,6 +40,7 @@ import java.util.NoSuchElementException; public class PackagesTreeModel implements TreeModel { Map cachedChildrens = new HashMap(); + Main main; class TreeElement implements Comparable { String fullName; @@ -89,6 +90,10 @@ public class PackagesTreeModel implements TreeModel { TreeElement root = new TreeElement("", "", false); Set listeners = new HashSet(); + public PackagesTreeModel(Main main) { + this.main = main; + } + public void rebuild() { cachedChildrens.clear(); TreeModelListener[] ls; @@ -99,6 +104,8 @@ public class PackagesTreeModel implements TreeModel { TreeModelEvent ev = new TreeModelEvent(this, new Object[] { root }); for (int i=0; i< ls.length; i++) ls[i].treeStructureChanged(ev); + + main.reselect(); } public TreeElement[] getChildrens(TreeElement parent) { @@ -158,7 +165,41 @@ public class PackagesTreeModel implements TreeModel { } public boolean isLeaf(Object node) { - return ((TreeElement)node).isLeaf(); + return ((TreeElement) node).isLeaf(); + } + + public boolean isValidClass(Object node) { + return ((TreeElement) node).isLeaf(); + } + + public TreePath getPath(String fullName) { + if (fullName == null || fullName.length() == 0) + return new TreePath(root); + int pos = -1; + int length = 2; + while ((pos = fullName.indexOf('.', pos+1)) != -1) + length++; + TreeElement[] path = new TreeElement[length]; + path[0] = root; + int i = 0; + pos = -1; + next_component: + while (pos < fullName.length()) { + int start = pos+1; + pos = fullName.indexOf('.', start); + if (pos == -1) + pos = fullName.length(); + String component = fullName.substring(start, pos); + TreeElement[] childs = getChildrens(path[i]); + for (int j=0; j< childs.length; j++) { + if (childs[j].getName().equals(component)) { + path[++i] = childs[j]; + continue next_component; + } + } + return null; + } + return new TreePath(path); } public String getFullName(Object node) {