diff --git a/projects/viztool/src/java/com/samskivert/viztool/Driver.java b/projects/viztool/src/java/com/samskivert/viztool/Driver.java new file mode 100644 index 00000000..0b1e3d0d --- /dev/null +++ b/projects/viztool/src/java/com/samskivert/viztool/Driver.java @@ -0,0 +1,34 @@ +// +// $Id: Driver.java,v 1.1 2001/07/14 00:55:21 mdb Exp $ + +package com.samskivert.viztool; + +import com.samskivert.viztool.enum.*; +import com.samskivert.viztool.viz.*; + +public class Driver +{ + public static void main (String[] args) + { + if (args.length < 1) { + System.err.println("Usage: Driver package_root"); + System.exit(-1); + } + String pkgroot = args[0]; + + // run ourselves on the classpath + String classpath = System.getProperty("java.class.path"); + ClassEnumerator enum = new ClassEnumerator(classpath); + + // print out the warnings + ClassEnumerator.Warning[] warnings = enum.getWarnings(); + for (int i = 0; i < warnings.length; i++) { + System.err.println("Warning: " + warnings[i].reason); + } + + // and finally generate the visualization + PackageEnumerator penum = new PackageEnumerator(pkgroot, enum, true); + HierarchyVisualizer viz = new HierarchyVisualizer(pkgroot, penum); + viz.render(10, System.out); + } +} diff --git a/projects/viztool/src/java/com/samskivert/viztool/hierarchy/CascadingChainVisualizer.java b/projects/viztool/src/java/com/samskivert/viztool/hierarchy/CascadingChainVisualizer.java index 28c86e80..6caf8ce4 100644 --- a/projects/viztool/src/java/com/samskivert/viztool/hierarchy/CascadingChainVisualizer.java +++ b/projects/viztool/src/java/com/samskivert/viztool/hierarchy/CascadingChainVisualizer.java @@ -1,5 +1,5 @@ // -// $Id: CascadingChainVisualizer.java,v 1.1 2001/07/13 23:25:13 mdb Exp $ +// $Id: CascadingChainVisualizer.java,v 1.2 2001/07/14 00:55:21 mdb Exp $ package com.samskivert.viztool.viz; @@ -40,7 +40,7 @@ public class CascadingChainLayout // the children will be below the name of this chain and inset by // four points to make space for the connecty lines - int x = GAP, y = hhei; + int x = 2*GAP, y = hhei; ArrayList kids = chain.getChildren(); for (int i = 0; i < kids.size(); i++) { diff --git a/projects/viztool/src/java/com/samskivert/viztool/hierarchy/Chain.java b/projects/viztool/src/java/com/samskivert/viztool/hierarchy/Chain.java index 8992067b..e3d2a404 100644 --- a/projects/viztool/src/java/com/samskivert/viztool/hierarchy/Chain.java +++ b/projects/viztool/src/java/com/samskivert/viztool/hierarchy/Chain.java @@ -1,5 +1,5 @@ // -// $Id: Chain.java,v 1.2 2001/07/13 23:25:13 mdb Exp $ +// $Id: Chain.java,v 1.3 2001/07/14 00:55:21 mdb Exp $ package com.samskivert.viztool.viz; @@ -16,14 +16,10 @@ public class Chain implements Element /** * Constructs a chain with the specified class as its root. */ - public Chain (String pkgroot, Class root) + public Chain (String name, Class root) { - _pkgroot = pkgroot; + _name = name; _root = root; - _name = _root.getName(); - if (_name.startsWith(pkgroot)) { - _name = _name.substring(_pkgroot.length()+1); - } } /** @@ -135,9 +131,9 @@ public class Chain implements Element * Adds a child to this chain. The specified class is assumed to * directly inherit from the class that is the root of this chain. */ - public void addClass (Class child) + public void addClass (String name, Class child) { - Chain chain = new Chain(_pkgroot, child); + Chain chain = new Chain(name, child); if (!_children.contains(chain)) { _children.add(chain); } @@ -192,9 +188,8 @@ public class Chain implements Element } } - protected String _pkgroot; - protected Class _root; protected String _name; + protected Class _root; protected ArrayList _children = new ArrayList(); protected Dimension _size = new Dimension(0, 0); diff --git a/projects/viztool/src/java/com/samskivert/viztool/hierarchy/ChainGroup.java b/projects/viztool/src/java/com/samskivert/viztool/hierarchy/ChainGroup.java new file mode 100644 index 00000000..1134c362 --- /dev/null +++ b/projects/viztool/src/java/com/samskivert/viztool/hierarchy/ChainGroup.java @@ -0,0 +1,106 @@ +// +// $Id: ChainGroup.java,v 1.1 2001/07/14 00:55:21 mdb Exp $ + +package com.samskivert.viztool.viz; + +import java.awt.Dimension; +import java.awt.Point; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Iterator; + +/** + * A chain group is used to group together all of the classes from a + * particular package. + */ +public class ChainGroup +{ + /** + * Constructs a chain group for a particular package with the + * specified package root and an iterator that is configured only to + * return classes from the specified package. + */ + public ChainGroup (String pkgroot, String pkg, Iterator iter) + { + // keep track of the package + _pkg = pkg; + + // process the classes provided by our enumerator + _roots = ChainUtil.buildChains(pkgroot, iter); + } + + /** + * Lays out the chains in this group and returns the total size. + */ + public Dimension layout (int pointSize) + { + // lay out the internal structure of our chains + ChainLayout clay = new CascadingChainLayout(); + for (int i = 0; i < _roots.size(); i++) { + Chain chain = (Chain)_roots.get(i); + chain.layout(pointSize, clay); + } + + // arrange them on the page + ElementLayout elay = new PackedColumnElementLayout(); + Dimension[] dims = elay.layout(_roots, MAX_WIDTH, MAX_HEIGHT); + + // for now we're punting and assume that no group will exceed a + // single page in size + _size = new Dimension(); + _size.width = dims[0].width + 2*BORDER; + _size.height = dims[0].height + 2*BORDER; + + return _size; + } + + /** + * Renders the chains in this group to the supplied output stream. + * This function requires that layoutGroup has previously + * been called to lay out the group's chains. + * + * @return the size of the rectangle occupied by the rendered chain + * group. + * + * @see #layoutGroup + */ + public void render (int pointSize, PrintStream out, int x, int y) + { + // render our chains + ChainRenderer renderer = new CascadingChainRenderer(); + for (int i = 0; i < _roots.size(); i++) { + Chain chain = (Chain)_roots.get(i); + Point loc = chain.getLocation(); + renderer.renderChain(chain, out, pointSize, + x + BORDER, y + BORDER); + } + + // print our title and a box around our border + out.println("/tname (" + _pkg + ") def"); + out.println("/twid tname stringwidth pop def"); + + int bx = x + BORDER + 2, by = y - pointSize/2; + out.println(bx + " " + by + " moveto"); + out.println("tname abshow"); + + out.println((x + BORDER - 2) + " " + y + " moveto"); + out.println("-" + (BORDER - 2) + " 0 rlineto"); + out.println("0 " + _size.height + " rlineto"); + out.println(_size.width + " 0 rlineto"); + out.println("0 -" + _size.height + " rlineto"); + out.println((_size.width - BORDER - 4) + " twid sub neg 0 rlineto"); + out.println("stroke"); + } + + protected String _pkg; + protected ArrayList _roots; + protected Dimension _size; + + protected static final int PAGE_WIDTH = (int)(72 * 7.5); + protected static final int PAGE_HEIGHT = (int)(72 * 10); + + protected static final int BORDER = 72/8; + + protected static final int MAX_WIDTH = PAGE_WIDTH - 2*BORDER; + protected static final int MAX_HEIGHT = PAGE_HEIGHT - 2*BORDER; +} diff --git a/projects/viztool/src/java/com/samskivert/viztool/hierarchy/ChainUtil.java b/projects/viztool/src/java/com/samskivert/viztool/hierarchy/ChainUtil.java index 6d4a8edd..a43b01aa 100644 --- a/projects/viztool/src/java/com/samskivert/viztool/hierarchy/ChainUtil.java +++ b/projects/viztool/src/java/com/samskivert/viztool/hierarchy/ChainUtil.java @@ -1,5 +1,5 @@ // -// $Id: ChainUtil.java,v 1.2 2001/07/13 23:25:13 mdb Exp $ +// $Id: ChainUtil.java,v 1.3 2001/07/14 00:55:21 mdb Exp $ package com.samskivert.viztool.viz; @@ -71,7 +71,7 @@ public class ChainUtil void computeRoots (String pkgroot, Iterator iter, ArrayList roots) { while (iter.hasNext()) { - insertClass(roots, pkgroot, (String)iter.next()); + insertClass(roots, pkgroot, (String)iter.next(), false); } } @@ -79,8 +79,8 @@ public class ChainUtil * Loads the supplied class and inserts it into the appropriate * position in the hierarchy based on its inheritance properties. */ - protected static - void insertClass (ArrayList roots, String pkgroot, String clazz) + protected static void insertClass ( + ArrayList roots, String pkgroot, String clazz, boolean outpkg) { try { // sanity check @@ -92,7 +92,7 @@ public class ChainUtil } // load and insert the class - insertClass(roots, pkgroot, Class.forName(clazz)); + insertClass(roots, pkgroot, Class.forName(clazz), outpkg); } catch (Exception e) { Log.warning("Unable to process class [class=" + clazz + @@ -105,22 +105,27 @@ public class ChainUtil * Inserts the specified class into the appropriate position in the * hierarchy based on its inheritance properties. */ - protected static - void insertClass (ArrayList roots, String pkgroot, Class target) + protected static void insertClass ( + ArrayList roots, String pkgroot, Class target, boolean outpkg) { // insert the parent of this class into the hierarchy Class parent = target.getSuperclass(); + String name = generateName(target, pkgroot, outpkg); // if we have no parent, we want to insert ourselves as a root // class if (parent == null || parent.equals(Object.class)) { - insertRoot(roots, pkgroot, target); + insertRoot(roots, name, target); } else { + String tpkg = pkgFromClass(target.getName()); + String ppkg = pkgFromClass(parent.getName()); + // if our parent is not in this package, we want to insert it // into the hierarchy as a root class - if (!parent.getName().startsWith(pkgroot)) { - insertRoot(roots, pkgroot, parent); + if (!tpkg.equals(ppkg)) { + String pname = generateName(parent, pkgroot, true); + insertRoot(roots, pname, parent); } // and now hang ourselves off of our parent class @@ -128,7 +133,9 @@ public class ChainUtil if (chain == null) { // if there's no chain for our parent class, we'll need to // insert it into the hierarchy - insertClass(roots, pkgroot, parent); + boolean samepkg = pkgFromClass(parent.getName()).equals( + pkgFromClass(target.getName())); + insertClass(roots, pkgroot, parent, !samepkg); // and refetch our chain chain = getChain(roots, parent); // sanity check @@ -140,16 +147,58 @@ public class ChainUtil return; } } + // add class will ignore our request if this class was already // added due to some previous operation - chain.addClass(target); + chain.addClass(name, target); } } - protected static boolean insertRoot (ArrayList roots, String pkgroot, + protected static String generateName (Class target, String pkgroot, + boolean outpkg) + { + String name; + + if (outpkg) { + // start with the fully qualified class name + name = target.getName(); + // if we're in the package root, we want to strip off the + // package root and prefix ... + if (name.startsWith(pkgroot)) { + name = ".." + name.substring(pkgroot.length()); + } + + } else { + name = nameFromClass(target.getName()); + } + + return name; + } + + /** + * Returns just the package qualifier given a fully qualified class + * name. + */ + public static String pkgFromClass (String fqn) + { + int didx = fqn.lastIndexOf("."); + return (didx == -1) ? fqn : fqn.substring(0, didx); + } + + /** + * Returns the unqualified class name given a fully qualified class + * name. + */ + public static String nameFromClass (String fqn) + { + int didx = fqn.lastIndexOf("."); + return (didx == -1) ? fqn : fqn.substring(didx+1); + } + + protected static boolean insertRoot (ArrayList roots, String name, Class root) { - Chain chroot = new Chain(pkgroot, root); + Chain chroot = new Chain(name, root); // make sure no chain already exists for this root if (!roots.contains(chroot)) { roots.add(chroot); diff --git a/projects/viztool/src/java/com/samskivert/viztool/hierarchy/HierarchyVisualizer.java b/projects/viztool/src/java/com/samskivert/viztool/hierarchy/HierarchyVisualizer.java index 3449b3fb..e376a58e 100644 --- a/projects/viztool/src/java/com/samskivert/viztool/hierarchy/HierarchyVisualizer.java +++ b/projects/viztool/src/java/com/samskivert/viztool/hierarchy/HierarchyVisualizer.java @@ -1,13 +1,16 @@ // -// $Id: HierarchyVisualizer.java,v 1.2 2001/07/13 23:25:13 mdb Exp $ +// $Id: HierarchyVisualizer.java,v 1.3 2001/07/14 00:55:21 mdb Exp $ package com.samskivert.viztool.viz; import java.awt.Dimension; import java.awt.Point; +import java.io.PrintStream; import java.util.*; import com.samskivert.viztool.Log; +import com.samskivert.viztool.enum.PackageEnumerator; +import com.samskivert.util.Comparators; /** * The hierarchy visualizer displays inheritance hierarchies in a compact @@ -33,64 +36,80 @@ public class HierarchyVisualizer // dump all the classes into an array list so that we can // repeatedly scan through the list - _classes = new ArrayList(); while (iter.hasNext()) { _classes.add(iter.next()); } - // process the classes provided by our enumerator - _roots = ChainUtil.buildChains(pkgroot, _classes.iterator()); + // compile a list of all packages in our collection + HashSet pkgset = new HashSet(); + iter = _classes.iterator(); + while (iter.hasNext()) { + pkgset.add(ChainUtil.pkgFromClass((String)iter.next())); + } - // now we need to sort out + // sort our package names + _packages = new String[pkgset.size()]; + iter = pkgset.iterator(); + for (int i = 0; iter.hasNext(); i++) { + _packages[i] = (String)iter.next(); + } + Arrays.sort(_packages, Comparators.STRING); + + // now create chain groups for each package + _groups = new ChainGroup[_packages.length]; + for (int i = 0; i < _groups.length; i++) { + PackageEnumerator penum = new PackageEnumerator( + _packages[i], _classes.iterator(), false); + _groups[i] = new ChainGroup(pkgroot, _packages[i], penum); + } } /** - * Processes the hierarchy of class chains assigning coordinates to - * all of the chains and subchains so that they can be used to - * generate a visualization. + * Lays out and renders each of the chain groups that make up this + * package hierarchy visualization. */ - public void layoutChains (int pointSize) + public void render (int pointSize, PrintStream out) { - // lay out the internal structure of our chains - ChainLayout clay = new CascadingChainLayout(); - for (int i = 0; i < _roots.size(); i++) { - Chain chain = (Chain)_roots.get(i); - chain.layout(pointSize, clay); - } + int x = X_MARGIN, y = Y_MARGIN; - // arrange them on the page - ElementLayout elay = new PackedColumnElementLayout(); - Dimension[] pdims = elay.layout(_roots, PAGE_WIDTH, PAGE_HEIGHT); + // print the preamble for the first page + out.println("0 setlinewidth"); - // render them - for (int p = 0; p < pdims.length; p++) { - ChainRenderer renderer = new CascadingChainRenderer(); - for (int i = 0; i < _roots.size(); i++) { - Chain chain = (Chain)_roots.get(i); - // skip chains not on this page - if (chain.getPage() != p) { - continue; - } - Point loc = chain.getLocation(); - renderer.renderChain(chain, System.out, pointSize, - X_MARGIN, Y_MARGIN); + for (int i = 0; i < _groups.length; i++) { + // lay out the group in question + Dimension dims = _groups[i].layout(pointSize); + + // determine if we need to skip to the next page or not + if ((y > 0) && (y + dims.height > PAGE_HEIGHT)) { + // print the postamble for this page and preamble for the + // new page + out.println("showpage"); + out.println("0 setlinewidth"); + y = Y_MARGIN; } - System.out.println("showpage"); - } - } - public void dumpClasses () - { - ChainUtil.dumpClasses(System.err, _roots); + // render the group at the requested location + _groups[i].render(pointSize, out, x, y); + + // increment our y location + y += (dims.height + GAP); + } + + // print the postamble for the final page + out.println("showpage"); } protected String _pkgroot; - protected ArrayList _roots; - protected ArrayList _classes; + protected ArrayList _classes = new ArrayList(); + + protected String[] _packages; + protected ChainGroup[] _groups; protected static final int PAGE_WIDTH = (int)(72 * 7.5); protected static final int PAGE_HEIGHT = (int)(72 * 10); protected static final int X_MARGIN = 72/2; protected static final int Y_MARGIN = 72/2; + + protected static final int GAP = 72/4; } diff --git a/projects/viztool/src/java/com/samskivert/viztool/layout/PackedColumnElementLayout.java b/projects/viztool/src/java/com/samskivert/viztool/layout/PackedColumnElementLayout.java index 644ba835..2cdfec99 100644 --- a/projects/viztool/src/java/com/samskivert/viztool/layout/PackedColumnElementLayout.java +++ b/projects/viztool/src/java/com/samskivert/viztool/layout/PackedColumnElementLayout.java @@ -1,5 +1,5 @@ // -// $Id: PackedColumnElementLayout.java,v 1.1 2001/07/13 23:25:13 mdb Exp $ +// $Id: PackedColumnElementLayout.java,v 1.2 2001/07/14 00:55:21 mdb Exp $ package com.samskivert.viztool.viz; @@ -70,6 +70,11 @@ public class PackedColumnElementLayout implements ElementLayout x += (size.width + GAP); } + // take a final stab at our maxwidth + if (x > maxwidth) { + maxwidth = x; + } + // make a note of how big the final page is pagedims.add(new Dimension(maxwidth, y+rowheight)); Dimension[] dims = new Dimension[pagedims.size()];