From 12b2fab254d51f0b714673fdfc44974cecaf3eb8 Mon Sep 17 00:00:00 2001 From: mdb Date: Fri, 13 Jul 2001 23:25:13 +0000 Subject: [PATCH] Mo' betta progress. git-svn-id: https://samskivert.googlecode.com/svn/trunk@182 6335cc39-0255-0410-8fd6-9bcaacd3b74c --- .../viztool/enum/ClassEnumerator.java | 38 ++++--- .../samskivert/viztool/enum/Enumerator.java | 21 ---- .../viztool/enum/FilterEnumerator.java | 23 ++-- .../viztool/enum/PackageEnumerator.java | 24 ++-- .../hierarchy/CascadingChainVisualizer.java | 65 +++++++++++ .../samskivert/viztool/hierarchy/Chain.java | 97 ++++++++++++++-- .../viztool/hierarchy/ChainUtil.java | 33 +++--- .../viztool/hierarchy/ChainVisualizer.java | 32 ++++++ .../hierarchy/HierarchyVisualizer.java | 59 ++++++++-- .../layout/CascadingChainRenderer.java | 73 ++++++++++++ .../viztool/layout/CascadingConstants.java | 23 ++++ .../viztool/layout/ChainRenderer.java | 37 +++++++ .../samskivert/viztool/layout/Element.java | 41 +++++++ .../viztool/layout/ElementLayout.java | 25 +++++ .../layout/PackedColumnElementLayout.java | 104 ++++++++++++++++++ .../viztool/layout/PostscriptUtil.java | 24 ++++ 16 files changed, 634 insertions(+), 85 deletions(-) delete mode 100644 projects/viztool/src/java/com/samskivert/viztool/enum/Enumerator.java create mode 100644 projects/viztool/src/java/com/samskivert/viztool/hierarchy/CascadingChainVisualizer.java create mode 100644 projects/viztool/src/java/com/samskivert/viztool/hierarchy/ChainVisualizer.java create mode 100644 projects/viztool/src/java/com/samskivert/viztool/layout/CascadingChainRenderer.java create mode 100644 projects/viztool/src/java/com/samskivert/viztool/layout/CascadingConstants.java create mode 100644 projects/viztool/src/java/com/samskivert/viztool/layout/ChainRenderer.java create mode 100644 projects/viztool/src/java/com/samskivert/viztool/layout/Element.java create mode 100644 projects/viztool/src/java/com/samskivert/viztool/layout/ElementLayout.java create mode 100644 projects/viztool/src/java/com/samskivert/viztool/layout/PackedColumnElementLayout.java create mode 100644 projects/viztool/src/java/com/samskivert/viztool/layout/PostscriptUtil.java diff --git a/projects/viztool/src/java/com/samskivert/viztool/enum/ClassEnumerator.java b/projects/viztool/src/java/com/samskivert/viztool/enum/ClassEnumerator.java index 0e48a301..e439c2f3 100644 --- a/projects/viztool/src/java/com/samskivert/viztool/enum/ClassEnumerator.java +++ b/projects/viztool/src/java/com/samskivert/viztool/enum/ClassEnumerator.java @@ -1,10 +1,11 @@ // -// $Id: ClassEnumerator.java,v 1.2 2001/07/04 18:22:26 mdb Exp $ +// $Id: ClassEnumerator.java,v 1.3 2001/07/13 23:25:13 mdb Exp $ package com.samskivert.viztool.enum; import java.util.ArrayList; import java.util.StringTokenizer; +import java.util.Iterator; /** * The class enumerator is supplied with a classpath which it decomposes @@ -14,7 +15,7 @@ import java.util.StringTokenizer; * The component enumerators are structured so that new enumerators can be * authored for new kinds of classpath component. */ -public class ClassEnumerator implements Enumerator +public class ClassEnumerator implements Iterator { /** * Constructs a class enumerator with the supplied classpath. A set of @@ -30,6 +31,7 @@ public class ClassEnumerator implements Enumerator // decompose the path and select enumerators for each component StringTokenizer tok = new StringTokenizer(classpath, ":"); ArrayList warnings = new ArrayList(); + ArrayList enums = new ArrayList(); while (tok.hasMoreTokens()) { String component = tok.nextToken(); @@ -46,7 +48,7 @@ public class ClassEnumerator implements Enumerator // ", component=" + component + "]."); // construct an enumerator to enumerate this component // and put it on our list - _enums.add(enum.enumerate(component)); + enums.add(enum.enumerate(component)); } catch (EnumerationException ee) { // if there was a problem creating an enumerator for @@ -56,6 +58,10 @@ public class ClassEnumerator implements Enumerator } } + // convert our list into an array + _enums = new ComponentEnumerator[enums.size()]; + enums.toArray(_enums); + // convert the warnings into an array _warnings = new Warning[warnings.size()]; warnings.toArray(_warnings); @@ -94,12 +100,12 @@ public class ClassEnumerator implements Enumerator return _warnings; } - public boolean hasMoreClasses () + public boolean hasNext () { return (_nextClass != null); } - public String nextClass () + public Object next () { String clazz = _nextClass; _nextClass = null; @@ -107,15 +113,20 @@ public class ClassEnumerator implements Enumerator return clazz; } + public void remove () + { + // not supported + } + /** * Queues up the next enumerator in the list or clears out our * enumerator reference if we have no remaining enumerators. */ protected void scanToNextClass () { - if (_enums.size() > 0) { - // grab the first enumerator in the list - ComponentEnumerator enum = (ComponentEnumerator)_enums.get(0); + if (_enumidx < _enums.length) { + // grab the current enumerator + ComponentEnumerator enum = _enums[_enumidx]; // if it has more classes if (enum.hasMoreClasses()) { @@ -124,14 +135,15 @@ public class ClassEnumerator implements Enumerator return; } else { - // otherwise remove it from the list and try the next enum - _enums.remove(0); + // otherwise try the next enum + _enumidx++; scanToNextClass(); } } } - protected ArrayList _enums = new ArrayList(); + protected ComponentEnumerator[] _enums; + protected int _enumidx; protected String _nextClass; protected Warning[] _warnings; @@ -162,8 +174,8 @@ public class ClassEnumerator implements Enumerator } // enumerate over whatever classes we can - while (enum.hasMoreClasses()) { - System.out.println("Class: " + enum.nextClass()); + while (enum.hasNext()) { + System.out.println("Class: " + enum.next()); } } diff --git a/projects/viztool/src/java/com/samskivert/viztool/enum/Enumerator.java b/projects/viztool/src/java/com/samskivert/viztool/enum/Enumerator.java deleted file mode 100644 index 9fd63ca9..00000000 --- a/projects/viztool/src/java/com/samskivert/viztool/enum/Enumerator.java +++ /dev/null @@ -1,21 +0,0 @@ -// -// $Id: Enumerator.java,v 1.1 2001/07/04 18:22:26 mdb Exp $ - -package com.samskivert.viztool.enum; - -/** - * An enumerator is used to iterate over a set of classes. - */ -public interface Enumerator -{ - /** - * Returns true if this enumerator has more classes yet to enumerate, - * false if all classes have been reported. - */ - public boolean hasMoreClasses (); - - /** - * Returns the class name of the next class in the enumeration. - */ - public String nextClass (); -} diff --git a/projects/viztool/src/java/com/samskivert/viztool/enum/FilterEnumerator.java b/projects/viztool/src/java/com/samskivert/viztool/enum/FilterEnumerator.java index 1e653140..0b6afb3b 100644 --- a/projects/viztool/src/java/com/samskivert/viztool/enum/FilterEnumerator.java +++ b/projects/viztool/src/java/com/samskivert/viztool/enum/FilterEnumerator.java @@ -1,8 +1,10 @@ // -// $Id: FilterEnumerator.java,v 1.1 2001/07/04 18:22:26 mdb Exp $ +// $Id: FilterEnumerator.java,v 1.2 2001/07/13 23:25:13 mdb Exp $ package com.samskivert.viztool.enum; +import java.util.Iterator; + /** * The filter enumerator provides the framework by which a particular * subset of classes can be filtered from the total enumeration of classes @@ -10,13 +12,13 @@ package com.samskivert.viztool.enum; * classes in a particular package or only considering interfaces, and so * on. */ -public abstract class FilterEnumerator implements Enumerator +public abstract class FilterEnumerator implements Iterator { /** * Constructs a filter enumerator with the supplied class enumerator * as the source of classes. */ - public FilterEnumerator (ClassEnumerator source) + public FilterEnumerator (Iterator source) { _source = source; // we'd love to call scanToNextClass() here but that calls @@ -26,7 +28,7 @@ public abstract class FilterEnumerator implements Enumerator // executed. sigh. } - public boolean hasMoreClasses () + public boolean hasNext () { if (_nextClass == null) { scanToNextClass(); @@ -34,7 +36,7 @@ public abstract class FilterEnumerator implements Enumerator return _nextClass != null; } - public String nextClass () + public Object next () { if (_nextClass == null) { scanToNextClass(); @@ -45,10 +47,15 @@ public abstract class FilterEnumerator implements Enumerator return clazz; } + public void remove () + { + // not supported + } + protected void scanToNextClass () { - while (_source.hasMoreClasses()) { - String clazz = _source.nextClass(); + while (_source.hasNext()) { + String clazz = (String)_source.next(); if (!filterClass(clazz)) { _nextClass = clazz; break; @@ -63,6 +70,6 @@ public abstract class FilterEnumerator implements Enumerator */ protected abstract boolean filterClass (String clazz); - protected ClassEnumerator _source; + protected Iterator _source; protected String _nextClass; } diff --git a/projects/viztool/src/java/com/samskivert/viztool/enum/PackageEnumerator.java b/projects/viztool/src/java/com/samskivert/viztool/enum/PackageEnumerator.java index c65e3187..7aed27c0 100644 --- a/projects/viztool/src/java/com/samskivert/viztool/enum/PackageEnumerator.java +++ b/projects/viztool/src/java/com/samskivert/viztool/enum/PackageEnumerator.java @@ -1,24 +1,31 @@ // -// $Id: PackageEnumerator.java,v 1.1 2001/07/04 18:22:26 mdb Exp $ +// $Id: PackageEnumerator.java,v 1.2 2001/07/13 23:25:13 mdb Exp $ package com.samskivert.viztool.enum; +import java.util.Iterator; + /** * The package enumerator filters out only classes from the specified - * package (and subpackages) from the class enumerator provided at - * construct time. + * package from the class enumerator provided at construct time. */ public class PackageEnumerator extends FilterEnumerator { - public PackageEnumerator (String pkg, ClassEnumerator source) + public PackageEnumerator (String pkg, Iterator source, boolean subpkgs) { super(source); _package = pkg; + _subpkgs = subpkgs; } protected boolean filterClass (String clazz) { - return !clazz.startsWith(_package); + if (!clazz.startsWith(_package)) { + return true; + } + + return _subpkgs ? false: + (clazz.substring(_package.length()+1).indexOf(".") != -1); } public static void main (String[] args) @@ -27,7 +34,7 @@ public class PackageEnumerator extends FilterEnumerator String classpath = System.getProperty("java.class.path"); ClassEnumerator enum = new ClassEnumerator(classpath); String pkg = "com.samskivert.viztool.enum"; - PackageEnumerator penum = new PackageEnumerator(pkg, enum); + PackageEnumerator penum = new PackageEnumerator(pkg, enum, true); // print out the warnings ClassEnumerator.Warning[] warnings = enum.getWarnings(); @@ -36,10 +43,11 @@ public class PackageEnumerator extends FilterEnumerator } // enumerate over whatever classes match our package - while (penum.hasMoreClasses()) { - System.out.println("Class: " + penum.nextClass()); + while (penum.hasNext()) { + System.out.println("Class: " + penum.next()); } } protected String _package; + protected boolean _subpkgs; } diff --git a/projects/viztool/src/java/com/samskivert/viztool/hierarchy/CascadingChainVisualizer.java b/projects/viztool/src/java/com/samskivert/viztool/hierarchy/CascadingChainVisualizer.java new file mode 100644 index 00000000..28c86e80 --- /dev/null +++ b/projects/viztool/src/java/com/samskivert/viztool/hierarchy/CascadingChainVisualizer.java @@ -0,0 +1,65 @@ +// +// $Id: CascadingChainVisualizer.java,v 1.1 2001/07/13 23:25:13 mdb Exp $ + +package com.samskivert.viztool.viz; + +import java.awt.Dimension; +import java.util.ArrayList; + +/** + * The cascading chain layout lays out chains in the standard cascading + * format that looks something like this: + * + *
+ * Foo
+ * |
+ * +-> Bar
+ * |   |
+ * |   +-> Biff
+ * |
+ * +-> Baz
+ * 
+ * + * It should be used in tandem with the + * CascadingChainRenderer. + * + * @see CascadingChainRenderer + */ +public class CascadingChainLayout + implements ChainLayout, CascadingConstants +{ + // docs inherited from interface + public void layoutChain (Chain chain, int pointSize) + { + // the header will be the name of this chain surrounded by N + // points of space and a box + int hwid = PostscriptUtil.estimateWidth(chain.getName(), pointSize) + + 2*HEADER_BORDER; + int hhei = 2*HEADER_BORDER + pointSize; + int maxwid = hwid; + + // 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; + ArrayList kids = chain.getChildren(); + + for (int i = 0; i < kids.size(); i++) { + Chain kid = (Chain)kids.get(i); + Dimension ksize = kid.getSize(); + y += GAP; // add the gap + kid.setLocation(x, y); + y += ksize.height; // add the dimensions of the kid +// System.err.println("Locating " + kid.getName() + +// " at +" + x + "+" + y + "."); + // track max width + if (maxwid < (x + ksize.width)) { + maxwid = x + ksize.width; + } + } + + // set the dimensions of the main chain +// System.err.println("Sizing " + chain.getName() + +// " to " + maxwid + "x" + y + "."); + chain.setSize(maxwid, y); + } +} 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 b94d9d30..8992067b 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.1 2001/07/04 18:24:07 mdb Exp $ +// $Id: Chain.java,v 1.2 2001/07/13 23:25:13 mdb Exp $ package com.samskivert.viztool.viz; @@ -11,20 +11,50 @@ import java.awt.Point; * A chain is used by the hierarchy visualizer to represent inheritance * chains. */ -public class Chain +public class Chain implements Element { /** * Constructs a chain with the specified class as its root. */ - public Chain (Class root) + public Chain (String pkgroot, Class root) { + _pkgroot = pkgroot; _root = root; + _name = _root.getName(); + if (_name.startsWith(pkgroot)) { + _name = _name.substring(_pkgroot.length()+1); + } + } + + /** + * Returns the name of this chain (which should be used when + * displaying the chain). + */ + public String getName () + { + return _name; + } + + /** + * Returns the class that forms the root of this chain. + */ + public Class getRoot () + { + return _root; + } + + /** + * Returns the name of the class that forms the root of this chain. + */ + public String getRootName () + { + return _root.getName(); } /** * Returns a Dimension instance representing the size of - * this chain (and all contained subchains) in whatever coordinates - * are being used to diagram this chain. + * this chain (and all contained subchains). All coordinates are in + * points. */ public Dimension getSize () { @@ -32,8 +62,8 @@ public class Chain } /** - * Returns the location of this chain in whatever coordinate system - * that is being used to diagram this chain. + * Returns the location of this chain relative to its parent chain. + * All coordinates are in points. */ public Point getLocation () { @@ -41,7 +71,7 @@ public class Chain } /** - * Sets the size of this chain. + * Sets the size of this chain. All coordinates are in points. * * @see #getSize */ @@ -51,7 +81,8 @@ public class Chain } /** - * Sets the location of this chain. + * Sets the location of this chain relative to its parent. All + * coordinates are in points. * * @see #getLocation */ @@ -60,13 +91,53 @@ public class Chain _location = new Point(x, y); } + // inherited from interface + public void setPage (int pageno) + { + _pageno = pageno; + } + + // inherited from interface + public int getPage () + { + return _pageno; + } + + /** + * Returns an array list containing the children chains of this chain. + * If this chain has no children the list will be of zero length but + * will not be null. This list should not be modified. Oh, + * for a const keyword. + */ + public ArrayList getChildren () + { + return _children; + } + + /** + * Lays out all of the children of this chain and then requests that + * the supplied layout manager arrange those children and compute the + * dimensions of this chain based on all of that information. + */ + public void layout (int pointSize, ChainLayout clay) + { + // first layout our children + for (int i = 0; i < _children.size(); i++) { + Chain child = (Chain)_children.get(i); + child.layout(pointSize, clay); + } + + // now lay ourselves out + clay.layoutChain(this, pointSize); + } + /** * 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) { - Chain chain = new Chain(child); + Chain chain = new Chain(_pkgroot, child); if (!_children.contains(chain)) { _children.add(chain); } @@ -114,15 +185,19 @@ public class Chain protected void toString (String indent, StringBuffer out) { - out.append(indent).append(_root.getName()).append("\n"); + out.append(indent).append(_name).append("\n"); for (int i = 0; i < _children.size(); i++) { Chain child = (Chain)_children.get(i); child.toString(indent + " ", out); } } + protected String _pkgroot; protected Class _root; + protected String _name; + protected ArrayList _children = new ArrayList(); protected Dimension _size = new Dimension(0, 0); protected Point _location = new Point(0, 0); + protected int _pageno; } 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 1f0f9d40..6d4a8edd 100644 --- a/projects/viztool/src/java/com/samskivert/viztool/hierarchy/ChainUtil.java +++ b/projects/viztool/src/java/com/samskivert/viztool/hierarchy/ChainUtil.java @@ -1,12 +1,13 @@ // -// $Id: ChainUtil.java,v 1.1 2001/07/04 18:24:07 mdb Exp $ +// $Id: ChainUtil.java,v 1.2 2001/07/13 23:25:13 mdb Exp $ package com.samskivert.viztool.viz; +import java.io.PrintStream; import java.util.ArrayList; +import java.util.Iterator; import com.samskivert.viztool.Log; -import com.samskivert.viztool.enum.Enumerator; /** * Chain related utility functions. @@ -21,10 +22,10 @@ public class ChainUtil * * @return an array list containing all of the root chains. */ - public static ArrayList buildChains (String pkgroot, Enumerator enum) + public static ArrayList buildChains (String pkgroot, Iterator iter) { ArrayList roots = new ArrayList(); - computeRoots(pkgroot, enum, roots); + computeRoots(pkgroot, iter, roots); return roots; } @@ -53,24 +54,24 @@ public class ChainUtil * Dumps the classes in the supplied array list of chain instances to * stdout. */ - public static void dumpClasses (ArrayList roots) + public static void dumpClasses (PrintStream out, ArrayList roots) { for (int i = 0; i < roots.size(); i++) { Chain root = (Chain)roots.get(i); - System.out.print(root.toString()); + out.print(root.toString()); } - System.out.flush(); + out.flush(); } /** - * Scans the list of classes provided by the supplied enumerator and + * Scans the list of classes provided by the supplied iterator and * constructs a hierarchical representation of those classes. */ protected static - void computeRoots (String pkgroot, Enumerator enum, ArrayList roots) + void computeRoots (String pkgroot, Iterator iter, ArrayList roots) { - while (enum.hasMoreClasses()) { - insertClass(roots, pkgroot, enum.nextClass()); + while (iter.hasNext()) { + insertClass(roots, pkgroot, (String)iter.next()); } } @@ -96,6 +97,7 @@ public class ChainUtil } catch (Exception e) { Log.warning("Unable to process class [class=" + clazz + ", error=" + e + "]."); + Log.logStackTrace(e); } } @@ -112,13 +114,13 @@ public class ChainUtil // if we have no parent, we want to insert ourselves as a root // class if (parent == null || parent.equals(Object.class)) { - insertRoot(roots, target); + insertRoot(roots, pkgroot, target); } else { // 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, parent); + insertRoot(roots, pkgroot, parent); } // and now hang ourselves off of our parent class @@ -144,9 +146,10 @@ public class ChainUtil } } - protected static boolean insertRoot (ArrayList roots, Class root) + protected static boolean insertRoot (ArrayList roots, String pkgroot, + Class root) { - Chain chroot = new Chain(root); + Chain chroot = new Chain(pkgroot, 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/ChainVisualizer.java b/projects/viztool/src/java/com/samskivert/viztool/hierarchy/ChainVisualizer.java new file mode 100644 index 00000000..b20bc3e9 --- /dev/null +++ b/projects/viztool/src/java/com/samskivert/viztool/hierarchy/ChainVisualizer.java @@ -0,0 +1,32 @@ +// +// $Id: ChainVisualizer.java,v 1.1 2001/07/13 23:25:13 mdb Exp $ + +package com.samskivert.viztool.viz; + +/** + * The chain layout is used to compute the dimensions of chains and their + * children in preparation for rendering them in a particular manner. In + * general a layout is coupled with one or more renderers which generate + * the proper rendering instructions based on the layout information + * computed by this layout manager. The reason rendering is decoupled from + * layout is to facilitate easier support for rendering the same layout + * via different formatting languages (postscript and vml for example). + * + * @see ChainRenderer + */ +public interface ChainLayout +{ + /** + * Assigns positions to the children of the supplied chain based on + * the layout policies desired by the implementation and assigns + * dimensions to the specified chain based on the dimensions of its + * children and the aforementioned layout policies. The children of + * the provided chain instance are guaranteed to have been layed out + * (meaning they have dimensions but no position) prior to this call. + * + * @param chain the chain to be layed out. + * @param pointSize the point size of the font to be used in computing + * our dimensions (all coordinates are in points). + */ + public void layoutChain (Chain chain, int pointSize); +} 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 69b9afdc..3449b3fb 100644 --- a/projects/viztool/src/java/com/samskivert/viztool/hierarchy/HierarchyVisualizer.java +++ b/projects/viztool/src/java/com/samskivert/viztool/hierarchy/HierarchyVisualizer.java @@ -1,12 +1,13 @@ // -// $Id: HierarchyVisualizer.java,v 1.1 2001/07/04 18:24:07 mdb Exp $ +// $Id: HierarchyVisualizer.java,v 1.2 2001/07/13 23:25:13 mdb Exp $ package com.samskivert.viztool.viz; -import java.util.ArrayList; +import java.awt.Dimension; +import java.awt.Point; +import java.util.*; import com.samskivert.viztool.Log; -import com.samskivert.viztool.enum.Enumerator; /** * The hierarchy visualizer displays inheritance hierarchies in a compact @@ -25,14 +26,22 @@ public class HierarchyVisualizer * @param enum The enumerator that will return the names of all of the * classes in the specified package. */ - public HierarchyVisualizer (String pkgroot, Enumerator enum) + public HierarchyVisualizer (String pkgroot, Iterator iter) { - // keep track of these + // keep track of the package root _pkgroot = pkgroot; - _enum = enum; + + // 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, enum); + _roots = ChainUtil.buildChains(pkgroot, _classes.iterator()); + + // now we need to sort out } /** @@ -42,14 +51,46 @@ public class HierarchyVisualizer */ public void layoutChains (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[] pdims = elay.layout(_roots, PAGE_WIDTH, PAGE_HEIGHT); + + // 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); + } + System.out.println("showpage"); + } } public void dumpClasses () { - ChainUtil.dumpClasses(_roots); + ChainUtil.dumpClasses(System.err, _roots); } protected String _pkgroot; - protected Enumerator _enum; protected ArrayList _roots; + protected ArrayList _classes; + + 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; } diff --git a/projects/viztool/src/java/com/samskivert/viztool/layout/CascadingChainRenderer.java b/projects/viztool/src/java/com/samskivert/viztool/layout/CascadingChainRenderer.java new file mode 100644 index 00000000..1e3321df --- /dev/null +++ b/projects/viztool/src/java/com/samskivert/viztool/layout/CascadingChainRenderer.java @@ -0,0 +1,73 @@ +// +// $Id: CascadingChainRenderer.java,v 1.1 2001/07/13 23:25:13 mdb Exp $ + +package com.samskivert.viztool.viz; + +import java.awt.Dimension; +import java.awt.Point; +import java.io.PrintStream; +import java.util.ArrayList; + +/** + * Renders a chain that has been layed out by the cascading chain layout + * manager. + * + * @see CascadingChainLayout + */ +public class CascadingChainRenderer + implements ChainRenderer, CascadingConstants +{ + // docs inherited from interface + public void renderChain (Chain chain, PrintStream out, + int pointSize, int x, int y) + { + Point loc = chain.getLocation(); + + // figure out where we'll be located + x += loc.x; + y += loc.y; + +// System.err.println("Rendering " + chain.getName() + +// " at +" + x + "+" + y + "."); + + // work out some useful stuff + out.println("gsave"); + out.println("/size " + pointSize + " def"); + out.println(x + " " + y + " translate"); + out.println("/cname (" + chain.getName() + ") def"); + out.println("/nwid cname stringwidth pop def"); + out.println("/nhei size def"); + out.println("/border " + HEADER_BORDER + " def"); + out.println("/dborder border 2 mul def"); + + // stroke a box that will contain the name + out.println("0 0 nwid dborder add nhei dborder add rectstroke"); +// out.println("border border nwid nhei rectstroke"); + out.println("border border moveto cname abshow"); + + // render our connecty lines + ArrayList kids = chain.getChildren(); + if (kids.size() > 0) { + Point kloc = ((Chain)kids.get(0)).getLocation(); + int half = kloc.x/2; + out.println(half + " nhei dborder add moveto"); + + for (int i = 0; i < kids.size(); i++) { + Chain kid = (Chain)kids.get(i); + kloc = kid.getLocation(); + out.println(half + " " + + (kloc.y + pointSize/2 + HEADER_BORDER) + " lineto"); + out.println(half + " 0 rlineto"); + out.println(half + " neg 0 rmoveto"); + } + out.println("stroke"); + } + out.println("grestore"); + + // now render the kids + for (int i = 0; i < kids.size(); i++) { + Chain kid = (Chain)kids.get(i); + renderChain(kid, out, pointSize, x, y); + } + } +} diff --git a/projects/viztool/src/java/com/samskivert/viztool/layout/CascadingConstants.java b/projects/viztool/src/java/com/samskivert/viztool/layout/CascadingConstants.java new file mode 100644 index 00000000..c1acf0c2 --- /dev/null +++ b/projects/viztool/src/java/com/samskivert/viztool/layout/CascadingConstants.java @@ -0,0 +1,23 @@ +// +// $Id: CascadingConstants.java,v 1.1 2001/07/13 23:25:13 mdb Exp $ + +package com.samskivert.viztool.viz; + +/** + * Constants used in laying out chains in the cascading style. + * + * @see CascadingChainLayout + * @see CascadingChainRenderer + */ +public interface CascadingConstants +{ + /** + * The number of points surrounding the name of the chain. + */ + public static final int HEADER_BORDER = 3; + + /** + * The number of points of spacing between each child chain. + */ + public static final int GAP = 4; +} diff --git a/projects/viztool/src/java/com/samskivert/viztool/layout/ChainRenderer.java b/projects/viztool/src/java/com/samskivert/viztool/layout/ChainRenderer.java new file mode 100644 index 00000000..bd17c9cd --- /dev/null +++ b/projects/viztool/src/java/com/samskivert/viztool/layout/ChainRenderer.java @@ -0,0 +1,37 @@ +// +// $Id: ChainRenderer.java,v 1.1 2001/07/13 23:25:13 mdb Exp $ + +package com.samskivert.viztool.viz; + +import java.io.PrintStream; + +/** + * The chain renderer is used to generate rendering output based on a + * previously layed-out chain. This generally means that renderers are + * tightly coupled to layout managers. This is intentional and the + * decoupling that exists only exists to facilitate multiple renderer + * implementations for multiple output formats (postscript and vml, for + * example). + * + * @see ChainLayout + */ +public interface ChainRenderer +{ + /** + * Generates rendering instructions for the specified chain (and its + * subchains) based on the layout information (dimensions) already + * computed for this chain. + * + * @param chain the chain to be rendered. + * @param out the output stream on which to write the rendering + * instructions. + * @param pointSize the point size of the font to be used when + * rendering this chain. + * @param x the x coordinate at which to render the chain (any + * relative coordinates maintained by the chain should be used as + * offsets to this absolute coordinate). + * @param y the y coordinate at which to render the chain. + */ + public void renderChain (Chain chain, PrintStream out, + int pointSize, int x, int y); +} diff --git a/projects/viztool/src/java/com/samskivert/viztool/layout/Element.java b/projects/viztool/src/java/com/samskivert/viztool/layout/Element.java new file mode 100644 index 00000000..06924f8c --- /dev/null +++ b/projects/viztool/src/java/com/samskivert/viztool/layout/Element.java @@ -0,0 +1,41 @@ +// +// $Id: Element.java,v 1.1 2001/07/13 23:25:13 mdb Exp $ + +package com.samskivert.viztool.viz; + +import java.awt.Point; +import java.awt.Dimension; + +/** + * A page is composed of elements which have some rectangular dimension. + * They can be laid out by ElementLayout implementations in a + * general purpose way. + */ +public interface Element +{ + /** + * Returns the name of this element. + */ + public String getName (); + + /** + * Returns the size of this element. All coordinates are in points. + */ + public Dimension getSize (); + + /** + * Sets the upper left position of this element. All coordinates are + * in points. + */ + public void setLocation (int x, int y); + + /** + * Sets the page number of this element. + */ + public void setPage (int pageno); + + /** + * Returns the page number previously set by setPage. + */ + public int getPage (); +} diff --git a/projects/viztool/src/java/com/samskivert/viztool/layout/ElementLayout.java b/projects/viztool/src/java/com/samskivert/viztool/layout/ElementLayout.java new file mode 100644 index 00000000..c96adfc0 --- /dev/null +++ b/projects/viztool/src/java/com/samskivert/viztool/layout/ElementLayout.java @@ -0,0 +1,25 @@ +// +// $Id: ElementLayout.java,v 1.1 2001/07/13 23:25:13 mdb Exp $ + +package com.samskivert.viztool.viz; + +import java.awt.Dimension; +import java.util.List; + +/** + * The element layout is used to lay a collection of elements out on a + * page. It computes the desired position of each element and sets it via + * setLocation() with the expectation that the location of the + * elements will be used later in the rendering process. + */ +public interface ElementLayout +{ + /** + * Lay out the supplied list of elements. Page numbers should be + * assigned to all elements if the layout spans multiple pages. + * + * @return an array of dimension objects representing the width and + * height of each page that was laid out. + */ + public Dimension[] layout (List elements, int pageWidth, int pageHeight); +} diff --git a/projects/viztool/src/java/com/samskivert/viztool/layout/PackedColumnElementLayout.java b/projects/viztool/src/java/com/samskivert/viztool/layout/PackedColumnElementLayout.java new file mode 100644 index 00000000..644ba835 --- /dev/null +++ b/projects/viztool/src/java/com/samskivert/viztool/layout/PackedColumnElementLayout.java @@ -0,0 +1,104 @@ +// +// $Id: PackedColumnElementLayout.java,v 1.1 2001/07/13 23:25:13 mdb Exp $ + +package com.samskivert.viztool.viz; + +import java.awt.Dimension; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +/** + * Lays out the elements in columns of balanced height with elements + * vertically arranged from tallest to shortest. + */ +public class PackedColumnElementLayout implements ElementLayout +{ + // docs inherited from interface + public Dimension[] layout (List elements, int pageWidth, int pageHeight) + { + // create a new list containing the elements whose order we can + // manipulate willy nilly + Element[] elems = new Element[elements.size()]; + elements.toArray(elems); + Arrays.sort(elems, HEIGHT_COMP); + + // lay out the elements across the page + ArrayList pagedims = new ArrayList(); + int x = 0, y = 0, rowheight = 0, maxwidth = 0, pageno = 0; + for (int i = 0; i < elems.length; i++) { + Dimension size = elems[i].getSize(); + + // see if we fit into this row or not (but force placement if + // we're currently at the left margin) + if ((x > 0) && ((x + size.width) > pageWidth)) { + // track our maxwidth + if (x > maxwidth) { + maxwidth = x; + } + // move down to the next row + x = 0; + y += (rowheight + GAP); + // reset our max rowheight + rowheight = size.height; + } + + // make sure we fit on this page (but force placement if we're + // currently at the top margin) + if ((y > 0) && ((y + size.height) > pageHeight)) { + // make a note of how big the current page is + pagedims.add(new Dimension(maxwidth, y)); + // move to the next page + x = 0; + y = 0; + rowheight = size.height; + maxwidth = 0; + pageno++; + } + + // lay this element out at our current coordinates + elems[i].setLocation(x, y); + elems[i].setPage(pageno); + + // keep track of the maximum row height + if (size.height > rowheight) { + rowheight = size.height; + } + + // advance in the x direction + x += (size.width + GAP); + } + + // make a note of how big the final page is + pagedims.add(new Dimension(maxwidth, y+rowheight)); + Dimension[] dims = new Dimension[pagedims.size()]; + pagedims.toArray(dims); + return dims; + } + + protected static class HeightComparator implements Comparator + { + public int compare (Object o1, Object o2) + { + Element e1 = (Element)o1; + Element e2 = (Element)o2; + + // tallest element wins + int diff = e2.getSize().height - e1.getSize().height; + + // if they are the same height, sort alphabetically + return (diff != 0) ? diff : e1.getName().compareTo(e2.getName()); + } + + public boolean equals (Object other) + { + return (other == this); + } + } + + // hard coded for now, half inch margins + protected static final int GAP = 72/4; + + protected static final Comparator HEIGHT_COMP = new HeightComparator(); +} diff --git a/projects/viztool/src/java/com/samskivert/viztool/layout/PostscriptUtil.java b/projects/viztool/src/java/com/samskivert/viztool/layout/PostscriptUtil.java new file mode 100644 index 00000000..d1b2aa1b --- /dev/null +++ b/projects/viztool/src/java/com/samskivert/viztool/layout/PostscriptUtil.java @@ -0,0 +1,24 @@ +// +// $Id: PostscriptUtil.java,v 1.1 2001/07/13 23:25:13 mdb Exp $ + +package com.samskivert.viztool.viz; + +/** + * Postscript related utility functions. + */ +public class PostscriptUtil +{ + /** + * Estimates the width of the supplied string rendered in a font of + * the specified point size. It does this by assuming the average + * character width is 60% of the point size and multiplies that by the + * length of the supplied string. Not the most accurate mechanism in + * the world but you should only use this for rough estimates and + * should include postscript code to do the right thing (using strwid) + * when actually rendering the page. + */ + public static int estimateWidth (String text, int pointSize) + { + return (pointSize * text.length() * 6) / 10; + } +}