Mo' betta progress.

git-svn-id: https://samskivert.googlecode.com/svn/trunk@182 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
mdb
2001-07-13 23:25:13 +00:00
parent c807416da4
commit 12b2fab254
16 changed files with 634 additions and 85 deletions
@@ -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());
}
}
@@ -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 ();
}
@@ -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;
}
@@ -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;
}
@@ -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:
*
* <pre>
* Foo
* |
* +-> Bar
* | |
* | +-> Biff
* |
* +-> Baz
* </pre>
*
* It should be used in tandem with the
* <code>CascadingChainRenderer</code>.
*
* @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);
}
}
@@ -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 <code>Dimension</code> 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 <em>not</em> be modified. Oh,
* for a <code>const</code> 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;
}
@@ -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);
@@ -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);
}
@@ -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;
}
@@ -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);
}
}
}
@@ -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;
}
@@ -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);
}
@@ -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 <code>ElementLayout</code> 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 <code>setPage</code>.
*/
public int getPage ();
}
@@ -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
* <code>setLocation()</code> 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);
}
@@ -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();
}
@@ -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;
}
}