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;
+ }
+}