//
// $Id: HierarchyVisualizer.java,v 1.8 2001/07/24 18:07:35 mdb Exp $

package com.samskivert.viztool.viz;

import java.awt.*;
import java.awt.geom.Rectangle2D;
import java.awt.print.*;
import java.util.*;

import com.samskivert.viztool.Log;
import com.samskivert.viztool.enum.PackageEnumerator;
import com.samskivert.util.Comparators;

/**
 * The hierarchy visualizer displays inheritance hierarchies in a compact
 * representation so that an entire package can be displayed on a single
 * page (or small number of pages).
 */
public class HierarchyVisualizer implements Printable
{
    /**
     * Constructs a hierarchy visualizer with the supplied enumerator as
     * its source of classes. If the hierarchy enumerator should be
     * limited to a particular set of classes, a filter enumerator should
     * be supplied that returns only the classes to be visualized.
     *
     * @param pkgroot The name of the package that is being visualized.
     * @param enum The enumerator that will return the names of all of the
     * classes in the specified package.
     */
    public HierarchyVisualizer (String pkgroot, Iterator iter)
    {
        // keep track of the package root
        _pkgroot = pkgroot;

        // dump all the classes into an array list so that we can
        // repeatedly scan through the list
        while (iter.hasNext()) {
            // strip out inner classes, we'll catch those via their
            // declaring classes
            String name = (String)iter.next();
            if (name.indexOf("$") != -1) {
                continue;
            }
            _classes.add(name);
        }
        // System.err.println("Scanned " + _classes.size() + " classes.");

        // compile a list of all packages in our collection
        HashSet pkgset = new HashSet();
        iter = _classes.iterator();
        while (iter.hasNext()) {
            pkgset.add(ChainUtil.pkgFromClass((String)iter.next()));
        }

        // sort our package names
        _packages = new String[pkgset.size()];
        iter = pkgset.iterator();
        for (int i = 0; iter.hasNext(); i++) {
            _packages[i] = (String)iter.next();
        }
        Arrays.sort(_packages, Comparators.STRING);
        // System.err.println("Scanned " + _packages.length + " packages.");

        // now create chain groups for each package
        _groups = new ArrayList();
        for (int i = 0; i < _packages.length; i++) {
            PackageEnumerator penum = new PackageEnumerator(
                _packages[i], _classes.iterator(), false);
            _groups.add(new ChainGroup(pkgroot, _packages[i], penum));
        }
    }

    /**
     * Lays out and renders each of the chain groups that make up this
     * package hierarchy visualization.
     */
    public int print (Graphics g, PageFormat pf, int pageIndex)
        throws PrinterException
    {
        Graphics2D gfx = (Graphics2D)g;

        // adjust the stroke
        gfx.setStroke(new BasicStroke(0.1f));

        // only relay things out if the page format has changed
        if (!pf.equals(_format)) {
            // keep this around
            _format = pf;

            // and do the layout
            layout(gfx, pf.getImageableX(), pf.getImageableY(),
                   pf.getImageableWidth(), pf.getImageableHeight());
        }

        // render the groups on the requested page
        int rendered = 0;
        for (int i = 0; i < _groups.size(); i++) {
            ChainGroup group = (ChainGroup)_groups.get(i);

            // skip groups not on this page
            if (group.getPage() != pageIndex) {
                continue;
            }

            Rectangle2D bounds = group.getBounds();
            group.render(gfx, bounds.getX(), bounds.getY());
            rendered++;
        }

        return (rendered > 0) ? PAGE_EXISTS : NO_SUCH_PAGE;
    }

    public void layout (Graphics2D gfx, double x, double y,
                        double width, double height)
    {
        double starty = y;
        int pageno = 0;

        // lay out our groups
        for (int i = 0; i < _groups.size(); i++) {
            ChainGroup group = (ChainGroup)_groups.get(i);

            // lay out the group in question
            ChainGroup ngrp = group.layout(gfx, width, height);
            // if the process of laying this group out caused it to become
            // split across pages, insert this new group into the list
            if (ngrp != null) {
                _groups.add(i+1, ngrp);
            }

            // determine if we need to skip to the next page or not
            Rectangle2D bounds = group.getBounds();
            if ((y > starty) && (y + bounds.getHeight() > height + starty)) {
                y = starty;
                pageno++;
            }

            // assign x and y coordinates to this group
            group.setPosition(x, y);
            // make a note of our page index
            group.setPage(pageno);

            // increment our y location
            y += (bounds.getHeight() + GAP);
        }
    }

    public void paint (Graphics2D gfx, int pageIndex)
    {
        // render the groups on the requested page
        for (int i = 0; i < _groups.size(); i++) {
            ChainGroup group = (ChainGroup)_groups.get(i);

            // skip groups not on this page
            if (group.getPage() != pageIndex) {
                continue;
            }

            Rectangle2D bounds = group.getBounds();
            group.render((Graphics2D)gfx, bounds.getX(), bounds.getY());
        }
    }

    protected String _pkgroot;
    protected ArrayList _classes = new ArrayList();

    protected String[] _packages;
    protected ArrayList _groups;

    protected PageFormat _format;

    protected static final int PAGE_WIDTH = (int)(72 * 7.5);
    protected static final int PAGE_HEIGHT = (int)(72 * 10);

    protected static final int X_MARGIN = 72/2;
    protected static final int Y_MARGIN = 72/2;

    protected static final int GAP = 72/4;
}

@@ -33,17 +33,17 @@
 ava,v 1.
-8
+9
  2001/07
@@ -50,16 +50,16 @@
 /24 
-18:07:35
+21:25:46
  mdb
@@ -1945,32 +1945,295 @@
 )));%0A        %7D%0A%0A
+        // remove the packages on our exclusion list%0A        String expkg = System.getProperty(%22exclude%22);%0A        StringTokenizer tok = new StringTokenizer(expkg, %22:%22);%0A        while (tok.hasMoreTokens()) %7B%0A            pkgset.remove(tok.nextToken());%0A        %7D%0A%0A
         // sort 

Allow the specification of packages to exclude from the output via -Dexclude=pkg:pkg:...