CS计算机代考程序代写 chain cache algorithm compiler c++ Java flex META-INF/MANIFEST.MF

META-INF/MANIFEST.MF

att/grappa/Attribute.java

att/grappa/AttributeHandler.java

att/grappa/CustomRenderer.java

att/grappa/Edge.java

att/grappa/Element.java

att/grappa/ExceptionDisplay.java

att/grappa/Graph.java

att/grappa/GraphEnumeration.java

att/grappa/GraphParserException.java

att/grappa/Grappa.java

att/grappa/GrappaAdapter.java

att/grappa/GrappaBacker.java

att/grappa/GrappaBox.java

att/grappa/GrappaColor.java

att/grappa/GrappaConstants.java

att/grappa/GrappaLine.java

att/grappa/GrappaListener.java

att/grappa/GrappaNexus.java

att/grappa/GrappaPanel.java

att/grappa/GrappaPathIterator.java

att/grappa/GrappaPoint.java

att/grappa/GrappaShape.java

att/grappa/GrappaSize.java

att/grappa/GrappaStyle.java

att/grappa/GrappaSupport.java

att/grappa/GrappaSupportPrintf.java

att/grappa/GrappaSupportRects.java

att/grappa/Lexer.java

att/grappa/Node.java

att/grappa/Parser.cup

att/grappa/Parser.java

att/grappa/Subgraph.java

att/grappa/Symbols.java

att/grappa/Attribute.class

att/grappa/AttributeHandler.class

att/grappa/GrappaConstants.class

att/grappa/CustomRenderer.class

att/grappa/Edge$Enumerator.class

att/grappa/Edge.class

att/grappa/Element.class

att/grappa/Node$Enumerator.class

att/grappa/Node.class

att/grappa/Subgraph$Enumerator.class

att/grappa/Subgraph.class

att/grappa/Graph.class

att/grappa/GrappaNexus.class

att/grappa/GrappaPoint.class

att/grappa/GrappaBox.class

att/grappa/GraphEnumeration.class

att/grappa/GrappaPanel.class

att/grappa/GrappaStyle.class

att/grappa/GrappaBacker.class

att/grappa/GrappaSize.class

att/grappa/GrappaListener.class

att/grappa/ExceptionDisplay$Display.class

att/grappa/ExceptionDisplay$Display$WindowObserver.class

att/grappa/ExceptionDisplay.class

att/grappa/GraphParserException.class

att/grappa/Grappa.class

att/grappa/GrappaAdapter.class

att/grappa/GrappaColor.class

att/grappa/GrappaLine.class

att/grappa/GrappaPathIterator.class

att/grappa/GrappaShape.class

att/grappa/GrappaSupport.class

att/grappa/GrappaSupportPrintf.class

att/grappa/PrintfParser.class

att/grappa/GrappaSupportRects.class

att/grappa/TableField.class

att/grappa/Lexer.class

att/grappa/Parser.class

att/grappa/CUP$Parser$actions.class

att/grappa/Symbols.class

java_cup/runtime/Scanner.java

java_cup/runtime/Symbol.java

java_cup/runtime/lr_parser.java

java_cup/runtime/virtual_parse_stack.java

java_cup/runtime/Symbol.class

java_cup/runtime/lr_parser.class

java_cup/runtime/Scanner.class

java_cup/runtime/virtual_parse_stack.class

Manifest-Version: 1.0
Created-By: 1.4.2_03 (Sun Microsystems Inc.)

att/grappa/Attribute.java
att/grappa/Attribute.java/*
 *  This software may only be used by you under license from AT&T Corp.
 *  (“AT&T”).  A copy of AT&T’s Source Code Agreement is available at
 *  AT&T’s Internet website having the URL:
 *  
 *  If you received this software without first entering into a license
 *  with AT&T, you have an infringing copy of this software and cannot use
 *  it without violating AT&T’s intellectual property rights.
 */

package att.grappa;

import java.util.*;

/**
 * A class used for representing attributes associated with the graph elements.
 * An attribute consists of a name-value pair and an element type.
 * Once an attribute is constructed, the name cannot be changed.
 * The element type and the attribute name are used in determining how a
 * string representation of an attribute value is to be converted to an
 * Object and vice versa. The Element class method setUserAttributeType allows
 * users to take advantage of Grappa’s built-in converters or to pass a
 * (negative) integer as a conversion indicator to a user-supplied
 * AttributeHandler.
 *
 * 


 * 
 * 
 * Grappa String Converters
 * 
 * 

 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
BOX_TYPEatt.grappa.GrappaBox
COLOR_TYPEjava.awt.Color
DIR_TYPEjava.lang.Integer (restricted)
DOUBLE_TYPEjava.lang.Double
FONTSTYLE_TYPEjava.lang.Integer (restricted)
HASHLIST_TYPEjava.lang.Hashtable
INTEGER_TYPEjava.lang.Integer
LINE_TYPEatt.grappa.GrappaLine
POINT_TYPEatt.grappa.GrappaPoint
SHAPE_TYPEjava.lang.Integer (restricted)
SIZE_TYPEatt.grappa.GrappaSize
STRING_TYPEjava.lang.String (default)
STYLE_TYPEatt.grappa.GrappaStyle

 *
 * @see AttributeHandler
 * @see Element#setUserAttributeType
 *
 *
 * @version 1.2, 26 May 2005; Copyright 1996 – 2005 by AT&T Corp.
 * @author  John Mocenigo, Research @ AT&T Labs
 */
public final class Attribute extends java.util.Observable
    implements
    att.grappa.AttributeHandler,
    att.grappa.GrappaConstants
{
    // the custom attribute handler
    private static AttributeHandler specialHandler = null;

    /**
     * Set a custom attribute handler for converting a String value
     * to an Object and vice versa.
     *
     * @param newHandler the AttributeHandler to use for conversions
     *
     * @return the previously set AttributeHandler or null
     *
     * @see AttributeHandler
     */
    public static AttributeHandler setAttributeHandler(AttributeHandler newHandler) {
    AttributeHandler oldHandler = specialHandler;
    specialHandler = newHandler;
    return oldHandler;
    }

    // attribute name
    private String name;
    // attribute value as a string
    private String stringValue;
    // attribute string value converted to an object based on its type
    private Object value;

    // the element type associated with this attribute (includes system)
    private int elementType;
    // the attribute type for conversion to/from a string
    private int attributeType;

    // the hash value of the attribute name
    private int nameHash;

    /**
     * Constructs a new attribute from a name / value pair.
     *
     * @param elemType the element type with which the attribute is 
     *                 or will be associated.
     * @param attrName the name of the attribute.
     * @param attrValue the value of the attribute.
     *
     * @see GrappaConstants#NODE
     * @see GrappaConstants#EDGE
     * @see GrappaConstants#SUBGRAPH
     */
    public Attribute(int elemType, String attrName, Object attrValue) {
    super();
    if(attrName == null) {
        throw new IllegalArgumentException(“the name of an Attribute pair cannot be null”);
    }
    attributeType = attributeType(elemType, attrName);
    elementType = elemType;
    name = attrName;
    nameHash = name.hashCode();
    setValue(attrValue);
    }

    /**
     * Constructs a new attribute from an existing one.
     *
     * @param attr the attribute from which this new one is to be generated
     */
    public Attribute(Attribute attr) {
    this(attr.getElementType(),attr.getName(),attr.getValue());
    }

    /**
     * Get the element type for this attribute.
     *
     * @return the element type for this attribute
     *
     * @see GrappaConstants#NODE
     * @see GrappaConstants#EDGE
     * @see GrappaConstants#SUBGRAPH
     */
    public final int getElementType() {
    return elementType;
    }

    /**
     * Get the attribute value type for this attribute.
     *
     * @return the attribute value type for this attribute.
     */
    public final int getAttributeType() {
    return attributeType;
    }

    /**
     * Get the name of this attribute.
     *
     * @return the name of this attribute.
     */
    public final String getName() {
    return name;
    }

    /**
     * Get the value of this attribute.
     *
     * @return the value of the attribute.
     */
    public final Object getValue() {
    if(value == null && stringValue != null) {
        value = convertStringValue(elementType,name,stringValue,attributeType);
    }
    return value;
    }

    /**
     * Get the value of this attribute converted to a String.
     *
     * @return the value of the attribute as a String.
     */
    public final String getStringValue() {
    switch(attributeType) {
        // put the types here that users might change on their own
        // after doing a getValue() so that we always recompute
        // the string value when it is requested
    case HASHLIST_TYPE:
        stringValue = null;
        break;
    }
    if(stringValue == null && value != null) {
        stringValue = convertValue(elementType,name,value,attributeType);
    }
    return stringValue;
    }

    /**
     * Set the value of the attribute.  If the value is different than the
     * current value, the Observable changed indicator is set.
     *
     * @param attrValue the new attribute value.
     * @return the old attribute value.
     */
    public final Object setValue(Object attrValue) {
    boolean changed = false;
    boolean isString = false;
    Object oldValue = null;
    if(attrValue != null && attrValue instanceof String) {
        isString = true;
        oldValue = getStringValue();
        attrValue = ((String)attrValue).trim();
    } else {
        oldValue = getValue();
    }
    // note: since we have called either getValue() or getStringValue(),
    //       both value and stringValue are up-to-date
    if (attrValue != null) {
        if(isString) {
        if(changed = (stringValue == null || !attrValue.equals(stringValue))) {
            stringValue = (String)attrValue;
            value = null;
        }
        } else {
        if(changed = (value == null || !attrValue.equals(value))) {
            value = copyValue(elementType, name, attrValue,attributeType);
            stringValue = null;
        }
        }
    } else {
        if(changed = (value != null)) {
        value = null;
        stringValue = null;
        }
    }
    if(changed) {
        setChanged();
    }
    return oldValue;
    }

    /**
     * Tests for equality with the given attribute.
     *
     * @param attr the attribute with which to compare this attribute.
     * @return true if the two attributes are equal, false otherwise.
     */
    public final boolean equals(Attribute attr) {
    if(attr == null) {
        return false;
    }
    if(this == attr) {
        return true;
    }
    if(nameHash != attr.getNameHash() || !attr.getName().equals(name)) {
        return false;
    }
    String attrValue = attr.getStringValue();
    if(attrValue == getStringValue()) {
        return true;
    }
    if(attrValue == null) {
        return false;
    }
    // note: since getStringValue() was called, stringValue is up-to-date
    return attrValue.equals(stringValue);
    }

    /**
     * Tests for equality of this attribute’s value with the given attribute’s
     * value. The attribute names are not compated.
     *
     * @param attr the attribute with which to compare this attribute.
     * @return true if the two attribute values are equal, false otherwise.
     */
    public final boolean equalsValue(Attribute attr) {
    if(attr == null) {
        return false;
    }
    if(this == attr) {
        return true;
    }
    String attrValue = attr.getStringValue();
    if(attrValue == getStringValue()) {
        return true;
    }
    if(attrValue == null) {
        return false;
    }
    // note: since getStringValue() was called, stringValue is up-to-date
    return attrValue.equals(stringValue);
    }

    /**
     * Get the hash value for this attributes name.
     *
     * @return the hash code for the name portion of this attribute
     */
    public final int getNameHash() {
    return nameHash;
    }

    /**
     * Use to indicate that this object has changed. 
     * This method is a convenience method that calls the corresponding
     * protected method of the Observable class.
     * 
     * @see java.util.Observable#setChanged()
     */
    public final void setChanged() {
    super.setChanged();
    }

    /**
     * Use to indicate that this object has no longer changed, or that it has
     * already notified all of its observers of its most recent change.
     * This method is a convenience method that calls the corresponding
     * protected method of the Observable class.
     * 
     * @see java.util.Observable#clearChanged()
     */
    public final void clearChanged() {
    super.clearChanged();
    }

    /**
     * Provide a generic string representation of the attribute.
     */
    public String toString() {
    return getClass().getName() + “[name=\””+name+”\”,value=\””+getStringValue()+”\”]”;
    }

    /**
     * Convert the supplied value to a string. How to convert the value is
     * based on the type, name and attrtype information supplied. Note: this
     * method really could be declared static except that it hides the
     * instance method declared in the AttributeHandler interface and a
     * class method cannot hide an instance method.
     *
     * @param type the element type to which the named attribute applies
     * @param name the name of the attribute
     * @param value the object value to be converted to a string
     * @param attrtype the type of the attribute
     * @return a string representation of the supplied value
     */
    public String convertValue(int type, String name, Object value, int attrtype) {
    String stringValue = null;

    switch(attrtype) {
    case BOX_TYPE:
        if(value instanceof GrappaBox) {
        stringValue = ((GrappaBox)value).toAttributeString();
        } else {
        throw new IllegalArgumentException(“value of attribute \”” + name + “\” is not an instance of GrappaBox”);
        }
        break;
    case COLOR_TYPE:
        if(value instanceof java.awt.Color) {
        stringValue = GrappaColor.getColorName((java.awt.Color)value);
        } else {
        throw new IllegalArgumentException(“value of attribute \”” + name + “\” is not an instance of Color”);
        }
        break;
    case DIR_TYPE:
        if(value instanceof Integer) {
        stringValue = GrappaSupport.xlateDir(((Integer)value).intValue());
        } else {
        throw new IllegalArgumentException(“value of attribute \”” + name + “\” is not an instance of Integer”);
        }
        break;
    case DOUBLE_TYPE:
        if(value instanceof Double) {
          stringValue = GrappaSupportPrintf.sprintf(new Object[] { “%g”, value });
        } else {
        throw new IllegalArgumentException(“value of attribute \”” + name + “\” is not an instance of Double”);
        }
        break;
    case FONTSTYLE_TYPE:
        if(value instanceof Integer) {
        stringValue = GrappaSupport.xlateFontStyle(((Integer)value).intValue());
        } else {
        throw new IllegalArgumentException(“value of attribute \”” + name + “\” is not an instance of Integer”);
        }
        break;
    case HASHLIST_TYPE:
        if(value instanceof Hashtable) {
        StringBuffer strbuf = new StringBuffer();
        Enumeration keys = ((Hashtable)value).keys();
        synchronized(strbuf) {
            while(keys.hasMoreElements()) {
            if(strbuf.length() > 0)
                strbuf.append(‘,’);
            strbuf.append((String)(keys.nextElement()));
            }
            stringValue = strbuf.toString();
        }
        } else {
        throw new IllegalArgumentException(“value of attribute \”” + name + “\” is not an instance of Hashtable”);
        }
        break;
    case INTEGER_TYPE:
        if(value instanceof Integer) {
        stringValue = ((Integer)value).toString();
        } else {
        throw new IllegalArgumentException(“value of attribute \”” + name + “\” is not an instance of Integer”);
        }
        break;
    case LINE_TYPE:
        if(value instanceof GrappaLine) {
        stringValue = ((GrappaLine)value).toAttributeString();
        } else {
        throw new IllegalArgumentException(“value of attribute \”” + name + “\” is not an instance of GrappaLine”);
        }
        break;
    case POINT_TYPE:
        if(value instanceof GrappaPoint) {
        stringValue = ((GrappaPoint)value).toAttributeString();
        } else {
        throw new IllegalArgumentException(“value of attribute \”” + name + “\” is not an instance of GrappaPoint”);
        }
        break;
    case SHAPE_TYPE:
        if(value instanceof Integer) {
        stringValue = (String)Grappa.shapeToKey.get(value);
        } else {
        throw new IllegalArgumentException(“value of attribute \”” + name + “\” is not an instance of Integer”);
        }
        break;
    case SIZE_TYPE:
        if(value instanceof GrappaSize) {
        stringValue = ((GrappaSize)value).toAttributeString();
        } else {
        throw new IllegalArgumentException(“value of attribute \”” + name + “\” is not an instance of GrappaSize”);
        }
        break;
    case STRING_TYPE:
        if(value instanceof String) {
        stringValue = (String)value;
        } else {
        throw new IllegalArgumentException(“value of attribute \”” + name + “\” is not an instance of String”);
        }
        break;
    case STYLE_TYPE:
        if(value instanceof GrappaStyle) {
        stringValue = ((GrappaStyle)value).toAttributeString();
        } else {
        throw new IllegalArgumentException(“value of attribute \”” + name + “\” is not an instance of GrappaStyle”);
        }
        break;
    default:
        if(Attribute.specialHandler != null) {
        stringValue = Attribute.specialHandler.convertValue(type, name, value, attrtype);
        } else {
        throw new RuntimeException(Element.typeString(type,true) + ” attribute (” + name + “) needs a special handler”);
        }
    }

    if(stringValue == null && value != null) {
        if(value instanceof String) {
        stringValue = (String)value;
        } else {
        throw new RuntimeException(“AttributeHandler needed to perform conversion of attribute \”” + name + “\”, please supply one via Attribute.setAttributeHandler()”);
        }
    }

    return stringValue;
    }

    /**
     * Convert the supplied string value to the appropriate Object.
     * How to convert the value is
     * based on the type, name and attrtype information supplied. Note: this
     * method really could be declared static except that it hides the
     * instance method declared in the AttributeHandler interface and a
     * class method cannot hide an instance method.
     *
     * @param type the element type to which the named attribute applies
     * @param name the name of the attribute
     * @param value the string value to be converted to an object
     * @param attrtype the type of the attribute
     * @return an object representation of the supplied value
     */
    public Object convertStringValue(int type, String name, String stringValue, int attrtype) {
    Object value = null;

    if(stringValue == null || (stringValue != null && attrtype != STRING_TYPE && stringValue.trim().length() == 0)) {
      value = null;
      stringValue = null;
    } else {

        switch(attrtype) {
        case BOX_TYPE:
        // stringValue is x1, y1, x2, y2
        value = new GrappaBox(stringValue, false);
        break;
        case COLOR_TYPE:
        value = GrappaColor.getColor(stringValue,null);
        break;
        case DIR_TYPE:
        value = new Integer(GrappaSupport.xlateDirString(stringValue));
        break;
        case DOUBLE_TYPE:
        try {
            value = Double.valueOf(stringValue);
        }
        catch(NumberFormatException nfe) {
            throw new IllegalArgumentException(“bad number format (” + stringValue + “) for attribute \”” + name + “\””);
        }
        break;
        case FONTSTYLE_TYPE:
        value = new Integer(GrappaSupport.xlateFontStyleString(stringValue));
        break;
        case HASHLIST_TYPE:
        String[] listvals = GrappaSupport.strsplit(stringValue);
        if(this.value != null && this.value instanceof Hashtable) {
            // is this more efficient than creating a new one??
            // does this introduce the danger of users being
            // tempted to hold on to the value??
            value = this.value;
            ((Hashtable)value).clear();
        } else {
            value = new Hashtable();
        }
        for(int i=0; i
 *  If you received this software without first entering into a license
 *  with AT&T, you have an infringing copy of this software and cannot use
 *  it without violating AT&T’s intellectual property rights.
 */

package att.grappa;

import java.util.Hashtable;

/**
 * An interface for methods that perform attribute value conversions.
 *
 * @version 1.2, 26 May 2005; Copyright 1996 – 2005 by AT&T Corp.
 * @author  John Mocenigo, Research @ AT&T Labs
 */
public interface AttributeHandler
{
    /**
     * Convert the supplied value to a string. How to convert the value is
     * based on the type, name and attrtype information supplied. Note: this
     * method really could be declared static except that it hides the
     * instance method declared in the AttributeHandler interface and a
     * class method cannot hide an instance method.
     *
     * @param elemType the element type to which the named attribute applies
     * @param name the name of the attribute
     * @param value the object value to be converted to a string
     * @param convType the object-to-string conversion type of the value object
     * @return a string representation of the supplied value
     */
    public String convertValue(int elemType, String name, Object value, int convType);

    /**
     * Convert the supplied string value to the appropriate Object.
     * How to convert the value is
     * based on the type, name and attrtype information supplied. Note: this
     * method really could be declared static except that it hides the
     * instance method declared in the AttributeHandler interface and a
     * class method cannot hide an instance method.
     *
     * @param elemType the element type to which the named attribute applies
     * @param name the name of the attribute
     * @param value the string value to be converted to an object
     * @param convType the string-to-object conversion type of the value object
     * @return an object representation of the supplied value
     */
    public Object convertStringValue(int elemType, String name, String stringValue, int convType);

    /**
     * Make a copy of the supplied value. How to copy the value is
     * based on the type, name and attrtype information supplied. Note: this
     * method really could be declared static except that it hides the
     * instance method declared in the AttributeHandler interface and a
     * class method cannot hide an instance method.
     *
     * @param elemType the element type to which the named attribute applies
     * @param name the name of the attribute
     * @param value the attribute value to be copied
     * @param convType the conversion type of the value object
     * @return a copy of the supplied value
     */
    public Object copyValue(int elemType, String name, Object value, int convType);
}

att/grappa/CustomRenderer.java
att/grappa/CustomRenderer.java/*
 *  This software may only be used by you under license from AT&T Corp.
 *  (“AT&T”).  A copy of AT&T’s Source Code Agreement is available at
 *  AT&T’s Internet website having the URL:
 *  
 *  If you received this software without first entering into a license
 *  with AT&T, you have an infringing copy of this software and cannot use
 *  it without violating AT&T’s intellectual property rights.
 */

package att.grappa;

/**
 * An interface for describing the drawing of custom shapes that cannot
 * be captured via a single GeneralPath. This interface would generally
 * be used when the Attribute SHAPE_ATTR=custom and CUSTOM_ATTR is set to
 * the name of a user provided class which would be an extension of
 * GrappaShape and implements this interface. Note that if the custom shape
 * desired by the user can be expressed as a single general path, then there
 * is no need to use this interface or provide the methods it requires.
 *
 * @version 1.2, 26 May 2005; Copyright 1996 – 2005 by AT&T Corp.
 * @author  John Mocenigo, Research @ AT&T Labs
 */
public interface CustomRenderer
{
    /**
     * The method called when the element needs to be drawn.
     * When used with an extention of GrappaShape,
     * the default behavior is obtained by:
     * 

     * public void draw(java.awt.Graphics2D g2d) {
     *   g2d.draw(this);
     * }
     * 

     *
     * @param g2d the Graphics2D context to be used for drawing
     */
    public void draw(java.awt.Graphics2D g2d);

    /**
     * The method called when the element needs to be filled.
     * When used with an extention of GrappaShape,
     * the default behavior is obtained by:
     * 

     * public void fill(java.awt.Graphics2D g2d) {
     *   g2d.fill(this);
     * }
     * 

     *
     * @param g2d the Graphics2D context to be used for drawing
     */
    public void fill(java.awt.Graphics2D g2d);

    /**
     * The method called when the element needs to draw its background
     * image.
     * When used with an extention of GrappaShape that provides
     * the underlying element as a global variable, the default behavior
     * is obtained by:
     * 

     * public void drawImage(java.awt.Graphics2D g2d) {
     *   Rectangle sbox = this.getBounds();
     *   Shape clip = g2d.getClip();
     *   g2d.clip(this);
     *   g2d.drawImage(element.getGrappaNexus().getImage(), sbox.x, sbox.y, sbox.width, sbox.height, null);
     *   g2d.setClip(clip);
     * }
     * 

     *
     * @param g2d the Graphics2D context to be used for drawing
     */
    public void drawImage(java.awt.Graphics2D g2d);

}

att/grappa/Edge.java
att/grappa/Edge.java/*
 *  This software may only be used by you under license from AT&T Corp.
 *  (“AT&T”).  A copy of AT&T’s Source Code Agreement is available at
 *  AT&T’s Internet website having the URL:
 *  
 *  If you received this software without first entering into a license
 *  with AT&T, you have an infringing copy of this software and cannot use
 *  it without violating AT&T’s intellectual property rights.
 */

package att.grappa;

import java.util.*;
import java.io.*;

/**
 * This class describes an edge.
 *
 * @version 1.2, 26 May 2005; Copyright 1996 – 2005 by AT&T Corp.
 * @author  John Mocenigo, Research @ AT&T Labs
 */
public class Edge extends Element
{
    /**
     * Default edge name prefix used by setName().
     *
     * @see Edge#setName()
     */
    public final static String defaultNamePrefix = “E”;

    /*
     * end nodes (port Ids are not used yet)
     */
    private Node headNode;
    private String headPortId = null;
    private Node tailNode;
    private String tailPortId = null;
    private String key = null;

    /*
     * direction info (adjusted here and by GrappaNexus)
     */
    int direction = GrappaLine.NONE_ARROW_EDGE;

    /**
     * Use this constructor when creating an edge.
     *
     * @param subg the parent subgraph.
     * @param tail node anchoring the tail of the edge.
     * @param head node anchoring the head of the edge.
     */
    public Edge(Subgraph subg, Node tail, Node head) {
    this(subg,tail,null,head,null,null,null);
    }

    /**
     * Use this constructor when creating an edge with ports.
     *
     * @param subg the parent subgraph.
     * @param tail node anchoring the tail of the edge.
     * @param tailPort the port to use within the tail node.
     * @param head node anchoring the head of the edge.
     * @param headPort the port to use within the head node.
     */
    public Edge(Subgraph subg, Node tail, String tailPort, Node head, String headPort) {
    this(subg,tail,tailPort,head,headPort,null,null);
    }

    /**
     * Use this constructor when creating an edge requiring a key to distinguish it.
     *
     * @param subg the parent subgraph.
     * @param tail node anchoring the tail of the edge.
     * @param tailPort the port to use within the tail node.
     * @param head node anchoring the head of the edge.
     * @param headPort the port to use within the head node.
     * @param key identifier (used in conjection with tail/head, but not ports) to uniquely define edge (and prevent unwanted duplicate from being created)
     */
    public Edge(Subgraph subg, Node tail, String tailPort, Node head, String headPort, String key) throws RuntimeException {
    this(subg,tail,tailPort,head,headPort,key,null);
    }

    /**
     * Use this constructor when creating an edge with a supplied unique name for easy look-up (the name is also used as the key).
     *
     * @param subg the parent subgraph.
     * @param tail node anchoring the tail of the edge.
     * @param head node anchoring the head of the edge.
     * @param name identifier to uniquely define edge within the entire graph (reather than just between head/tail pairs)
     */
    public Edge(Subgraph subg, Node tail, Node head, String name) throws RuntimeException {
    this(subg,tail,null,head,null,null,name);
    }

    /**
     * Use this constructor when creating an edge requiring a key to distinguish it and a supplied lookup name.
     * When name is null, it is automatically generated. When key is null, it is automatically generated or set to name, if it was supplied.
     *
     * @param subg the parent subgraph.
     * @param tail node anchoring the tail of the edge.
     * @param tailPort the port to use within the tail node.
     * @param head node anchoring the head of the edge.
     * @param headPort the port to use within the head node.
     * @param key identifier (used in conjection with tail/head, but not ports) to uniquely define edge (and prevent unwanted duplicate from being created)
     * @param name a unique name that can be used for lookup (if null, automatically generated)
     */
    public Edge(Subgraph subg, Node tail, String tailPort, Node head, String headPort, String key, String name) throws RuntimeException {
    super(Grappa.EDGE,subg);
    boolean directed = subg.getGraph().isDirected();

    if(directed)
        direction = GrappaLine.TAIL_ARROW_EDGE;
    else
        direction = GrappaLine.NONE_ARROW_EDGE;

    if(subg.getGraph().isStrict()) {
        if(tail == head) {
        throw new RuntimeException(“cannot create self-looping edge in a strict graph (” + tail.getName() + (directed?”->”:”–“) + head.getName() + “)”);
        } else {
        Enumeration enm = Edge.findEdgesByEnds(tail,head);
        if(enm.hasMoreElements()) {
            if(!directed) {
            throw new RuntimeException(“cannot create multiple edges between the same nodes in a strict graph”);
            } else {
            Edge tmpedge = null;
            while(enm.hasMoreElements()) {
                tmpedge = (Edge)enm.nextElement();
                if(tmpedge.getHead() == head && tmpedge.getTail() == tail) {
                throw new RuntimeException(“cannot create multiple edges between the same nodes in the same direction in a strict directed graph”);
                }
            }
            }
        }
        }
    }
    if(!directed && tail.getId() > head.getId()) {
        Node tmpNode = tail;
        tail = head;
        head = tmpNode;
        String tmpPort = tailPort;
        tailPort = headPort;
        headPort = tmpPort;
    }
    tailNode = tail;
    if(tailPort != null) {
        tailPortId = new String(tailPort);
    }
    headNode = head;
    if(headPort != null) {
        headPortId = new String(headPort);
    }
    if(name != null) {
        if(subg.getGraph().findEdgeByName(name) != null) {
        throw new RuntimeException(“cannot create edge with duplicate name ‘” + name + “‘ (” + tailNode.getName() + ” -> ” + headNode.getName() + “)”);
        }
        this.name = name;
        subg.addEdge(this);
        if(key == null) {
        key = name;
        }
    } else {
        setName();
    }
    if(key == null) {
        if(headPort != null && tailPort != null) {
        this.key = tailPort + “::” + headPort;
        } else if(headPort != null) {
        this.key = “::” + headPort;
        } else if(tailPort != null) {
        this.key = tailPort + “::”;
        } else {
        this.key = name;
        }
    } else {
        this.key = key;
    }
    if(this.key != null) {
        if(findEdgeByKey(tailNode,headNode,this.key) != null) {
        subg.removeEdge(this.name);
        throw new RuntimeException(“cannot create duplicate edge (” + tailNode.getName() + (directed?”->”:”–“) + headNode.getName() + “) with key ‘” + this.key + “‘”);
        }
    }
    tailNode.addEdge(this,false);
    headNode.addEdge(this,true);

    edgeAttrsOfInterest();
    }

    // a listing of the attributes of interest for Edges
    private void edgeAttrsOfInterest() {
    attrOfInterest(POS_ATTR);
    attrOfInterest(DIR_ATTR);
    attrOfInterest(LP_ATTR);
    attrOfInterest(HEADLABEL_ATTR);
    attrOfInterest(HEADLP_ATTR);
    attrOfInterest(TAILLABEL_ATTR);
    attrOfInterest(TAILLP_ATTR);
    attrOfInterest(STYLE_ATTR);
    }

    /**
     * Returns the edge with the given tail node, head node and key.
     *
     * @param tail the tail node of the desired edge.
     * @param head the head node of the desired edge.
     * @param key the key specifying the desired edge.
     * @return the Edge matching the arguments or null, if there is no match.
     * @see Edge#findEdgesByEnds
     */
    public static Edge findEdgeByKey(Node tail, Node head, String key) {
    if(tail == null || head == null || key == null) {
        return(null);
    }
    return tail.findOutEdgeByKey(head,key);
    }

    /**
     * Check if this element is an edge.
     * Useful for testing the subclass type of a Element object.
     *
     * @return true if this object is a Edge.
     */
    public boolean isEdge() {
    return(true);
    }

    /**
     * Get the type of this element.
     * Useful for distinguishing Element objects.
     *
     * @return the class variable constant Grappa.EDGE.
     * @see Grappa
     */
    public int getType() {
    return(Grappa.EDGE);
    }

    /**
     * Generates and sets the name for this edge.
     * The generated name is the concatenation of tail node name,
     * the separator “>>”, the head node name, the separator “##”,
     * and the id of this edge Instance.
     * Also, takes the opportunity to add the edge to the subgraph and node
     * dictionaries.
     * Implements the abstract Element method.
     *
     * @see Element#getId()
     */
    void setName() {
    String oldName = name;
    
    while(true) {
        name = Edge.defaultNamePrefix + getId() + “_” + System.currentTimeMillis();
        if(getGraph().findEdgeByName(name) == null) {
        break;
        }
    }

    // update subgraph edge dictionary
    if(oldName != null) {
        getSubgraph().removeEdge(oldName);
    }
    getSubgraph().addEdge(this);

    canonName = null;
    }

    /**
     * Get the key for this edge.
     *
     * @return the key of the edge
     */
    public String getKey() {
    return key;
    }

    /**
     * Get the node at the head end of the edge.
     *
     * @return the head node of the edge
     */
    public Node getHead() {
    return headNode;
    }

    /**
     * Get the head port id of the edge.
     *
     * @return the head port id of the edge
     */
    public String getHeadPortId() {
    return headPortId;
    }

    /**
     * Get the node at the tail end of the edge.
     *
     * @return the tail node of the edge
     */
    public Node getTail() {
    return tailNode;
    }

    /**
     * Get the tail port id of the edge.
     *
     * @return the tail port id of the edge
     */
    public String getTailPortId() {
    return tailPortId;
    }

    /**
     * Get the String rendition of the edge.
     *
     * @return the string rendition of the edge, quoted as needed.
     */
    public String toString() {
    if(canonName == null) {
        String tail = null;
        String head = null;

        if(tailPortId == null) {
        tail = tailNode.toString();
        } else {
        tail = tailNode.toString() + “:” + canonString(tailPortId);
        }
        if(headPortId == null) {
        head = headNode.toString();
        } else {
        head = headNode.toString() + “:” + canonString(headPortId);
        }

        if(getGraph().isDirected()) {
        canonName = tail + ” -> ” + head;
        } else {
        canonName = tail + ” — ” + head;
        }
    }
    return(canonName);
    }

    /**
     * Print the edge description to the provided stream.
     *
     * @param out the output stream for writing the description.
     */
    public void printEdge(PrintWriter out) {
    this.printElement(out);
    }

    /**
     * Check if the edge connects in the forward direction.
     *
     * @return true when edge connects in the forward direction (tail to head)
     */
    public boolean goesForward() {
    return(direction != GrappaLine.HEAD_ARROW_EDGE);
    }

    /**
     * Check if the edge connects in the reverse direction.
     *
     * @return true when edge connects in the reverse direction (head to tail)
     */
    public boolean goesReverse() {
    return(direction != GrappaLine.TAIL_ARROW_EDGE);
    }

    /**
     * Returns the attribute conversion type for the supplied attribute name.
     * After edge specific attribute name/type mappings are checked, mappings
     * at the element level are checked.
     *
     * @param attrname the attribute name
     * @return the currently associated attribute type
     */
    public static int attributeType(String attrname) {
    int convtype = -1;
    int hashCode;

    if(attrname != null) {
        hashCode = attrname.hashCode();

        if(hashCode == POS_HASH && attrname.equals(POS_ATTR)) {
        convtype = LINE_TYPE;
        } else if(hashCode == MINLEN_HASH && attrname.equals(MINLEN_ATTR)) {
        convtype = INTEGER_TYPE;
        } else if(hashCode == DIR_HASH && attrname.equals(DIR_ATTR)) {
        convtype = DIR_TYPE;
        } else if(hashCode == WEIGHT_HASH && attrname.equals(WEIGHT_ATTR)) {
        convtype = DOUBLE_TYPE;
        } else if(hashCode == HEADLABEL_HASH && attrname.equals(HEADLABEL_ATTR)) {
        convtype = STRING_TYPE;
        } else if(hashCode == HEADLP_HASH && attrname.equals(HEADLP_ATTR)) {
        convtype = POINT_TYPE;
        } else if(hashCode == TAILLABEL_HASH && attrname.equals(TAILLABEL_ATTR)) {
        convtype = STRING_TYPE;
        } else if(hashCode == TAILLP_HASH && attrname.equals(TAILLP_ATTR)) {
        convtype = POINT_TYPE;
        } else {
        return(Element.attributeType(attrname));
        }
    }

    return(convtype);
    }

    /**
     * Returns an enumeration of edges that have one end fixed at node1
     * and the other end at node2.  If node2 is empty, an enumeration of
     * all edges attached to node1 is returned.
     *
     * @param node1 one vertex of the set of edges to be returned
     * @param node2 the other vertex of the set of edges to be returned,
     *              or null for no constraint on the other vertex
     * @return an enumeration of Edge objects.
     */
    public static Enumeration findEdgesByEnds(Node node1, Node node2) {
    if(node1 == null) {
        return Grappa.emptyEnumeration.elements();
    }
    return new Enumerator(node1,node2);
    }

    static class Enumerator implements Enumeration {
    Node node1 = null;
    Node node2 = null;
    Edge next = null;
    Enumeration outEdges = null;
    Enumeration inEdges = null;

    Enumerator(Node node1, Node node2) {
        this.node1 = node1;
        this.node2 = node2;
        if(node1 != null) {
        this.outEdges = node1.outEdgeElements();
        this.inEdges = node1.inEdgeElements();
        next = getNext();
        }
    }

    private Edge getNext() {
        Edge tmpEdge = null;
        if(outEdges != null) {
        while(outEdges.hasMoreElements()) {
            tmpEdge = (Edge)outEdges.nextElement();
            if(node2 == null || tmpEdge.getHead() == node2) {
            return tmpEdge;
            }
        }
        outEdges = null;
        }
        if(inEdges != null) {
        while(inEdges.hasMoreElements()) {
            tmpEdge = (Edge)inEdges.nextElement();
            if(node2 == null || tmpEdge.getTail() == node2) {
            return tmpEdge;
            }
        }
        inEdges = null;
        }
        return null;
    }
  
    public boolean hasMoreElements() {
        return(next != null);
    }

    public Object nextElement() {
        if(next == null) {
        throw new NoSuchElementException(“Node$Enumerator”);
        }
        Edge edge = next;
        next = getNext();
        return edge;
    }
    }
}

att/grappa/Element.java
att/grappa/Element.java/*
 *  This software may only be used by you under license from AT&T Corp.
 *  (“AT&T”).  A copy of AT&T’s Source Code Agreement is available at
 *  AT&T’s Internet website having the URL:
 *  
 *  If you received this software without first entering into a license
 *  with AT&T, you have an infringing copy of this software and cannot use
 *  it without violating AT&T’s intellectual property rights.
 */

package att.grappa;

import java.io.*;
import java.util.*;

/**
 * This abstract class is the root class for the
 * Node,
 * Edge,
 * Subgraph and
 * Graph classes.
 * It is the basis for describing the graph elements.
 *
 * @version 1.2, 26 May 2005; Copyright 1996 – 2005 by AT&T Corp.
 * @author  John Mocenigo, Research @ AT&T Labs
 */
public abstract class Element
    implements att.grappa.GrappaConstants
{
    // the containing graph and the parent subgraph element
    private Graph graph = null;
    private Subgraph subgraph = null;

    // used for checking whether we’ve been here for dfs/bfs
    long visastamp = -1L;

    /*
     * These (deleteCalled and busy) used by reserve/release/setDelete to
     * queue a delete request during a critical function (e.g., drawing) and
     * to define the start and end of that critical function.
     */
    private boolean deleteCalled = false;
    private boolean busy = false;

    /*
     * A look-up table that allows a user (via setUserAttributeType) to
     * associate a String-to-Object and vice versa translation for
     * attributes they may supply.
     */
    private static Hashtable userAttributeTypeMap = null;

    /**
     * A general-purpose object not used by Grappa and intended for
     * application writers to attach whatever they want to an Element
     * without the need for extending the class.
     */
    public Object object;

    /**
     * Indicates whether this element should be considered visible or not.
     * If not visible, it will not be drawn and will not be selected by a
     * mouse click (if the supplied selection methods are used). Note that
     * there is redundancy here with the invis component of the
     * style attribute. This value must be true and invis
     * must be false for the element to be visible.
     */
    public boolean visible = true;

    /**
     * Indicates whether this element should be considered selectable or not.
     * The default is true.
     */
    public boolean selectable = true;

    /**
     * Indicates indicates line width for element (for nodes or subgraphs,
     * it is the outline width, when applicable).
     * The default is 0 (single pixel).
     */
    public int linewidth = 0;

    /**
     * Indicates whether only the list of attributes found in the
     * PRINTLIST_ATTR should be printed.
     * The default is false.
     */
    public static boolean usePrintList              = false;

    /**
     * A convenience variable, not used by Grappa, but available to keep
     * track of or otherwise mark graph elements when traversing a graph.
     */
    public int counter = 0;

    /**
     * Determines the type of highlighting to apply, if any, when drawing.
     * Currently recognized choices are SELECTION_MASK and DELETION_MASK. 
     */
    public int highlight = 0;

    // identification
    private Long idKey = null;
    String name = null;

    // attributes
    Hashtable attributes = null;

    // attributes
    Hashtable attrsOfInterest = null;

    // the Shape for drawing
    GrappaNexus grappaNexus = null;

    /**
     * Boolean to indicate if all of this element’s attributes should
     * be printed. Either this flag or the elementPrintAllAttributes
     * can turn on printing of all attributes.
     *
     * @see Subgraph#printSubgraph
     * @see Grappa#elementPrintAllAttributes
     */
    public boolean printAllAttributes = false;
    /**
     * Boolean to indicate if the default attributes associated with
     * this element should be printed. Naturally, this option only
     * is effective if the element is a subgraph.
     *
     * @see Subgraph#printSubgraph
     * @see Grappa#elementPrintDefaultAttributes
     */
    public boolean printDefaultAttributes = false;

    // canonical name
    String canonName = null;

    /**
     * Element constructor needed only during init phase of
     * Graph class.
     * Since the Element class is abstact, it cannot be instantiated directly.
     */
    protected Element() {
    // needed due to Graph init (a special case of Subgraph)
    }
  
    /**
     * Element constructor used during init phase of the
     * Node,
     * Edge and
     * Subgraph classes.
     * Since the Element class is abstact, it cannot be instantiated directly.
     *
     * @param type the type of the element (Grappa.NODE, Grappa.EDGE or Grappa.SUBGRAPH).
     * @param subg the subgraph containing this element.
     *
     * @see GrappaConstants#NODE
     * @see GrappaConstants#EDGE
     * @see GrappaConstants#SUBGRAPH
     */
    protected Element(int type, Subgraph subg) {
    //super();
    setSubgraph(subg);
    setGraph(subg.getGraph());
    setIdKey(type);
    getGraph().addIdMapping(this);

    elementAttrsOfInterest();
    }

    // set the attributes of interest to all elements
    private void elementAttrsOfInterest() {
    attrOfInterest(COLOR_ATTR);
    attrOfInterest(FONTCOLOR_ATTR);
    attrOfInterest(FONTNAME_ATTR);
    attrOfInterest(FONTSIZE_ATTR);
    attrOfInterest(FONTSTYLE_ATTR);
    attrOfInterest(LABEL_ATTR);
    }
    
    /**
     * Get the type of this Element. Useful for distinguishing Element objects.
     *
     * @return the appropriate class variable constant
     *
     * @see GrappaConstants#NODE
     * @see GrappaConstants#EDGE
     * @see GrappaConstants#SUBGRAPH
     */
    public abstract int getType();

    /**
     * Check if this Element is a node. Overridden in Node to return true.
     *
     * @return false, unless overridden.
     *
     * @see Node#isNode()
     */
    public boolean isNode() {
    return(false);
    }

    /**
     * Check if this Element is an edge. Overridden in Edge to return true.
     *
     * @return false, unless overridden.
     *
     * @see Edge#isEdge()
     */
    public boolean isEdge() {
    return(false);
    }

    /**
     * Check if this Element is a subgraph. Overridden in Subgraph to return true.
     *
     * @return false, unless overridden.
     *
     * @see Subgraph#isSubgraph()
     */
    public boolean isSubgraph() {
    return(false);
    }

    /**
     * Intended to be a subclass-specific name generating method.
     * Used by edges and when nodes or graphs are created without
     * an explicit name.
     * Note that graphs and nodes should also have a setName that
     * takes an explicit name as an argument.
     */
    abstract void setName();

    /**
     * Get the name of this Element.
     *
     * @return the name of the element.
     */
    public String getName() {
    return(name);
    }

    /**
     * Check if this Element can be reserved, otherwise queue request
     *
     * @return true, if successfully reserved.
     */
    boolean reserve() {
    return setReserved(true, false);
    }

    /**
     * Release the reservation on this Element, if any.
     */
    void release() {
    setReserved(false, false);
    }

    /**
     * Queue or unqueue a delete request.
     *
     * @return true, unless a delete request has already been queued
     *         for this Element.
     */
    boolean setDelete(boolean delete) {
    return setReserved(delete, true);
    }

    // handle the reserve/release/setDelete requests
    private synchronized boolean setReserved(boolean state, boolean isDelete) {
    if(isDelete) {
        if(state) {
        deleteCalled = true;
        if(busy) {
            return(false);
        } else {
            return(busy = true);
        }
        } else {
        deleteCalled = busy = false;
        return(true);
        }
    } else if(state) {
        if(deleteCalled) return(false);
        return(busy = true);
    } else {
        if(!deleteCalled) {
        busy = false;
        } else {
        busy = deleteCalled = false;
        delete();
        }
        return(true);
    }
    }

    /**
     * Add the name of an attribute of interest to this element
     *
     * @param name the name of the attribute
     */
    protected void attrOfInterest(String name) {
    if(name == null || isOfInterest(name)) return;
    if(attrsOfInterest == null) {
        attrsOfInterest = new Hashtable();
    }
    attrsOfInterest.put(name,name);
    if(grappaNexus != null) {
        Attribute attr = getAttribute(name);
        if(attr != null) {
        attr.addObserver(grappaNexus);
        }
    }
    }

    /**
     * Remove the name of an attribute of interest to this object
     *
     * @param name the name of the attribute
     */
    protected void attrNotOfInterest(String name) {
    if(name == null || !isOfInterest(name)) return;
    if(grappaNexus != null) {
        Attribute attr = getAttribute(name);
        if(attr != null) attr.deleteObserver(grappaNexus);
    }
    attrsOfInterest.remove(name);
    }

    /**
     * Provide an enumeration of the names of the attributes of interest.
     *
     * @return an enumeration of attribute names that are of interest
     */
    public Enumeration listAttrsOfInterest() {
    if(attrsOfInterest == null) return Grappa.emptyEnumeration.elements();
    return attrsOfInterest.elements();
    }

    /**
     * Check if the name of an attribute of interest to this object
     *
     * @param name the name of the attribute
     * @return true when the name is of interest
     */
    public boolean isOfInterest(String name) {
    if(name == null || attrsOfInterest == null) return false;
    return attrsOfInterest.contains(name);
    }
  
    /**
     * Sets or creates an attribute for this element from the attribute supplied.
     * The storage key is the attribute name.  If the value portion of the
     * supplied attribute is null, then the attribute will be removed from the
     * element.
     *
     * @param attr the attribute from which to set the element’s attribute.
     * @return the value of the (local) attribute previously stored
     *         under the same name
     */
    public Object setAttribute(Attribute attr) {
    if(attr == null) {
        return null;
    }
    return setAttribute(attr.getName(),attr.getValue());
    }
  
    /**
     * Sets or creates an attribute for this element from the supplied
     * arguments. The storage key is the attribute name.  If the value
     * argument is null, then the attribute will be removed from the element.
     *
     * @param name the attribute name
     * @param value the attribute value
     * @return the value of the (local) attribute previously stored
     *         under the same name
     */
    public Object setAttribute(String name, Object value) {
    if(attributes == null) {
        attributes = new Hashtable();
    }
    if(name == null) {
        throw new IllegalArgumentException(“cannot set an attribute using a null name”);
    }

    Object oldValue = null;
    Attribute crntAttr =  getLocalAttribute(name);
    if(crntAttr == null) {
        if(value == null) {
        return null;
        } else if(value instanceof String && ((String)value).trim().length() == 0 && Attribute.attributeType(getType(),name) != STRING_TYPE) {
        return null;
        }
        attributes.put(name,(crntAttr = new Attribute(getType(),name,value)));
        if(grappaNexus != null && isOfInterest(name)) {
        crntAttr.addObserver(grappaNexus);
        }
    } else {
        oldValue = crntAttr.getValue();
        if(value == null) {
        //System.err.println(“direct removal of (“+name+”,”+value+”) from “+getName());
        removeAttribute(name);
        return oldValue;
        } else if(value instanceof String && ((String)value).trim().length() == 0 && Attribute.attributeType(getType(),name) != STRING_TYPE) {
        //System.err.println(“removal of (“+name+”,”+value+”) from “+getName());
        removeAttribute(name);
        return oldValue;
        } else {
        crntAttr.setValue(value);
        }
    }
    if(crntAttr.hasChanged()) {
        crntAttr.notifyObservers(new Long(System.currentTimeMillis()));
    }
    return oldValue;
    }

  
    /*
     * Removes the named attribute from the (local) attribute table and
     * applies the default attribute (if any)
     *
     * @param name the name of the attribute to be removed.
     * @return the default attribute pair for this attribute.
     */
    private Attribute removeAttribute(String name) {
    if(name == null) return null;
    Attribute dfltAttr =  getDefaultAttribute(name);
    Attribute attr = null;
    if(attributes != null) attr = (Attribute)attributes.remove(name);
    if(attr == null) return dfltAttr;
    if(dfltAttr == null) {
        attr.setValue(“”);
    }
    attr.setChanged();
    attr.notifyObservers(new Object[] { dfltAttr, new Long(System.currentTimeMillis()) });
    return dfltAttr;
    }
  
    /**
     * Sets or creates a default attribute for this element type within the
     * containing subgraph of this element from the supplied arguments.
     * The storage key is the attribute name.
     * If the value argument is null, then the
     * attribute will be removed from the subgraph.
     *
     * @param name the attribute name
     * @param value the attribute value
     * @return the value of the (default) attribute previously stored
     *         under the same name
     */
    public Object setDefaultAttribute(String name, Object value) {
    return setDefaultAttribute(getType(),name,value);
    }
  
    /**
     * Sets or creates a default attribute of the specified type within the
     * containing subgraph of this element from the supplied arguments.
     * The storage key is the attribute name.
     * If the value argument is null, then the
     * attribute will be removed from the subgraph.
     *
     * @param type the default attribute type
     * @param name the attribute name
     * @param value the attribute value
     * @return the value of the (default) attribute previously stored
     *         under the same name
     */
    public Object setDefaultAttribute(int type, String name, Object value) {
    Object oldValue = null;
    Subgraph subg = getSubgraph();
    switch(type) {
    case Grappa.NODE:
        oldValue = subg.setNodeAttribute(name,value);
        break;
    case Grappa.EDGE:
        oldValue = subg.setEdgeAttribute(name,value);
        break;
    case Grappa.SUBGRAPH:
        // ignore subg == null (i.e., root subgraph case) 
        if(subg != null) {
        oldValue = subg.setAttribute(name,value);
        }
        break;
    }
    return oldValue;
    }

    /**
     * Sets or creates a default attribute for this element type within the
     * containing subgraph of this element from the supplied arguments.
     * The storage key is the attribute name.
     * If the value portion of the supplied attribute is null, then the
     * attribute will be removed from the subgraph.
     *
     * @param attr the attribute to which the default should be set
     * @return the value of the (default) attribute previously stored
     *         under the same name
     */
    public Object setDefaultAttribute(Attribute attr) {
    return setDefaultAttribute(getType(),attr);
    }

    /**
     * Sets or creates a default attribute of the specified type within the
     * containing subgraph of this element from the supplied arguments.
     * The storage key is the attribute name.
     * If the value portion of the supplied attribute is null, then the
     * attribute will be removed from the subgraph.
     *
     * @param type the default attribute type
     * @param attr the attribute to which the default should be set
     * @return the value of the (default) attribute previously stored
     *         under the same name
     */
    public Object setDefaultAttribute(int type, Attribute attr) {
    if(attr == null) return null;
    Object oldValue = null;
    Subgraph subg = getSubgraph();
    switch(type) {
    case Grappa.NODE:
        oldValue = subg.setNodeAttribute(attr);
        break;
    case Grappa.EDGE:
        oldValue = subg.setEdgeAttribute(attr);
        break;
    case Grappa.SUBGRAPH:
        // ignore subg == null (i.e., root subgraph case) 
        if(subg != null) {
        oldValue = subg.setAttribute(attr);
        }
        break;
    }
    return oldValue;
    }
  
    /**
     * Gets an enumeration of the keys for this Element’s local attributes.
     *
     * @return an Enumneration of String objects
     */
  
    public Enumeration  getLocalAttributeKeys() {
    if(attributes == null) {
        return Grappa.emptyEnumeration.elements();
    }
    return(attributes.keys());
    }

    /**
     * Get an Enumeration of the Attribute objects for this Element.
     *
     * @return an Enumneration of the (local) Attribute objects.
     */
    public Enumeration  getLocalAttributePairs() {
    if(attributes == null) {
        return Grappa.emptyEnumeration.elements();
    }
    return(attributes.elements());
    }

    /**
     * Get an enumeration of all attribute pairs for this element.
     *
     * @return an enumeration of local and default Attribute objects for this element.
     */
    public Enumeration getAttributePairs() {
    Hashtable pairs = null;
    Attribute attr = null;

    Enumeration enm = getLocalAttributePairs();
    if(enm.hasMoreElements()) pairs = new Hashtable(32);
    while(enm.hasMoreElements()) {
        attr = (Attribute)enm.nextElement();
        pairs.put(attr.getName(),attr);
    }

    switch(getType()) {
    case Grappa.NODE:
        enm = getSubgraph().getNodeAttributePairs();
        break;
    case Grappa.EDGE:
        enm = getSubgraph().getEdgeAttributePairs();
        break;
    case Grappa.SUBGRAPH:
        enm = getLocalAttributePairs();
        break;
    }

    if(pairs != null) {
        while(enm.hasMoreElements()) {
        attr = (Attribute)enm.nextElement();
        if(!pairs.containsKey(attr.getName())) {
            pairs.put(attr.getName(),attr);
        }
        }
        return pairs.elements();
    }

    return enm;
    }

    /**
     * Get only the corresponding local attribute for the specified key.  A local attribute is
     * one associated directly with this element as opposed to a subgraph
     * ancestor.
     *
     * @param key the search key for the corresponding attribute.
     *
     * @return the local Attribute object matching the key or null.
     */
    public Attribute getLocalAttribute(String key) {
    if(attributes == null) return(null);
    return((Attribute)(attributes.get(key)));
    }

    /**
     * Get only the corresponding local attribute for the specified key if
     * it is not inherited from the parent, otherwise return null. Sometimes
     * a local attribute will be set, but it will be a pointer to the
     * parent value. This method distinguishes that case.
     *
     * @param key the search key for the corresponding attribute.
     *
     * @return the local Attribute object matching the key or null if it is not defined or it is a pointer to the parent attribute..
     */
    public Attribute getThisAttribute(String key) {
    Attribute attr;
    Subgraph sg;
    if(attributes == null) return(null);
    if((attr = (Attribute)(attributes.get(key))) == null) return(null);
    if((sg = getSubgraph()) == null) return(attr);
    if(attr == sg.getAttribute(key)) return(null);
    return(attr);
    }

    /**
     * Get only the value of the corresponding local attribute for the
     * specified key if the attribute is not inherited from the parent,
     * otherwise return null. Sometimes a local attribute will be set,
     * but it will be simply a pointer to the parent attribute.
     * This method distinguishes that case.
     *
     * @param key the search key for the corresponding attribute.
     *
     * @return the value of the local Attribute object matching the key or null if it is not defined or it is a pointer to the parent attribute..
     */
    public Object getThisAttributeValue(String key) {
    Attribute attr = getThisAttribute(key);
    if(attr == null) return(null);
    return(attr.getValue());
    }

    /**
     * Get the corresponding default attribute for the specified type and key.
     *
     * @param type the type of the default attribute
     * @param key the search key for the corresponding attribute.
     * @return the value of the default Attribute object matching the key or null.
     */
    public Attribute getDefaultAttribute(int type, String key) {
    Attribute value = null;
    Subgraph sg = null;

    if(isSubgraph()) sg = (Subgraph)this;
    else sg = getSubgraph();

    if(sg == null) {
        // unattached, so try global attributes
        return(Graph.getGlobalAttribute(type,key));
    }

    switch(type) {
    case Grappa.NODE:
        value = sg.getNodeAttribute(key);
        break;
    case Grappa.EDGE:
        value = sg.getEdgeAttribute(key);
        break;
    case Grappa.SUBGRAPH:
        value = sg.getLocalAttribute(key);
        break;
    }
    return(value);
    }

    /**
     * Get the default attribute of this element for the specified key.
     *
     * @param key the search key for the corresponding attribute.
     * @return the value of the default Attribute object matching the key or null.
     */
    public Attribute getDefaultAttribute(String key) {
    return getDefaultAttribute(getType(),key);
    }

    /**
     * Get the Attribute of this Element for the specified key.
     * Search first local, then default attributes until a match is found.
     *
     * @param key the search key for the attribute.
     * @return the corresponding Attribute object or null.
     */
    public Attribute getAttribute(String key) {
    Attribute attr = null;
    
    if((attr = getLocalAttribute(key)) == null) {
        attr = getDefaultAttribute(key);
    }
    return(attr);
    }

    /**
     * Get the Attribute value of this Element for the specified key.
     * Search first local, then default attributes until a match is found.
     *
     * @param key the search key for the attribute.
     * @return the corresponding attribute value or null.
     */
    public Object getAttributeValue(String key) {
    Object value = null;

    Attribute attr = getAttribute(key);
    if(attr != null) {
        value = attr.getValue();
    }
    return(value);
    }

    /**
     * Checks to see if this element has an Attribute matching the key
     *
     * @param key the search key for the attribute.
     * @return true if there is a matching attribute, false otherwise.
     */
    public boolean hasAttributeForKey(String key) {
    if(getAttribute(key) == null) return(false);
    return(true);
    }

    /**
     * Get the Graph of this Element.
     *
     * @return the containing graph object.
     */
    public Graph getGraph() {
    return(graph);
    }

    /**
     * Get the containing Subgraph of this Element.
     *
     * @return the parent subgraph object.
     */
    public Subgraph getSubgraph() {
    return(subgraph);
    }

    /**
     * Set the containing graph for this element.
     *
     * @param graph the overall graph that contains this element.
     */
    void setGraph(Graph graph) {
    this.graph = graph;
    }

    /**
     * Set the parent subgraph for this element.
     *
     * @param subgraph the parent subgraph that contains this element.
     */
    public void setSubgraph(Subgraph subgraph) {
    if(this.subgraph != null && this.subgraph != subgraph) {
        switch(this.getType()) {
        case Grappa.NODE:
        this.subgraph.removeNode(((Node)this).getName());
        subgraph.addNode((Node)this);
        break;
        case Grappa.EDGE:
        this.subgraph.removeEdge(((Edge)this).getName());
        subgraph.addEdge((Edge)this);
        break;
        case Grappa.SUBGRAPH:
        this.subgraph.removeSubgraph(((Subgraph)this).getName());
        subgraph.addSubgraph((Subgraph)this);
        break;
        }
    }
    if(this.subgraph != subgraph) {
        if(this.subgraph != null && this.subgraph.grappaNexus != null)
        this.subgraph.clearBBox();
        if(subgraph != null && subgraph.grappaNexus != null)
        subgraph.clearBBox();
    }
    this.subgraph = subgraph;
    }

    protected void clearBBox() { // formerly resetBBox
    if(grappaNexus != null) grappaNexus.bbox = null;
    Subgraph prnt = getSubgraph();
    while(prnt != null) {
        if(prnt.grappaNexus != null) {
        prnt.grappaNexus.bbox = null;
        }
        prnt = prnt.getSubgraph();
    }
    }

    /**
     * Get the ID number of this Element.
     *
     * @return the id number of this element.
     */
    public int getId() {
    return (int)((getIdKey().longValue())>>Grappa.TYPES_SHIFT);
    }

    /**
     * Get the ID of this Element as a Long object.
     *
     * @return the id object of this element.
     */
    public Long getIdKey() {
    return(idKey);
    }

    /**
     * Sets the id key of this element
     */
    protected void setIdKey(int type) {
    idKey = Graph.idMapKey(type,getGraph().nextId(type));
    }

    /**
     * Print a description of this element to the given print stream.
     *
     * @param out the print stream for output.
     */
    public void printElement(PrintWriter out) {
    String indent = new String(getGraph().getIndent());

    if(Grappa.printVisibleOnly && (!visible || grappaNexus.style.invis))
        return;
    
    out.print(indent + toString());
    getGraph().incrementIndent();
    printAttributes(out,indent);
    getGraph().decrementIndent();
    out.println();
    }

    /*
     * Print attributes to given stream.  A square open bracket prefix and
     * closed bracket suffix enclose the attributes, but are printed only if
     * there are any attributes to print.  The supplied indent determines the
     * indentation of the final bracket (it is assumed the element name has
     * already printed to the output stream.
     *
     * @param out the print stream for output.
     * @param outerIndent the indent to use for the prefix and suffix.
     */
    private void  printAttributes(PrintWriter out, String outerIndent) {
    String indent = new String(getGraph().getIndent());
    String prefix = ” [“;
    String suffix = Grappa.NEW_LINE + outerIndent + “];”;
    Attribute attr;
    String key;
    boolean first = true;
    // thanks to Ginny Travers (bbn.com) for suggesting the printlist feature
    Hashtable printlist = null;

    if(Grappa.usePrintList || usePrintList) {
        printlist = (Hashtable)getAttributeValue(PRINTLIST_ATTR);
    }

    Enumeration attrs = null;
    if(Grappa.elementPrintAllAttributes || printAllAttributes) {
        attrs = getAttributePairs();
    } else if(attributes != null && !attributes.isEmpty()) {
        attrs = attributes.elements();
    }
    if(attrs != null) {
        while(attrs.hasMoreElements()) {
        attr = (Attribute)(attrs.nextElement());
        key = attr.getName();
        if(printlist != null && printlist.get(key) == null) continue;
        if(attr != null && (Grappa.elementPrintAllAttributes || printAllAttributes || !attr.equalsValue(getDefaultAttribute(key)))) {
            if(first) {
            first = false;
            out.println(prefix);
            } else {
            out.println(“,”);
            }
            out.print(indent + key + ” = ” + canonString(attr.getStringValue()));
        }
        }
    }
    if(getGraph().filterMode && isEdge()) {
        if(first) {
        first = false;
        out.println(prefix);
        } else {
        out.println(“,”);
        }
        out.print(indent + “__nAmE__ = ” + canonString(getName()));
    }
    if(!first) {
        out.print(suffix);
    }
    }

    /**
     * Get the String rendition of the element.
     *
     * @return the string rendition of the element, quoted as needed.
     */
    public String toString() {
    if(canonName == null) {
        canonName = canonString(name);
    }
    return(canonName);
    }

    /**
     * Canonicalizes the supplied string for output.
     *
     * @param input the string to be quoted, possibly.
     * @return the input string, possibly enclosed in double quotes and
     *         with internal double quotes protected.
     */
    // essentially the agstrcanon function from libgraph (by S. C. North)
    public static String canonString(String input) {
    int len;

    if(input == null || (len = input.length()) == 0) {
        return(“\”\””);
    }
    
    StringBuffer strbuf = new StringBuffer(len + 8);
    char[] array = input.toCharArray();
    char ch;
    boolean has_special = false;

    for(int isub = 0; isub < array.length; isub++) {         if(array[isub] == '"') {         strbuf.append('\\');         has_special = true;         } else if(!has_special) {         if(!Lexer.id_char(array[isub])) {             has_special = true;         }         }         strbuf.append(array[isub]);     }     // annoying, but necessary kludge to make libgraph parser happy     if(!has_special && len <= 8) {         String low = input.toLowerCase();         if(            low.equals("node") || low.equals("edge") || low.equals("graph") ||            low.equals("digraph") || low.equals("subgraph") || low.equals("strict")            ) {         has_special = true;         }     }     if(has_special) {         strbuf.append('"');         strbuf.insert(0,'"');     }     return(strbuf.toString());     }     /**      * Provides the element type as a string.      *      * @param elemType an integer value representing an element type      * @param uplow set true to indicate the return value should be      *              leading-capitalized, otherwise lower-case is returned      * @return the meaning of the element type in english      */     public final static String typeString(int elemType, boolean uplow) {     String type = null;     switch(elemType) {     case NODE:         type = uplow ? "Node" : "node";         break;     case EDGE:         type = uplow ? "Edge" : "edge";         break;     case SUBGRAPH:         type = uplow ? "Subgraph" : "subgraph";         break;     case SYSTEM:         type = uplow ? PKG_UPLOW : PKG_LOWER;         break;     default:         type = null;     }     return(type);     }     /**      * Canonicalizes the supplied string for look-up.      * NOTE: Not currently used by Grappa.      *      * @param input the string to be canonicalized.      * @return the input string, with non-alphanumerics       *              removed and alphabetics are converted to lower-case.      */     public static String canonValue(String input) {     if(input == null) return null;     char[] array = input.toCharArray();     int len = 0;     boolean allDigits = true;     for(int i = 0; i < array.length; i++) {         if(Character.isUpperCase(array[i])) {         array[len++] = Character.toLowerCase(array[i]);         allDigits = false;         } else if(Character.isLowerCase(array[i])) {         array[len++] = array[i];         allDigits = false;         } else if(Character.isDigit(array[i])) {         array[len++] = array[i];         }     }     if(len == 0 || allDigits) return null;     return new String(array,0,len);     }     /**      * Boolean inicating if a delete request has been received by this element.      */     boolean deleteCalled() {     return deleteCalled;     }     /**      * Method for deleting an element.      * Clears element references from graph tables and frees up space explicitly.      * @see Graph#reset()      */     public final boolean delete() {     if(!setDelete(true)) return(false);     String name = getName();     Enumeration enm = null;     if(attributes != null && grappaNexus != null) {         enm = attributes.elements();         while(enm.hasMoreElements()) {         ((Attribute)enm.nextElement()).deleteObserver(grappaNexus);         }     }     Element elem = null;     Subgraph prnt = null;     // account for bounding box change due to deletion     if(grappaNexus != null) grappaNexus.bbox = null;     prnt = getSubgraph();     while(prnt != null) {         if(prnt.grappaNexus != null) prnt.grappaNexus.bbox = null;         prnt = prnt.getSubgraph();     }     switch(getType()) {     case Grappa.NODE:         enm = ((Node)this).edgeElements();         while(enm.hasMoreElements()) {         elem = (Element)(enm.nextElement());         prnt = elem.getSubgraph();         while(prnt != null) {             if(prnt.grappaNexus != null) prnt.grappaNexus.bbox = null;             prnt = prnt.getSubgraph();         }         ((Edge)elem).delete();         }         getSubgraph().removeNode(name);         break;     case Grappa.EDGE:         ((Edge)this).getTail().removeEdge(((Edge)this),false);         ((Edge)this).getHead().removeEdge(((Edge)this),true);         getSubgraph().removeEdge(name);         break;     case Grappa.SUBGRAPH:         enm = ((Subgraph)this).nodeElements();         elem = null;         while(enm.hasMoreElements()) {         elem = (Element)enm.nextElement();         elem.delete();         }         enm = ((Subgraph)this).edgeElements();         elem = null;         while(enm.hasMoreElements()) {         elem = (Element)enm.nextElement();         elem.delete();         }         enm = ((Subgraph)this).subgraphElements();         elem = null;         while(enm.hasMoreElements()) {         elem = (Element)enm.nextElement();         elem.delete();         }         if(getSubgraph() != null) getSubgraph().removeSubgraph(name);         break;     }     getGraph().removeIdMapping(this);     if(grappaNexus != null) {         grappaNexus.element = null;         grappaNexus = null;     }     return(true);     }     /**      * Tags the element with the supplied string.       *       * @param tag the tag to associate with this Element.      */     public void addTag(String tag) {     Attribute attr;     Hashtable tags;     if(tag == null || tag.indexOf(',') >= 0) {
        throw new RuntimeException(“tag value null or contains a comma (” + tag + “)”);
    }

    if((attr = getLocalAttribute(TAG_ATTR)) == null) {
        attr = new Attribute(getType(),TAG_ATTR,new Hashtable());
        setAttribute(attr);
    }
    tags = (Hashtable)(attr.getValue());

    tags.put(tag,tag);
    // if it becomes desireable to retain the original order, we
    // could always use the value in the following (instead of
    // what is done above) to reconstruct the original order
    // (Note that no code makes use of the value at this point,
    // so that would all have to be added in printAttributes, for
    // example)
    // tags.put(tag,new Long(System.currentTimeMillis()));
    }

    /**
     * Check if this Element has the supplied tag either locally or as a default.
     *
     * @param tag tag value to be searched for
     * @return true, if this Element contains the supplied tag
     */
    public boolean hasTag(String tag) {
    Attribute attr;
    Hashtable tags;

    if(tag == null || tag.indexOf(‘,’) >= 0) {
        throw new RuntimeException(“tag value null or contains a comma (” + tag + “)”);
    }

    if((attr = getLocalAttribute(TAG_ATTR)) == null) {
        return(hasDefaultTag(tag));
    }
    tags = (Hashtable)(attr.getValue());
    if(tags == null || tags.size() == 0) return false;
    return(tags.containsKey(tag));
    }

    /**
     * Check if this Element has the supplied tag locally.
     *
     * @param tag tag value to be searched for
     * @return true, if this Element contains the supplied tag
     */
    public boolean hasLocalTag(String tag) {
    Attribute attr;
    Hashtable tags;
    if((attr = getLocalAttribute(TAG_ATTR)) == null) return false;
    tags = (Hashtable)(attr.getValue());
    if(tags == null || tags.size() == 0) return false;
    return(tags.containsKey(tag));
    }

    /**
     * Check if this Element has the supplied tag as a default tag.
     *
     * @param tag tag value to be searched for
     * @return true, if this Element has the supplied tag as a default tag
     */
    public boolean hasDefaultTag(String tag) {
    Attribute attr;
    Hashtable tags;

    if((attr = getDefaultAttribute(TAG_ATTR)) == null) return false;
    tags = (Hashtable)(attr.getValue());
    if(tags == null || tags.size() == 0) return false;
    return(tags.containsKey(tag));
    }

    /**
     * Check if this Element is tagged at all either locally or with a default.
     *
     * @return true, if this Element is tagged at all
     */
    public boolean hasTags() {
    Attribute attr;
    Hashtable tags;

    if((attr = getLocalAttribute(TAG_ATTR)) == null) {
        return(hasDefaultTags());
    }
    tags = (Hashtable)(attr.getValue());
    if(tags == null || tags.size() == 0) return false;
    return true;
    }

    /**
     * Check if this Element is tagged at all locally.
     *
     * @return true, if this Element is tagged locally
     */
    public boolean hasLocalTags() {
    Attribute attr;
    Hashtable tags;
    if((attr = getLocalAttribute(TAG_ATTR)) == null) return false;
    tags = (Hashtable)(attr.getValue());
    if(tags == null || tags.size() == 0) return false;
    return true;
    }

    /**
     * Check if this Element has any default tags at all.
     *
     * @return true, if this Element has any default tags
     */
    public boolean hasDefaultTags() {
    Attribute attr;
    Hashtable tags;

    if((attr = getDefaultAttribute(TAG_ATTR)) == null) return false;
    tags = (Hashtable)(attr.getValue());
    if(tags == null || tags.size() == 0) return false;
    return true;
    }

    /**
     * Removes all tags locally associated with this element.
     */
    public void removeTags() {
    Attribute attr;
    Hashtable tags;
    if((attr = getLocalAttribute(TAG_ATTR)) == null) return;
    tags = (Hashtable)(attr.getValue());
    if(tags == null || tags.size() == 0) return;
    tags.clear();
    }

    /**
     * Removes the specified tag locally from this element.
     *
     * @param tag the tag value to remove
     */
    public void removeTag(String tag) {
    Attribute attr;
    Hashtable tags;
    if((attr = getLocalAttribute(TAG_ATTR)) == null) return;
    tags = (Hashtable)(attr.getValue());
    if(tags == null || tags.size() == 0) return;
    tags.remove(tag);
    }

    /**
     * Sets the conversion type of a user-defined attribute.
     * Unless provided for a specific attribute name, the attribute value
     * will only be treated as a string. When provided, the string value
     * of the attribute will be converted to the given type and vice
     * versa.
     *
     * @param attrname the attribute name
     * @param attrtype the attribute type
     *
     * @return the previous type associated with this attribute name
     */
    public static int setUserAttributeType(String attrname, int attrtype) {
    int oldtype = _NO_TYPE;
    Integer type = null;

    if(attrname == null || attrname.trim().length() == 0) {
        throw new IllegalArgumentException(“supplied attribute name should be non-null and contain some non-blank characters”);
    }

    if(attrtype < 0) {         type = new Integer(attrtype);     } else {         switch(attrtype) {         case _NO_TYPE:         default:         // ignore         break;         case BOX_TYPE:         case COLOR_TYPE:         case DOUBLE_TYPE:         case FONTSTYLE_TYPE:         case INTEGER_TYPE:         case LINE_TYPE:         case POINT_TYPE:         case SHAPE_TYPE:         case SIZE_TYPE:         case STRING_TYPE:         case STYLE_TYPE:         type = new Integer(attrtype);         break;         }     }     if(type == null) {         throw new IllegalArgumentException("supplied type for attribute (" + attrname + ") should be less than zero or a recognized type value");     }     if(userAttributeTypeMap == null) {         userAttributeTypeMap = new Hashtable();     }     Integer old = (Integer)(userAttributeTypeMap.get(attrname));     if(old != null) {         oldtype = old.intValue();     }     userAttributeTypeMap.put(attrname,type);     return(oldtype);     }     /**      * Returns the attribute conversion type for the supplied attribute name.      *      * @param attrname the attribute name      * @return the currently associated attribute type      */     public static int attributeType(String attrname) {     int convtype = -1;     int hashCode;     if(attrname != null) {         hashCode = attrname.hashCode();         if(hashCode == BBOX_HASH && attrname.equals(BBOX_ATTR)) {         convtype = BOX_TYPE;         } else if(hashCode == COLOR_HASH && attrname.equals(COLOR_ATTR)) {         convtype = COLOR_TYPE;         } else if(hashCode == FILLCOLOR_HASH && attrname.equals(FILLCOLOR_ATTR)) {         convtype = COLOR_TYPE;         } else if(hashCode == FONTCOLOR_HASH && attrname.equals(FONTCOLOR_ATTR)) {         convtype = COLOR_TYPE;         } else if(hashCode == FONTSIZE_HASH && attrname.equals(FONTSIZE_ATTR)) {         convtype = INTEGER_TYPE;         } else if(hashCode == FONTSTYLE_HASH && attrname.equals(FONTSTYLE_ATTR)) {         convtype = FONTSTYLE_TYPE;         } else if(hashCode == HEIGHT_HASH && attrname.equals(HEIGHT_ATTR)) {         convtype = DOUBLE_TYPE;         } else if(hashCode == LABEL_HASH && attrname.equals(LABEL_ATTR)) {         convtype = STRING_TYPE;         } else if(hashCode == LP_HASH && attrname.equals(LP_ATTR)) {         convtype = POINT_TYPE;         } else if(hashCode == PATCH_HASH && attrname.equals(PATCH_ATTR)) {         convtype = DOUBLE_TYPE;         } else if(hashCode == PRINTLIST_HASH && attrname.equals(PRINTLIST_ATTR)) {         convtype = HASHLIST_TYPE;         } else if(hashCode == STYLE_HASH && attrname.equals(STYLE_ATTR)) {         convtype = STYLE_TYPE;         } else if(hashCode == TAG_HASH && attrname.equals(TAG_ATTR)) {         convtype = HASHLIST_TYPE;         } else if(hashCode == STYLE_HASH && attrname.equals(STYLE_ATTR)) {         } else if(hashCode == WIDTH_HASH && attrname.equals(WIDTH_ATTR)) {         convtype = DOUBLE_TYPE;         } else if(userAttributeTypeMap != null) {         Integer usertype = (Integer)(userAttributeTypeMap.get(attrname));         if(usertype == null) {             convtype = STRING_TYPE;         } else {             convtype = usertype.intValue();         }         } else {         convtype = STRING_TYPE;         }     }     return(convtype);     }          /**      * Creates and populates the GrappaNexus object for this element.      * The GrappaNexus object provides bounding and drawing information      * for the element based on the element's attributes.      */     public void buildShape() {     if(grappaNexus == null) {         grappaNexus = new GrappaNexus(this);         Attribute attr = null;         Enumeration enm = listAttrsOfInterest();         while(enm.hasMoreElements()) {         attr = getAttribute((String)enm.nextElement());         if(attr != null) {             attr.addObserver(grappaNexus);         }         }     }     if(grappaNexus == null) {         throw new InternalError("grappaNexus did not get created");     }     }     /**      * Returns the GrappaNexus object associated with this element.      */     public GrappaNexus getGrappaNexus() {     if(grappaNexus == null) {         buildShape();     }     return(grappaNexus);     }     /**      * Performs a breadth-first or a depth-first search starting at this Element.      *      * @param steps when negative, the search is exhaustive; otherwise the search stops after the number of steps indicated      *      * @return a Vector of Vector, the ith element of which gives the search results for step i. Reading the vector in increasing order gives breadth-first search results, while using decreasing order gives depth-first results.      */     public Vector bdfs(int steps) {     Vector input, layers;     Element elem;     int size;     input = new Vector(1);     input.addElement(this);     layers = new Vector();     synchronized(getGraph()) {         doBDFS(getType(), steps, System.currentTimeMillis(), 0, input, layers);     }     return(layers);     }     private static void doBDFS(int type, int depth, long stamp, int level, Vector inbox, Vector layers) {     Element elem;     Subgraph subg;     Edge edge;     Node node;     int sz, szz;     Enumeration enm;     Vector input;     if((sz = inbox.size()) == 0)         return;     layers.addElement(inbox);     level++;     if(depth >= 0 && level > depth)
        return;

    input = new Vector();

    for(int i=0; i 0)
        doBDFS(type, depth, stamp, level, input, layers);
    }

    //
    // Start PatchWork stuff
    //

    private double patchSize = 0;

    double getPatchSize() {
    return(patchSize);
    }

    void setPatchSize(double val) {
    patchSize = val;
    }

    private java.awt.geom.Rectangle2D.Double patch = null;

    java.awt.geom.Rectangle2D.Double getPatch() {
    return(patch);
    }

    void setPatch(java.awt.geom.Rectangle2D.Double p) {
    if(p == null)
        patch = p;
    else if(patch == null)
        patch = new GrappaBox(p.getX(), p.getY(), p.getWidth(), p.getHeight());
    else
        patch.setRect(p.getX(), p.getY(), p.getWidth(), p.getHeight());
    }

    void setPatch(double x, double y, double w, double h) {
    if(patch == null)
        patch = new GrappaBox(x, y, w, h);
    else
        patch.setRect(x, y, w, h);
    }

    //
    // End PatchWork stuff
    //
}

att/grappa/ExceptionDisplay.java
att/grappa/ExceptionDisplay.java/*
 *  This software may only be used by you under license from AT&T Corp.
 *  (“AT&T”).  A copy of AT&T’s Source Code Agreement is available at
 *  AT&T’s Internet website having the URL:
 *  
 *  If you received this software without first entering into a license
 *  with AT&T, you have an infringing copy of this software and cannot use
 *  it without violating AT&T’s intellectual property rights.
 */
package att.grappa;

import java.io.*;
import java.awt.*;
import java.awt.event.*;

/**
 * A class for displaying exception information in a pop-up frame.
 * As a convenience, an instance exists as a static member of
 * the Grappa class.
 *
 * @see Grappa#displayException(java.lang.Exception)
 * @see Grappa#displayException(java.lang.Exception,java.lang.String)
 *
 * @version 1.2, 26 May 2005; Copyright 1996 – 2005 by AT&T Corp.
 * @author  John Mocenigo, Research @ AT&T Labs
 */
public class ExceptionDisplay {
    private String title = null;
    Exception exception = null;
    Display display = null;

    /**
     * Creates an instance of the class for displaying exceptions.
     *
     * @param title the title for the pop-up frame
     */
    public ExceptionDisplay(String title) {
    this.title = title;
    }

    /**
     * Pops up the frame and displays information on the supplied exception.
     * Initially, a text area displays the message associated with the exception.
     * By pressing a button, an end-user can view a stack trace as well.
     *
     * @param ex the exception about which informtaion is to be displayed.
     */
    public void displayException(Exception ex) {
    displayException(ex,null);
    }

    /**
     * Pops up the frame and displays information on the supplied exception.
     * Initially, a text area displays the supplied string followed on the
     * next line by the message associated with the exception.
     * By pressing a button, an end-user can view a stack trace as well.
     *
     * @param ex the exception about which informtaion is to be displayed.
     */
    public void displayException(Exception ex, String msg) {
    if(display == null) display = new Display(title);
    exception = ex;
    if(ex == null && msg == null) {
        return;
    }
    if(msg != null) {
        if(ex == null) {
        display.setText(msg);
        } else {
        display.setText(msg + Grappa.NEW_LINE + ex.getMessage());
        }
    } else {
        display.setText(ex.getMessage());
    }
    display.setVisible(true);
    }

    // TODO: re-do this using JFrame (not a big deal)
    class Display extends Frame {
    private TextArea textarea = null;
    private Panel buttonPanel = null;
    private Button trace = null;
    private Button dismiss = null;
    private WindowObserver observer = null;

    Display(String title) {
        super(title);

        observer = new WindowObserver();

        GridBagLayout gbl = new GridBagLayout();
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.insets = new Insets(4,4,4,4);
        gbc.weightx = 1;
        gbc.weighty = 1;
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        setLayout(gbl);

        textarea = new TextArea(“”,7,80);
        textarea.setEditable(false);

        buttonPanel = new Panel();
        buttonPanel.setLayout(new BorderLayout());

        trace = new Button(“Stack Trace”);
        trace.addActionListener(observer);
        dismiss = new Button(“Dismiss”);
        dismiss.addActionListener(observer);

        buttonPanel.add(“West”,trace);
        buttonPanel.add(“East”,dismiss);

        gbc.fill = GridBagConstraints.BOTH;
        gbl.setConstraints(textarea,gbc);
        add(textarea);
        gbc.weighty = 0;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbl.setConstraints(buttonPanel,gbc);
        add(buttonPanel);

        addWindowListener(observer);
        pack();
    }

    void setText(String text) {
        if(text == null) text = “No message to display, try stack trace.”;
        textarea.setText(text);
    }

    Exception getException() {
        return exception;
    }

    class WindowObserver extends WindowAdapter implements ActionListener {

        public void windowClosing(WindowEvent evt) {
        dismiss();
        }

        private void dismiss() {
        setVisible(false);
        dispose();
        display = null;
        }

        public void actionPerformed(ActionEvent evt) {
        Object src = evt.getSource();
        if(src instanceof Button) {
            Button btn = (Button)src;
            if(btn.getLabel().equals(“Dismiss”)) {
            setVisible(false);
            } else if(btn.getLabel().equals(“Stack Trace”)) {
            if(getException() == null) {
                setText(“No stack trace available (exception is null).”);
            } else {
                StringWriter swriter = new StringWriter();
                PrintWriter pwriter = new PrintWriter(swriter);
                getException().printStackTrace(pwriter);
                pwriter.flush();
                setText(swriter.toString());
                pwriter.close();
            }
            }
        }
        }
    }
    }
}

att/grappa/Graph.java
att/grappa/Graph.java/*
 *  This software may only be used by you under license from AT&T Corp.
 *  (“AT&T”).  A copy of AT&T’s Source Code Agreement is available at
 *  AT&T’s Internet website having the URL:
 *  
 *  If you received this software without first entering into a license
 *  with AT&T, you have an infringing copy of this software and cannot use
 *  it without violating AT&T’s intellectual property rights.
 */

package att.grappa;

import java.util.*;
import java.io.*;

/**
 * This class is the root of the overall graph and provides methods for
 * working with the entire graph (for example. printing the graph). It is an
 * extension of the Subgraph class.
 *
 * @version 1.2, 26 May 2005; Copyright 1996 – 2005 by AT&T Corp.
 * @author  John Mocenigo, Research @ AT&T Labs
 */
public class Graph extends Subgraph
{
    /**
     * The string used for indentation when printing out the graph.
     */
    public final static String INDENT_STRING = ”  “;

    /**
     * used internally by Grappa
     */
    boolean filterMode = false;

    // used with getIndent, incrementIndent and decrementIndent
    private StringBuffer indent = null;

    // used for error message (when set by setErrorWriter)
    private PrintWriter errWriter = null;

    // for keeping track of paint calls
    private boolean paintCalled = false;
    // for indicating if graph is busy being painted or altered
    private boolean busy = false;
    // for indicating if synchronization (involving the above) should be used
    private boolean synchronizePaint = false;

    //private UndoStack undoStack = new UndoStack();
    //private EditOp lastUndo = null;

    // graph-specific outside-the-bounds tooltip text
    private String toolTipText = null;

    // list of panels displaying this graph
    private List panelList = null;

    // counters for subgraph, node and edge elements (for id generation)
    private int gid = 0;
    private int nid = 0;
    private int eid = 0;

    // indicators for properties of the graph
    private boolean editable = false;
    private boolean menuable = false;
    private boolean selectable = true;

    // not used yet
    // public boolean autoUpdate = false; // TODO: add thread, etc.

    // directed graph?
    private boolean directed = true;

    // strict graph?
    private boolean strict = false;

    // for mapping id to an element
    Hashtable id2element = null;

    // Grappa global attributes (apply to all elements)
    private Hashtable grattributes = null;

    // tables for graph default node, edge and graph attributes, which are
    // initialized below
    private static Hashtable sysdfltNodeAttributes  = new Hashtable(8);
    private static Hashtable sysdfltEdgeAttributes  = new Hashtable(7);
    private static Hashtable sysdfltGraphAttributes = new Hashtable(11);

    // graph default node, edge and graph attributes, these should be
    // consistent with the dot layout program, although it is not necessary.
    static {
    // node
    putAttribute(sysdfltNodeAttributes,NODE,COLOR_ATTR,”black”);
    putAttribute(sysdfltNodeAttributes,NODE,FONTCOLOR_ATTR,”black”);
    putAttribute(sysdfltNodeAttributes,NODE,FONTNAME_ATTR,”TimesRoman”);
    putAttribute(sysdfltNodeAttributes,NODE,FONTSIZE_ATTR,”14″);
    putAttribute(sysdfltNodeAttributes,NODE,FONTSTYLE_ATTR,”normal”);
    putAttribute(sysdfltNodeAttributes,NODE,HEIGHT_ATTR,”0.5″);
    putAttribute(sysdfltNodeAttributes,NODE,POS_ATTR,”0,0″);
    putAttribute(sysdfltNodeAttributes,NODE,LABEL_ATTR,”\\N”);
    putAttribute(sysdfltNodeAttributes,NODE,SHAPE_ATTR,”ellipse”);
    putAttribute(sysdfltNodeAttributes,NODE,STYLE_ATTR,GrappaStyle.DEFAULT_SET_STRING);
    putAttribute(sysdfltNodeAttributes,NODE,WIDTH_ATTR,”0.75″);

    // edge
    putAttribute(sysdfltEdgeAttributes,EDGE,COLOR_ATTR,”black”);
    putAttribute(sysdfltEdgeAttributes,EDGE,DIR_ATTR,”forward”);
    putAttribute(sysdfltEdgeAttributes,EDGE,FONTCOLOR_ATTR,”black”);
    putAttribute(sysdfltEdgeAttributes,EDGE,FONTNAME_ATTR,”TimesRoman”);
    putAttribute(sysdfltEdgeAttributes,EDGE,FONTSIZE_ATTR,”14″);
    putAttribute(sysdfltEdgeAttributes,EDGE,FONTSTYLE_ATTR,”normal”);
    putAttribute(sysdfltEdgeAttributes,EDGE,MINLEN_ATTR,”1″);
    putAttribute(sysdfltEdgeAttributes,EDGE,STYLE_ATTR,GrappaStyle.DEFAULT_SET_STRING);
    putAttribute(sysdfltEdgeAttributes,EDGE,WEIGHT_ATTR,”1″);

    // graph
    putAttribute(sysdfltGraphAttributes,SUBGRAPH,CLUSTERRANK_ATTR,”local”);
    putAttribute(sysdfltGraphAttributes,SUBGRAPH,COLOR_ATTR,”white”);
    putAttribute(sysdfltGraphAttributes,SUBGRAPH,FONTCOLOR_ATTR,”black”);
    putAttribute(sysdfltGraphAttributes,SUBGRAPH,FONTNAME_ATTR,”TimesRoman”);
    putAttribute(sysdfltGraphAttributes,SUBGRAPH,FONTSIZE_ATTR,”14″);
    putAttribute(sysdfltGraphAttributes,SUBGRAPH,FONTSTYLE_ATTR,”normal”);
    putAttribute(sysdfltGraphAttributes,SUBGRAPH,MARGIN_ATTR,”0.5,0.5″);
    putAttribute(sysdfltGraphAttributes,SUBGRAPH,MCLIMIT_ATTR,”1″);
    putAttribute(sysdfltGraphAttributes,SUBGRAPH,NODESEP_ATTR,”0.25″);
    putAttribute(sysdfltGraphAttributes,SUBGRAPH,ORIENTATION_ATTR,”portrait”);
    putAttribute(sysdfltGraphAttributes,SUBGRAPH,RANKDIR_ATTR,”TB”);
    putAttribute(sysdfltGraphAttributes,SUBGRAPH,RANKSEP_ATTR,”0.75″);
    putAttribute(sysdfltGraphAttributes,SUBGRAPH,STYLE_ATTR,GrappaStyle.DEFAULT_SET_STRING);
    }

    // used for the above static initialization
    private static void putAttribute(Hashtable table, int type, String name, String value) {
    Attribute attr = new Attribute(type,name,value);
    attr.clearChanged();
    table.put(name,attr);
    }

    /**
     * Reference FontRenderContext
     */
    public final java.awt.font.FontRenderContext REFCNTXT = new java.awt.font.FontRenderContext(IDENTXFRM, Grappa.useAntiAliasing, Grappa.useFractionalMetrics);

    /**
     * Creates a new, empty Graph object.
     *
     * @param graphName the name of this graph.
     * @param directed use true if graph is to be a directed graph
     * @param strict use true if graph is a strict graph
     */
    public Graph(String graphName, boolean directed, boolean strict) {
    //super();
    initialize(graphName);

    setDirection(directed);
    this.strict = strict;

    // grappa attributes used for drawing
    setGrappaAttribute(GRAPPA_BACKGROUND_COLOR_ATTR,”white”);
    setGrappaAttribute(GRAPPA_SELECTION_STYLE_ATTR,”lineColor(red),lineWidth(3)”);
    setGrappaAttribute(GRAPPA_DELETION_STYLE_ATTR,”lineColor(grey85),lineWidth(3),dotted”);
    setGrappaAttribute(GRAPPA_FONTSIZE_ADJUSTMENT_ATTR,”0″);

    }

    /**
     * Creates a directed graph that is not strict
     * A convenience method equivalent to Graph(graphName,true,false).
     *
     * @param graphName the name of this graph.
     * @see Graph#Graph(java.lang.String, boolean, boolean)
     */
    public Graph(String graphName) {
    this(graphName,true,false);
    }

    // graph initialization steps
    private void initialize(String graphName) {
    eid = nid = gid = 0;
    clearBBox();

    if(id2element != null) {
        id2element.clear();
    }

    setGraph(this);
    setSubgraph(null);
    setIdKey(Grappa.SUBGRAPH);
    addIdMapping(this);
    setName(graphName);

    Attribute attr = null;
    Enumeration enm = getGlobalAttributePairs(Grappa.NODE);
    while(enm.hasMoreElements()) {
        setNodeAttribute((Attribute)enm.nextElement());
    }
    enm = getGlobalAttributePairs(Grappa.EDGE);
    while(enm.hasMoreElements()) {
        setEdgeAttribute((Attribute)enm.nextElement());
    }
    enm = getGlobalAttributePairs(Grappa.SUBGRAPH);
    while(enm.hasMoreElements()) {
        setAttribute((Attribute)enm.nextElement());
    }

    setDelete(false);
    }

    private void setDirection(boolean directed) {
    this.directed = directed;
    if(directed) {
        setEdgeAttribute(DIR_ATTR, “forward”);
    } else {
        setEdgeAttribute(DIR_ATTR, “none”);
    }
    }

    /**
     * Sets or unsets indication that paint requests should be done
     * within a synchronized wrapper that prevents concurrent paints
     * and any paints between calls to the dropcloth method.
     *
     * @param sync value to which indicator will be set
     * @return the previous indicator value
     * @see Graph#dropcloth(boolean, boolean)
     */
    public boolean setSynchronizePaint(boolean sync) {
    boolean oldSync = synchronizePaint;
    synchronizePaint = sync;
    return(oldSync);
    }

    /**
     * Get the current paint synchronization indicator value.
     *
     * @return the current paint synchronization indicator value
     * @see Graph#setSynchronizePaint(boolean)
     */
    public boolean getSynchronizePaint() {
    return(synchronizePaint);
    }

    /**
     * Sets and unsets a flag in a synchronized manner so that during the
     * period that the flag is set, painting will not occur.
     *
     * @param block value to which to set the indicator flag
     * @param auto when block is false, setting this parameter true will request a repaint() if any paint requests arrived while the dropcloth was laid out.
     * @return returns false only when block is true and a paint is pending or in progress.
     * @see Graph#setSynchronizePaint(boolean)
     *
     */
    public boolean dropcloth(boolean block, boolean auto) {
    return setBlocked(block, false, auto);
    }

    // used in GrappaPanel 
    boolean setPaint(boolean paint) {
    return setBlocked(paint, true, false);
    }

    private synchronized boolean setBlocked(boolean state, boolean isPaint, boolean repaint) {
    if(isPaint) {
        if(state) {
        paintCalled = true;
        if(busy) {
            return(false);
        } else {
            return(busy = true);
        }
        } else {
        paintCalled = busy = false;
        return(true);
        }
    } else if(state) {
        if(paintCalled) return(false);
        return(busy = true);
    } else {
        if(!paintCalled) {
        busy = false;
        } else {
        busy = paintCalled = false;
        if(repaint) repaint();
        }
        return(true);
    }
    }

    /**
     * Gets Grappa default attribute.
     *
     * @param key the search key for the corresponding attribute.
     * @exception IllegalArgumentException whenever the key is null
     * @return the value of the matching Grappa default attribute or null.
     */
    public Attribute getGrappaAttribute(String key) throws IllegalArgumentException {
    if(key == null) {
        throw new IllegalArgumentException(“key value cannot be null”);
    }
    if(grattributes == null) return null;
    return ((Attribute)(grattributes.get(key)));
    }

    /**
     * Gets Grappa default attribute value.
     *
     * @param key the search key for the corresponding attribute.
     * @exception IllegalArgumentException whenever the key is null
     * @return the value portion of the matching Grappa default attribute or null.
     */
    public Object getGrappaAttributeValue(String key) throws IllegalArgumentException {
    if(key == null) {
        throw new IllegalArgumentException(“key value cannot be null”);
    }
    if(grattributes == null) return null;
    Attribute attr =  (Attribute)(grattributes.get(key));
    if(attr == null) return null;
    return(attr.getValue());
    }

    /**
     * Sets a Grappa package attribute.  A Grappa package attribute is one
     * specific to Grappa (for example, a display color) rather than an
     * attribute that relates to a graph.
     *
     * @param key the search key for the corresponding attribute.
     * @exception IllegalArgumentException whenever the key is not prefixed by Grappa.PKG_LOWER
     * @return the previous value of the matching Grappa default attribute or null.
     * @see GrappaConstants#PKG_LOWER
     */
    public Object setGrappaAttribute(String key, String value) throws IllegalArgumentException {
    if(grattributes == null) {
        grattributes = new Hashtable(4);
    }
    // the get also tests if key is null
    Attribute oldValue = getGrappaAttribute(key);
    if(oldValue == null) {
        if(!validGrappaAttributeKey(key)) {
        throw new IllegalArgumentException(Grappa.PKG_UPLOW + ” attribute key must use \”” + Grappa.PKG_LOWER + “\” as a prefix”);
        }
        oldValue = new Attribute(SYSTEM,key,value);
        grattributes.put(key,oldValue);
        return null;
    } else {
        grattributes.put(key, new Attribute(SYSTEM,key,value));
    }
    return oldValue.getValue();
    }

    /**
     * Returns the attribute conversion type for the supplied attribute name.
     * Only graph global specific attribute name/type mappings are checked.
     *
     * @param attrname the attribute name
     * @return the currently associated attribute type
     */
    public static int attributeType(String attrname) {
    int convtype = -1;
    int hashCode;

    if(attrname != null) {
        hashCode = attrname.hashCode();

        if(hashCode == GRAPPA_BACKGROUND_COLOR_HASH && attrname.equals(GRAPPA_BACKGROUND_COLOR_ATTR)) {
        convtype = COLOR_TYPE;
        } else if(hashCode == GRAPPA_SELECTION_STYLE_HASH && attrname.equals(GRAPPA_SELECTION_STYLE_ATTR)) {
        convtype = STYLE_TYPE;
        } else if(hashCode == GRAPPA_DELETION_STYLE_HASH && attrname.equals(GRAPPA_DELETION_STYLE_ATTR)) {
        convtype = STYLE_TYPE;
        } else if(hashCode == GRAPPA_FONTSIZE_ADJUSTMENT_HASH && attrname.equals(GRAPPA_FONTSIZE_ADJUSTMENT_ATTR)) {
        convtype = INTEGER_TYPE;
        } else {
        convtype = STRING_TYPE;
        }
    }

    return(convtype);
    }

    /**
     * Get an enumeration of the Grappa package attribute keys.
     *
     * @return an Enumeration of Attribute objects
     */
    public Enumeration getGrappaAttributeKeys() {
    if(grattributes == null) {
        return Grappa.emptyEnumeration.elements();
    }
    return grattributes.keys();
    }

    /**
     * Check if the given key has a format consistent with Grappa package
     * attribute keys.  A Grappa package key starts with Grappa.PKG_LOWER.
     *
     * @param key the key to validate
     * @return true if the supplied key could serve as a Grappa package attribute key.
     * @see GrappaConstants#PKG_LOWER
     */
    public static boolean validGrappaAttributeKey(String key) {
    return (key != null && key.startsWith(Grappa.PKG_LOWER) && key.length() > Grappa.PKG_LOWER.length());
    }

    /**
     * Gets a graph default attribute. A graph default attribute determines
     * basic graph characteristics initially (e.g., node shape).
     *
     * @param type indicates attribute type.
     * @param key the search key for the corresponding attribute.
     * @exception IllegalArgumentException whenever the specified type is not valid
     * @return the value of the matching graph default attribute or null.
     * @see GrappaConstants#NODE
     * @see GrappaConstants#EDGE
     * @see GrappaConstants#SUBGRAPH
     */
    public static Attribute getGlobalAttribute(int type, String key) throws IllegalArgumentException {
    switch(type) {
    case Grappa.NODE:
        return((Attribute)sysdfltNodeAttributes.get(key));
    case Grappa.EDGE:
        return((Attribute)sysdfltEdgeAttributes.get(key));
    case Grappa.SUBGRAPH:
        return((Attribute)sysdfltGraphAttributes.get(key));
    }
    throw new IllegalArgumentException(“specified type must be NODE, EDGE or SUBGRAPH”);
    }

    /**
     * Gets an enumeration of the specified graph default attribute keys
     *
     * @param type indicates attribute type.
     * @exception IllegalArgumentException whenever the specified type is not valid
     * @return an Enumeration of String objects
     * @see GrappaConstants#NODE
     * @see GrappaConstants#EDGE
     * @see GrappaConstants#SUBGRAPH
     */
    public static Enumeration getGlobalAttributeKeys(int type) throws IllegalArgumentException {
    switch(type) {
    case Grappa.NODE:
        return(sysdfltNodeAttributes.keys());
    case Grappa.EDGE:
        return(sysdfltEdgeAttributes.keys());
    case Grappa.SUBGRAPH:
        return(sysdfltGraphAttributes.keys());
    }
    throw new IllegalArgumentException(“specified type must be NODE, EDGE or SUBGRAPH”);
    }

    /**
     * Gets an enumeration of the specified graph default attributes
     *
     * @param type indicates attribute type.
     * @exception IllegalArgumentException whenever the specified type is not valid
     * @return an Enumeration of Attribute objects
     * @see GrappaConstants#NODE
     * @see GrappaConstants#EDGE
     * @see GrappaConstants#SUBGRAPH
     */
    public static Enumeration getGlobalAttributePairs(int type) throws IllegalArgumentException {
    switch(type) {
    case Grappa.NODE:
        return(sysdfltNodeAttributes.elements());
    case Grappa.EDGE:
        return(sysdfltEdgeAttributes.elements());
    case Grappa.SUBGRAPH:
        return(sysdfltGraphAttributes.elements());
    }
    throw new IllegalArgumentException(“specified type must be NODE, EDGE or SUBGRAPH”);
    }

    /**
     * Get a count of the graph default attributes of a particular type.
     *
     * @param type indicates attribute type.
     * @exception IllegalArgumentException whenever the specified type is not valid
     * @return a count of the specified graph default attributes
     * @see GrappaConstants#NODE
     * @see GrappaConstants#EDGE
     * @see GrappaConstants#SUBGRAPH
     */
    public static int getGlobalAttributeSize(int type) throws IllegalArgumentException {
    switch(type) {
    case Grappa.NODE:
        return(sysdfltNodeAttributes.size());
    case Grappa.EDGE:
        return(sysdfltEdgeAttributes.size());
    case Grappa.SUBGRAPH:
        return(sysdfltGraphAttributes.size());
    }
    throw new IllegalArgumentException(“specified type must be NODE, EDGE or SUBGRAPH”);
    }

    /**
     * Add id to element lookup table
     * (used in setId method)
     *
     * @param elem the element associated with the id
     */
    Element addIdMapping(Element elem) {
    if(elem == null) {
        return null;
    }
    if(id2element == null) {
        id2element = new Hashtable();
    }
    return (Element)id2element.put(elem.getIdKey(),elem);
    }

    /**
     * Creates a id key given a type and id number.
     *
     * @param type one of Grappa.NODE, Grappa.EDGE or Grappa.SUBGRAPH
     * @param id an id number
     * @exception IllegalArgumentException whenever the specified type is not valid
     * @return an idKey for an element
     * @see GrappaConstants#NODE
     * @see GrappaConstants#EDGE
     * @see GrappaConstants#SUBGRAPH
     */
    static Long idMapKey(int type, int id) throws IllegalArgumentException {
    long value = (long)(id);
    int tval = (type&(Grappa.NODE|Grappa.EDGE|Grappa.SUBGRAPH));
    if(tval == 0) {
        throw new IllegalArgumentException(“supplied type does not specify node, edge or subgraph”);
    }
    value = (value << Grappa.TYPES_SHIFT) | (type&(Grappa.NODE|Grappa.EDGE|Grappa.SUBGRAPH));     return new Long(value);     }     /**      * Get the type of the id key.      *      * @param idKey the id key to examine      * @return the type of the id key (Grappa.NODE, Grappa.EDGE, Grappa.SUBGRAPH)      * @see GrappaConstants#NODE      * @see GrappaConstants#EDGE      * @see GrappaConstants#SUBGRAPH      */     static int idKeyType(Long idKey) {     long value = idKey.longValue();     return (int)(value&(Grappa.NODE|Grappa.EDGE|Grappa.SUBGRAPH));     }     /**      * Get the type of the id key.      *      * @param idKey the id key to examine      * @return the type of the id key (Grappa.NODE, Grappa.EDGE, Grappa.SUBGRAPH)      * @see GrappaConstants#NODE      * @see GrappaConstants#EDGE      * @see GrappaConstants#SUBGRAPH      */     static int idKeyId(Long idKey) {     long value = idKey.longValue();     return (int)(value>>>Grappa.TYPES_SHIFT);
    }

    /**
     * Get the element associated with an id key
     *
     * @param idKey the id key of the element to be located
     * @return the Element object matching the id key or null
     */
    Element element4Id(Long idKey) {
    if(id2element == null) {
        return null;
    }
    return (Element)id2element.get(idKey);
    }

    /**
     * Remove id2element dictionary element
     *
     * @param id the id number of the element entry to be removed
     */
    void removeIdMapping(Element elem) {
    if(id2element != null && elem != null) {
        id2element.remove(elem.getIdKey());
    }
    }

    /**
     * Output graph to specified Writer.
     *
     * @param output the Writer for writing
     */
    public void printGraph(Writer output) {
    PrintWriter out = null;
    
    if(output instanceof PrintWriter) {
        out = (PrintWriter)output;
    } else {
        out = new PrintWriter(output);
    }
    getGraph().printSubgraph(out);
    out.flush();
    }

    /**
     * Output graph to specified OutputStream.
     * A convenience method to accomodate the OuputStreams easily.
     *
     * @param output the OutputStream for writing
     */
    public void printGraph(OutputStream output) {
    printGraph(new PrintWriter(output));
    }

    /**
     * Get the next id number for the specified type and increment the counter.
     *
     * @param type type of id number to return
     * @exception IllegalArgumentException whenever the specified type is not valid
     * @return the next sequential id number (counter is incremented).
     * @see GrappaConstants#NODE
     * @see GrappaConstants#EDGE
     * @see GrappaConstants#SUBGRAPH
     */
    int nextId(int type) throws IllegalArgumentException {
    switch(type) {
    case Grappa.NODE:
        return(nid++);
    case Grappa.EDGE:
        return(eid++);
    case Grappa.SUBGRAPH:
        return(gid++);
    }
    throw new IllegalArgumentException(“Type (“+type+”) is not recognized.”);
    }

    /**
     * Get the next id number for the specified type, but do not increment the counter.
     *
     * @param type type of id number to return
     * @exception IllegalArgumentException whenever the specified type is not valid
     * @return the next sequential id number (counter is not incremented).
     * @see GrappaConstants#NODE
     * @see GrappaConstants#EDGE
     * @see GrappaConstants#SUBGRAPH
     */
    public int getId(int type) throws IllegalArgumentException {
    switch(type) {
    case Grappa.NODE:
        return(nid);
    case Grappa.EDGE:
        return(eid);
    case Grappa.SUBGRAPH:
        return(gid);
    }
    throw new IllegalArgumentException(“Type (“+type+”) is not recognized.”);
    }

    /**
     * Get the current indent string.
     *
     * @return the current indent string.
     */
    public String getIndent() {
    if(indent == null) {
        indent = new StringBuffer(5 * INDENT_STRING.length());
    }
    return(indent.toString());
    }

    /**
     * Increase the indent string by appending INDENT_STRING.
     *
     * @see Graph#INDENT_STRING
     */
    public void incrementIndent() {
    if(indent == null) {
        indent = new StringBuffer(5 * INDENT_STRING.length());
    }
    indent.append(INDENT_STRING);
    }

    /**
     * Decrease the indent string by removing one INDENT_STRING.
     *
     * @see Graph#INDENT_STRING
     */
    public void decrementIndent() {
    int len = indent.length();

    if(len == 0) return;

    if(len < INDENT_STRING.length()) {         indent.setLength(0);     } else {         indent.setLength(len - INDENT_STRING.length());     }     }     /**      * Check if the graph is directed.      *      * @return true if graph is a directed graph      */     public boolean isDirected() {     return(directed);     }     /**      * Check if the graph is strict (i.e., no self-loops).      *      * @return true if the graph is strict      */     public boolean isStrict() {     return(strict);     }     /**      * Set the tooltip text displayed when outside the graph area.      *      * @param text out-of-graph tooltip text      * @return previous out-of-graph tooltip text      */     public String setToolTipText(String text) {     String oldTip = toolTipText;     toolTipText = text;     return(oldTip);     }     /**      * Get the tooltip text displayed when outside the graph area.      *      * @return out-of-graph tooltip text      */     public String getToolTipText() {     return(toolTipText);     }     //TODO find out dot options to fill or outline graph also orientation.     /**      * Reset this graph by removing all its elements and re-initiailizing      * its internal variables.      */     public void reset() {     String graphName = getName();     if(delete()) initialize(graphName);     }     /**      * Reset this graph by removing all its elements and re-initiailizing      * its internal variables and possibly changing its name, directedness      * and strictness.      */     public void reset(String graphName, boolean directed, boolean strict) {     name = graphName;     reset();     setDirection(directed);     this.strict = strict;     }     /**      * Check if this graph is interactively editable (i.e., through mouse events).      *      * @return true if the graph can be edited interactively.      */     public boolean isEditable() {     return editable;     }     /**      * Set the editability of the graph.      *      * @param mode true to turn on editability.      * @return previous value      * @see Graph#isEditable()      */     public boolean setEditable(boolean mode) {     boolean wasMode = editable;     editable = mode;     return wasMode;     }     /**      * Check if graph elements are interactively selectable  (i.e., through mouse events).      *      * @return true if graph elements can be selected interactively.      */     public boolean isSelectable() {     return selectable;     }     /**      * Set the selectability of the graph.      *      * @param mode true to turn on selectability.      * @return previous value      * @see Graph#isSelectable()      */     public boolean setSelectable(boolean mode) {     boolean wasMode = selectable;     selectable = mode;     return wasMode;     }     /**      * Check if an element-specific menu is available interactively (i.e., through mouse events).      *      * @return true if an element-specific menu is available      */     public boolean isMenuable() {     return menuable;     }     /**      * Set whether element-specific menus are to be available interactively.      *      * @param mode true to turn on element-specific-menus.      * @return previous value      * @see Graph#isMenuable()      */     public boolean setMenuable(boolean mode) {     boolean wasMode = menuable;     menuable = mode;     return wasMode;     }     /**      * Set the PrintWriter for error messages.      *      * @param errWriter the PrintWriter to use for error messages.      * @return the previous PrintWriter used for error messages.      * @see java.io.PrintWriter      */     public PrintWriter setErrorWriter(PrintWriter errWriter) {     PrintWriter oldWriter = this.errWriter;     this.errWriter = errWriter;     return oldWriter;     }     /**      * Get the current PrintWriter used for error messages.      *      * @return the current PrintWriter used for error messages.      * @see java.io.PrintWriter      */     public PrintWriter getErrorWriter() {     return errWriter;     }     /**      * Print the supplied message to the error output.      * Nothing happens if the error output is set to null.      *      * @param msg the message to print on the error output.      * @see Graph#setErrorWriter(java.io.PrintWriter)      */     public void printError(String msg) {     printError(msg,null);     }     /**      * Print the supplied message and exception information to the error output.      * Nothing happens if the error output is set to null.      *      * @param msg the message to print on the error output.      * @param ex if supplied, the stack trace associated with this exception is also printed.      * @see Graph#setErrorWriter(java.io.PrintWriter)      */     public void printError(String msg, Exception ex) {     if(getErrorWriter() == null) return;     getErrorWriter().println("ERROR: " + msg);     if(ex != null) ex.printStackTrace(getErrorWriter());     getErrorWriter().flush();     }     //////////////////////////////////////////////////////////////////////     /**      * Builds any GrappaNexus object not already built for elements in      * this graph.      */     public void buildShapes() {     GraphEnumeration enm = elements();     Element elem;     while(enm.hasMoreElements()) {         elem = enm.nextGraphElement();         if(elem.grappaNexus == null) {         elem.buildShape();         }     }     }     /**      * Builds any GrappaNexus object not already built and rebuilds those      * that already exist for all elements in this graph.      */     public void resync() {     Element elem = null;     GraphEnumeration enm = elements();     while(enm.hasMoreElements()) {         elem = enm.nextGraphElement();         if(elem.grappaNexus == null) elem.buildShape();         else elem.grappaNexus.rebuild();     }     }     //////////////////////////////////////////////////////////////////////     /**      * Makes a repaint request of all GrappaPanels that are displaying      * this graph.      */     public void repaint() {     if(panelList == null) return;     boolean incomplete = true;     ListIterator li = null;     while(incomplete) {         try {         li = panelList.listIterator(0);         while(li.hasNext()) {             ((GrappaPanel)li.next()).repaint();         }         } catch(ConcurrentModificationException cme) {         continue;         }         incomplete = false;     }     }     /**      * Makes a paintImmediately request of all GrappaPanels that are displaying      * this graph.      */     public void paintImmediately() {     if(panelList == null) return;     boolean incomplete = true;     ListIterator li = null;     GrappaPanel panel = null;     while(incomplete) {         try {         li = panelList.listIterator(0);         while(li.hasNext()) {             panel = ((GrappaPanel)li.next());             panel.paintImmediately(panel.getVisibleRect());         }         } catch(ConcurrentModificationException cme) {         continue;         }         incomplete = false;     }     }     /**      * Adds a panel to the list of GrappaPanels that are displaying      * this graph.      *      * @param panel the GrappaPanel to be added to the list      */     public void addPanel(GrappaPanel panel) {     if(panelList == null) {         panelList = Collections.synchronizedList(new LinkedList());     }     synchronized(panelList) {         if(!panelList.contains(panel)) panelList.add(panel);     }     }     /**      * Removes a panel to the list of GrappaPanels that are displaying      * this graph.      *      * @param panel the GrappaPanel to be removed to the list      */     public void removePanel(GrappaPanel panel) {     if(panelList == null) return;     synchronized(panelList) {         panelList.remove(panel);     }     } } att/grappa/GraphEnumeration.java att/grappa/GraphEnumeration.java/*  *  This software may only be used by you under license from AT&T Corp.  *  ("AT&T").  A copy of AT&T's Source Code Agreement is available at  *  AT&T's Internet website having the URL:  *  
 *  If you received this software without first entering into a license
 *  with AT&T, you have an infringing copy of this software and cannot use
 *  it without violating AT&T’s intellectual property rights.
 */
package att.grappa;

/**
 * An extension of the Enumeration interface specific to enumerations of
 * graph elements.
 *
 * @version 1.2, 26 May 2005; Copyright 1996 – 2005 by AT&T Corp.
 * @author  John Mocenigo, Research @ AT&T Labs
 */
public interface GraphEnumeration extends java.util.Enumeration
{
  /**
   * Get the root of this enumeration.
   *
   * @return the root subgraph for this enumeration
   */
  public Subgraph getSubgraphRoot();

  /**
   * Get the types of elements possibly contained in this enumeration.
   *
   * @return an indication of the types of elements in this enumeration
   * @see GrappaConstants#NODE
   * @see GrappaConstants#EDGE
   * @see GrappaConstants#SUBGRAPH
   */
  public int getEnumerationTypes();

  /**
   * A convenience method that should just return a cast
   * of a call to nextElement()
   *
   * @return the next graph element in the enumeration
   * @exception java.util.NoSuchElementException whenever the enumeration has no more
   *                                   elements.
   */
  public Element nextGraphElement() throws java.util.NoSuchElementException;
}

att/grappa/GraphParserException.java
att/grappa/GraphParserException.java/*
 *  This software may only be used by you under license from AT&T Corp.
 *  (“AT&T”).  A copy of AT&T’s Source Code Agreement is available at
 *  AT&T’s Internet website having the URL:
 *  
 *  If you received this software without first entering into a license
 *  with AT&T, you have an infringing copy of this software and cannot use
 *  it without violating AT&T’s intellectual property rights.
 */

package att.grappa;

/**
 * This class is used whenever a problem is detected during parsing.
 *
 * @version 1.2, 26 May 2005; Copyright 1996 – 2005 by AT&T Corp.
 * @author  John Mocenigo, Research @ AT&T Labs
 */
public class GraphParserException extends RuntimeException
{
  /**
   * Constructs an GraphParserException with no detail  message.
   */
  public GraphParserException() {}
  /**
   * Constructs an GraphParserException with the specified 
   * detail message. 
   *
   * @param   message   the detail message.
   */
  public GraphParserException(String message) {
    super(message);
  }
}

att/grappa/Grappa.java
att/grappa/Grappa.java/*
 *  This software may only be used by you under license from AT&T Corp.
 *  (“AT&T”).  A copy of AT&T’s Source Code Agreement is available at
 *  AT&T’s Internet website having the URL:
 *  
 *  If you received this software without first entering into a license
 *  with AT&T, you have an infringing copy of this software and cannot use
 *  it without violating AT&T’s intellectual property rights.
 */

package att.grappa;

/**
 * This class sets default option variables and other set-up.
 * In addition, some convenience methods for exception display are
 * included and some lookup tables are initialized.
 *
 * 

The Grappa package itself has two roles:


     * 
  • building, maintaining and manipulating graph structure, and
     * 
  • drawing a positioned graph.

 * Grappa itself does not have any methods for graph positioning.
 * Grappa simply draws the nodes and edges based on the value of the
 * element’s pos attribute, though it will treat an unpositioned
 * edge as a straight line between the center points of its attached nodes.
 *
 * Some graph layout references are:

     * 
  1. “An Algorithm for Drawing General Undirected Graphs”,
     * Tomihisa Kamada and Satoru Kawai,
     * Information Processing Letters, Vol. 31 (1989), pp. 7 – 15
     *
     * 
  2. “Graph Drawing by Force-directed Placement”,
     * Thomas M. J. Fruchterman and Edward M. Reingold,
     * Software – Practice and Experience, Vol. 21 No. 11 (1991), pp. 1129 – 1164
     *
     * 
  3. “A Technique for Drawing Directed Graphs”,
     * Emden R. Gansner, Eleftherios Koutsofios, Stephen C. North and
     * Kiem-Phong Vo,
     * IEEE Transactions on Software Engineering, Vol 19 No. 3 (1993), pp. 214 – 230
     *
     * 
  4. “NicheWorks – Interactive Visualization of Very Large Graphs”,
     * Graham J. Wills,
     * http://www.bell-labs.com:80/user/gwills/NICHEguide/nichepaper.html, circa 1997

 *
 * 

Grappa does supply a utility, GrappaSupport.filterGraph(), for updating
 * a graph (including position information) from I/O streams such as might
 * be obtained from a java.net.URLConnection.
 *
 * @version 1.2, 26 May 2005; Copyright 1996 – 2005 by AT&T Corp.
 * @author  John Mocenigo, Research @ AT&T Labs
 */
public abstract class Grappa
    implements att.grappa.GrappaConstants
{

    /**
     * Look-up table that maps a shape name to its integer reference value.
     */
    public static java.util.Hashtable keyToShape = new java.util.Hashtable();
    /**
     * Look-up table that maps a shape reference value to its name.
     */
    public static java.util.Hashtable shapeToKey = new java.util.Hashtable();

    /*
     * Set-up lookup table that defines recognized shapes (useful
     * for switch statements).
     */
    static {
    keyToShape.put(“box”, new Integer(BOX_SHAPE));
    keyToShape.put(“circle”, new Integer(OVAL_SHAPE));
    keyToShape.put(“custom”, new Integer(CUSTOM_SHAPE));
    keyToShape.put(“diamond”, new Integer(DIAMOND_SHAPE));
    keyToShape.put(“doublecircle”, new Integer(DOUBLECIRCLE_SHAPE));
    keyToShape.put(“doubleoctagon”, new Integer(DOUBLEOCTAGON_SHAPE));
    keyToShape.put(“egg”, new Integer(EGG_SHAPE));
    keyToShape.put(“ellipse”, new Integer(OVAL_SHAPE));
    keyToShape.put(“hexagon”, new Integer(HEXAGON_SHAPE));
    keyToShape.put(“house”, new Integer(HOUSE_SHAPE));
    keyToShape.put(“invhouse”, new Integer(INVERTEDHOUSE_SHAPE));
    keyToShape.put(“invtrapezium”, new Integer(INVERTEDTRAPEZIUM_SHAPE));
    keyToShape.put(“invtriangle”, new Integer(INVERTEDTRIANGLE_SHAPE));
    keyToShape.put(“octagon”, new Integer(OCTAGON_SHAPE));
    keyToShape.put(“pentagon”, new Integer(PENTAGON_SHAPE));
    keyToShape.put(“parallelogram”, new Integer(PARALLELOGRAM_SHAPE));
    keyToShape.put(“plaintext”, new Integer(PLAINTEXT_SHAPE));
    keyToShape.put(“point”, new Integer(POINT_SHAPE));
    keyToShape.put(“polygon”, new Integer(POLYGON_SHAPE));
    keyToShape.put(“record”, new Integer(RECORD_SHAPE));
    keyToShape.put(“roundedbox”, new Integer(ROUNDEDBOX_SHAPE));
    keyToShape.put(“trapezium”, new Integer(TRAPEZIUM_SHAPE));
    keyToShape.put(“triangle”, new Integer(TRIANGLE_SHAPE));
    keyToShape.put(“tripleoctagon”, new Integer(TRIPLEOCTAGON_SHAPE));
    keyToShape.put(“Mcircle”, new Integer(MCIRCLE_SHAPE));
    keyToShape.put(“Mdiamond”, new Integer(MDIAMOND_SHAPE));
    keyToShape.put(“Mrecord”, new Integer(MRECORD_SHAPE));
    keyToShape.put(“Msquare”, new Integer(MSQUARE_SHAPE));

    java.util.Enumeration enm = keyToShape.keys();
    String key = null;
    while(enm.hasMoreElements()) {
        key = (String)enm.nextElement();
        shapeToKey.put(keyToShape.get(key),key);
    }

    // special case, but don’t want it in shapeToKey
    keyToShape.put(“square”, new Integer(BOX_SHAPE));
    }

    /**
     * The java.awt.Toolkit.getDefaultToolkit() value, if available
     */
    public static java.awt.Toolkit toolkit = null;

    static {
    try {
        toolkit = java.awt.Toolkit.getDefaultToolkit();
    }
    catch(java.awt.AWTError err) {
    }
    }

  
    /*
     * A instance of ExceptionDisplay used with the (public) convenience
     * methods that follow.
     */ 
    private static final ExceptionDisplay exceptionDisplay = new ExceptionDisplay(Grappa.PKG_UPLOW + “:  Exception Detected”);

    /**
     * Boolean for enabling/disabling exception pop-up window display.
     */
    public static boolean doDisplayException = true;

    /**
     * Method for displaying an exception in a pop-up window (if enabled).
     * @param ex The exception value about which information is to be displayed.
     * @see Grappa#doDisplayException
     * @see DisplayException
     */
    public static void displayException(java.lang.Exception ex) {
    if(doDisplayException) exceptionDisplay.displayException(ex);
    }

    /**
     * Method for displaying an exception in a pop-up window (if enabled).
     * @param ex The exception value about which information is to be displayed.
     * @param msg Additional text to be displayed ahead of exception info.
     * @see Grappa#doDisplayException
     * @see DisplayException
     */
    public static void displayException(java.lang.Exception ex, java.lang.String msg) {
    if(doDisplayException) exceptionDisplay.displayException(ex,msg);
    }

    /**
     * A convenience Vector useful when an enumeration is to be returned, but
     * the object to be enumerated is null (in which case, the return value can
     * be Grappa.emptyEnumeration.elements(), whose hasMoreElements() method
     * will return false).
     */
    public static final java.util.Vector emptyEnumeration = new java.util.Vector(0,0);

    /*
     * Default tool-tip text when cursor is outside graph, but inside
     * the display panel.
     */
    private static String toolTipText = ““;

    /**
     * Sets the tool-tip text displayed when outside graph, but inside
     * the display panel.
     *
     * @param text The new outside-the-graph tool-tip text to display.
     *
     * @return The former outside-the-graph tool-tip text.
     */
    public static String setToolTipText(String text) {
    String oldTip = toolTipText;
    toolTipText = text;
    return(oldTip);
    }

    /**
     * Gets the current  tool-tip text displayed when outside graph, but inside
     * the display panel.
     *
     * @return The current outside-the-graph tool-tip text.
     */
    public static String getToolTipText() {
    return(toolTipText);
    }

    /*
     * global options
     */

    /**
     * Boolean to indicate if all element attributes should be printed.
     * By default, only the element specific attributes are printed and
     * attributes inherited from a parent element (subgraph) are skipped.
     *
     * @see Subgraph#printSubgraph
     * @see Element#printAllAttributes
     */
    public static boolean elementPrintAllAttributes = false;
    /**
     * Boolean to indicate if the default attributes associated with
     * a subgraph should be printed.
     * By default, only those differing from the pre-defined defaults are
     * printed.
     *
     * @see Subgraph#printSubgraph
     * @see Element#printDefaultAttributes
     */
    public static boolean elementPrintDefaultAttributes = false;

    /**
     * Indicates if element text should be included in the element
     * bounding box. By default, it is.
     *
     * @see GrappaNexus#boundText
     */
    public static boolean shapeBoundText        = true;
    /**
     * Indicates if the area bounding the element text should be
     * filled/outlined along with the element when being drawn.
     * By default, it is not.
     *
     * @see GrappaNexus#clearText
     */
    public static boolean shapeClearText        = false;
    /**
     * Indicates if element text should be drawn when drawing the
     * element. By default, it is.
     *
     * @see GrappaNexus#drawText
     */
    public static boolean shapeDrawText         = true;

    /**
     * Indicates if element node position indicates the node center
     * point. Otherwise, the position is assumed to indicate the
     * upper-left corner of the nodes bounding box.
     * By default, the position indicates the node’s center point.
     */
    public static boolean centerPointNodes      = true;
    /**
     * Indicates if the label position for node labels should automatically
     * be set to the center point of the node. When true, positioning
     * information provided by the lp attribute is completely
     * ignored. By default, auto-positioning is used.
     */
    public static boolean autoPositionNodeLabel     = true;

    // not used
    //public static boolean graphAutoUpdate     = false;

    /**
     * Indicates if the bb attribute of a subgraph should
     * automatically be set whenever the bounding box is calculated.
     * By default, the attribute is not set automatically.
     */
    public static boolean provideBBoxAttribute      = false;

    /**
     * Indicates what winding rule to use whenever a winding rule is
     * required. The default is the java.awt.geom.PathIterator
     * WIND_NON_ZERO rule.
     */
    public static int windingRule           = java.awt.geom.PathIterator.WIND_NON_ZERO;

    /**
     * Indicates whether the orientation attribute is specifed
     * in degrees rather than radians. Degrees is the default.
     */
    public static boolean orientationInDegrees          = true;
    /**
     * Indicates whether the rotation attribute is specifed
     * in degrees rather than radians. Degrees is the default.
     */
    public static boolean rotationInDegrees             = true;

    /**
     * Indicates whether only the list of attributes found in the
     * PRINTLIST_ATTR should be printed. See also printVisibleOnly.
     * The default is false.
     */
    public static boolean usePrintList              = false;

    /**
     * Indicates whether only visible elements should be included
     * when printing a graph.
     * The default is false.
     */
    public static boolean printVisibleOnly          = false;

    /**
     * Indicates whether anti-aliasing should be used when drawing.
     * The default is true.
     */
    public static boolean useAntiAliasing           = true;

    /**
     * Indicates whether anti-aliasing should be used when drawing text.
     * The default is false.
     */
    public static boolean antiAliasText         = false;
    /**
     * Indicates whether fractional metrics should be used when drawing text.
     * The default is false.
     */
    public static boolean useFractionalMetrics      = false;

    /**
     * Indicates whether the value of y-coordinates should be negated
     * when reading or writing y-coord information as string attributes.
     * Note: this indicator should be set to true when working
     * with string attributes generated by or to be read by the dot
     * graph layout program or to be compatible to earlier versions of Grappa.
     * The default is true.
     */
    public static boolean negateStringYCoord        = true;

    /**
     * Indicates that graph labels, when not explicitly positioned via
     * the lp attribute, should be placed at the bottom of the
     * graph instead of the top. The default is true, meaning the label
     * will be placed at the bottom of the graph.
     */
    public static boolean labelGraphBottom          = true;
    /**
     * Indicates that graph labels, when not explicitly positioned via
     * the lp attribute, should be placed just outside the
     * graph bounding box instead of just inside. The default is true,
     * meaning the label will be placed outside the bounding box.
     */
    public static boolean labelGraphOutside         = true;

    /**
     * Indicates that background drawing, if any is provided via
     * a GrappaBacker implementation, should be displayed
     * or not. The default of true means the background drawing should
     * be displayed, if provided.
     */
    public static boolean backgroundDrawing         = true;

    /**
     * When the transform scale applied when drawing in a GrappaPanel is
     * less than this value, then node labels are suppressed.
     * 
     */
    public static double nodeLabelsScaleCutoff      = 0.5;

    /**
     * Cluster subgraphs will have their bounding box outlined. To
     * similarly outline all types of subgraphs (except the root subgraph),
     * set this value to true.
     * 
     */
    public static boolean outlineSubgraphs      = false;

    /**
     * When the transform scale applied when drawing in a GrappaPanel is
     * less than this value, then edge labels are suppressed.
     * 
     */
    public static double edgeLabelsScaleCutoff      = 0.5;

    /**
     * When the transform scale applied when drawing in a GrappaPanel is
     * less than this value, then subgraph labels are suppressed.
     * 
     */
    public static double subgLabelsScaleCutoff      = 0.3;

    /**
     * Indicates whether paints should be done within a synchronized
     * wrapper. When enable the Graph dropcloth method can be used to
     * prevent paints during certain critical operations.
     *
     * @see Graph#dropcloth(boolan, boolean)
     */
    public static boolean synchronizePaint          = false;

    /**
     * Indicates that an image requested via the IMAGE_ATTR of
     * an element should be loaded before the element is drawn.
     * By default, Grappa will wait.
     *
     * @see GrappaNexus#drawImage
     */
    public static boolean waitForImages         = true;

    /**
     * Indicates which classes of elements are suitable for selection
     * based on cursor position. Value is the logical OR of NODE,
     * EDGE and SUBGRAPH. The default is SUBGRAPH|NODE|EDGE.
     *
     */
    public static int elementSelection          = SUBGRAPH|NODE|EDGE;

}

att/grappa/GrappaAdapter.java
att/grappa/GrappaAdapter.java/*
 *  This software may only be used by you under license from AT&T Corp.
 *  (“AT&T”).  A copy of AT&T’s Source Code Agreement is available at
 *  AT&T’s Internet website having the URL:
 *  
 *  If you received this software without first entering into a license
 *  with AT&T, you have an infringing copy of this software and cannot use
 *  it without violating AT&T’s intellectual property rights.
 */

package att.grappa;

import java.awt.*;
import java.awt.print.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Dimension2D;
import java.awt.geom.Rectangle2D;
import java.awt.event.InputEvent;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.util.*;

/**
 * A convenience class that implements the GrappaListener interface
 * for handling mouse-related activity that occurs on a graph.
 *
 * This particular GrappaListener implementation allows the following
 * interactions with a displayed graph:
 *
 * 


     * 

  •  * display tooltips for each graph element;
     * 

  •  * button-1 click will select an element;
     * 

  •  * button-1 sweep will select several elements;
     * 

  •  * shift-button 1 click will create a node;
     * 

  •  * shift-button 1 drag, when starting in one node and ending in another, will create an edge;
     * 

  •  * button-2 or button-3 click will raise a pop-up option menu with:
     *   

       *   

    •  *   zoom in, zoom out and reset zoom options;
       *   

    •  *   a zoom-to-sweep option, if applicable;
       *   

    •  *   clear selection, enclose the selected items in a new subgraph, 
       *   preview deletion, cancel preview and perform deletion, if at least
       *   one item is selected;
       *   

    •  *   a tooltip on/off toggle option.
       *   

     * 

 *
 * @version 1.2, 26 May 2005; Copyright 1996 – 2005 by AT&T Corp.
 * @author  John Mocenigo, Research @ AT&T Labs
 */
public class GrappaAdapter
    implements GrappaConstants, GrappaListener, ActionListener
{
  /**
   * The method called when a mouse click occurs on a displayed subgraph.
   *
   * @param subg displayed subgraph where action occurred
   * @param elem subgraph element in which action occurred
   * @param pt the point where the action occurred (graph coordinates)
   * @param modifiers mouse modifiers in effect
   * @param clickCount count of mouse clicks that triggered this action
   * @param panel specific panel where the action occurred
   */
    public void grappaClicked(Subgraph subg, Element elem, GrappaPoint pt, int modifiers, int clickCount, GrappaPanel panel) {
    if((modifiers&InputEvent.BUTTON1_MASK) == InputEvent.BUTTON1_MASK) {
        if(clickCount == 1) {
        // looks like Java has a single click occur on the way to a
        // multiple click, so this code always executes (which is
        // not necessarily a bad thing)
        if(subg.getGraph().isSelectable()) {
            if(modifiers == InputEvent.BUTTON1_MASK) {
            // select element
            if(elem == null) {
                if(subg.currentSelection != null) {
                if(subg.currentSelection instanceof Element) {
                    ((Element)(subg.currentSelection)).highlight &= ~HIGHLIGHT_MASK;
                } else {
                    Vector vec = ((Vector)(subg.currentSelection));
                    for(int i = 0; i < vec.size(); i++) {                     ((Element)(vec.elementAt(i))).highlight &= ~HIGHLIGHT_MASK;                     }                 }                 subg.currentSelection = null;                 subg.getGraph().repaint();                 }             } else {                 if(subg.currentSelection != null) {                 if(subg.currentSelection == elem) return;                 if(subg.currentSelection instanceof Element) {                     ((Element)(subg.currentSelection)).highlight &= ~HIGHLIGHT_MASK;                 } else {                     Vector vec = ((Vector)(subg.currentSelection));                     for(int i = 0; i < vec.size(); i++) {                     ((Element)(vec.elementAt(i))).highlight &= ~HIGHLIGHT_MASK;                     }                 }                 subg.currentSelection = null;                 }                 elem.highlight |= SELECTION_MASK;                 subg.currentSelection = elem;                 subg.getGraph().repaint();             }             } else if(modifiers == (InputEvent.BUTTON1_MASK|InputEvent.CTRL_MASK)) {             // adjust selection             if(elem != null) {                 if((elem.highlight&SELECTION_MASK) == SELECTION_MASK) {                 // unselect element                 elem.highlight &= ~SELECTION_MASK;                 if(subg.currentSelection == null) {                 // something got messed up somewhere                     throw new InternalError("currentSelection improperly maintained");                 } else if(subg.currentSelection instanceof Element) {                     if(((Element)(subg.currentSelection)) != elem) {                     // something got messed up somewhere                     throw new InternalError("currentSelection improperly maintained");                     }                     subg.currentSelection = null;                 } else {                     Vector vec = ((Vector)(subg.currentSelection));                     boolean problem = true;                     for(int i = 0; i < vec.size(); i++) {                     if(((Element)(vec.elementAt(i))) == elem) {                         vec.removeElementAt(i);                         problem = false;                         break;                     }                     }                     if(problem) {                     // something got messed up somewhere                     throw new InternalError("currentSelection improperly maintained");                     }                 }                 } else {                 // select element                 elem.highlight |= SELECTION_MASK;                 if(subg.currentSelection == null) {                     subg.currentSelection = elem;                 } else if(subg.currentSelection instanceof Element) {                     Object obj = subg.currentSelection;                     subg.currentSelection = new Vector();                     ((Vector)(subg.currentSelection)).add(obj);                     ((Vector)(subg.currentSelection)).add(elem);                 } else {                     ((Vector)(subg.currentSelection)).add(elem);                 }                 }                 subg.getGraph().repaint();             }             }         }         } else {         // multiple clicks         // this code executes for each click beyond the first         //System.err.println("clickCount="+clickCount);         }     }     }   /**    * The method called when a mouse press occurs on a displayed subgraph.    *    * @param subg displayed subgraph where action occurred    * @param elem subgraph element in which action occurred    * @param pt the point where the action occurred (graph coordinates)    * @param modifiers mouse modifiers in effect    * @param panel specific panel where the action occurred    */     public void grappaPressed(Subgraph subg, Element elem, GrappaPoint pt, int modifiers, GrappaPanel panel) {     if((modifiers&(InputEvent.BUTTON2_MASK|InputEvent.BUTTON3_MASK)) != 0 && (modifiers&(InputEvent.BUTTON2_MASK|InputEvent.BUTTON3_MASK)) == modifiers) {         // pop-up menu if button2 or button3         javax.swing.JPopupMenu popup = new javax.swing.JPopupMenu();         javax.swing.JMenuItem item = null;         if(panel.getToolTipText() == null) {         popup.add(item = new javax.swing.JMenuItem("ToolTips On"));         } else {         popup.add(item = new javax.swing.JMenuItem("ToolTips Off"));         }         item.addActionListener(this);         popup.addSeparator();         popup.add(item = new javax.swing.JMenuItem("Print"));         item.addActionListener(this);         popup.addSeparator();         if(subg.currentSelection != null) {         popup.add(item = new javax.swing.JMenuItem("Clear Selection"));         item.addActionListener(this);         popup.addSeparator();         if(subg.currentSelection instanceof Element) {             popup.add(item = new javax.swing.JMenuItem("Select Siblings in Subgraph"));             item.addActionListener(this);             popup.addSeparator();         }         popup.add(item = new javax.swing.JMenuItem("Enclose Selected Items in a new Subgraph"));         item.addActionListener(this);         popup.addSeparator();         popup.add(item = new javax.swing.JMenuItem("Preview Deletion"));         item.addActionListener(this);         popup.add(item = new javax.swing.JMenuItem("Cancel Preview"));         item.addActionListener(this);         popup.add(item = new javax.swing.JMenuItem("Perform Deletion"));         item.addActionListener(this);         popup.addSeparator();         }         if(panel.hasOutline()) {         popup.add(item = new javax.swing.JMenuItem("Zoom to Sweep"));         item.addActionListener(this);         }         popup.add(item = new javax.swing.JMenuItem("Zoom In"));         item.addActionListener(this);         popup.add(item = new javax.swing.JMenuItem("Zoom Out"));         item.addActionListener(this);         popup.add(item = new javax.swing.JMenuItem("Reset Zoom"));         item.addActionListener(this);         popup.add(item = new javax.swing.JMenuItem("Scale to Fit"));         item.addActionListener(this);         if(subg.hasEmptySubgraphs()) {         popup.addSeparator();         popup.add(item = new javax.swing.JMenuItem("Remove Empty Subgraphs"));         item.addActionListener(this);         }         java.awt.geom.Point2D mpt = panel.getTransform().transform(pt,null);         popup.show(panel,(int)mpt.getX(),(int)mpt.getY());     }     }   /**    * The method called when a mouse release occurs on a displayed subgraph.    *    * @param subg displayed subgraph where action occurred    * @param elem subgraph element in which action occurred    * @param pt the point where the action occurred (graph coordinates)    * @param modifiers mouse modifiers in effect    * @param pressedElem subgraph element in which the most recent mouse press occurred    * @param pressedPt the point where the most recent mouse press occurred (graph coordinates)    * @param pressedModifiers mouse modifiers in effect when the most recent mouse press occurred    * @param outline enclosing box specification from the previous drag position (for XOR reset purposes)    * @param panel specific panel where the action occurred    */     public void grappaReleased(Subgraph subg, Element elem, GrappaPoint pt, int modifiers, Element pressedElem, GrappaPoint pressedPt, int pressedModifiers, GrappaBox outline, GrappaPanel panel) {     if(modifiers == InputEvent.BUTTON1_MASK && subg.getGraph().isSelectable()) {         if(outline != null) {         boolean xorOutline = false;         if(subg.currentSelection != null) {             if(subg.currentSelection instanceof Element) {             ((Element)(subg.currentSelection)).highlight = 0;             } else {             Vector vec = ((Vector)(subg.currentSelection));             for(int i = 0; i < vec.size(); i++) {                 ((Element)(vec.elementAt(i))).highlight = 0;             }             }             subg.currentSelection = null;         }         Vector elems = GrappaSupport.findContainedElements(subg, outline);         if(elems != null) {             drillDown(subg, elems, SELECTION_MASK, HIGHLIGHT_ON);             xorOutline = false;         }         if(!xorOutline) {             subg.getGraph().paintImmediately();         }         if(xorOutline) {             Graphics2D g2d = (Graphics2D)(panel.getGraphics());             AffineTransform orig = g2d.getTransform();             g2d.setTransform(panel.getTransform());             g2d.setXORMode(Color.darkGray);             g2d.draw(outline);             g2d.setPaintMode();             g2d.setTransform(orig);         }         }     } else if(modifiers == (InputEvent.BUTTON1_MASK|InputEvent.CTRL_MASK) && subg.getGraph().isSelectable()) {         if(outline != null) {         Vector elems = GrappaSupport.findContainedElements(subg, outline);         if(elems != null) {             drillDown(subg, elems, SELECTION_MASK, HIGHLIGHT_TOGGLE);             subg.getGraph().repaint();         } else {             Graphics2D g2d = (Graphics2D)(panel.getGraphics());             AffineTransform orig = g2d.getTransform();             g2d.setTransform(panel.getTransform());             g2d.setXORMode(Color.darkGray);             g2d.draw(outline);             g2d.setPaintMode();             g2d.setTransform(orig);         }         }     } else if(modifiers == (InputEvent.BUTTON1_MASK|InputEvent.SHIFT_MASK) && subg.getGraph().isEditable()) {         if(elem != null && pressedElem != null) {         if(pressedModifiers == modifiers) {             if(outline == null) {             if(pressedElem == elem && pt.distance(pressedPt) < 5) {                 // [should we only allow elem.isSubgraph()?]                 // create node                 Attribute[] attrs = null;                 Attribute attr = subg.getNodeAttribute(LABEL_ATTR);                 if(attr == null || attr.getValue().equals("\\N")) {                 attrs = new Attribute[] { new Attribute(Grappa.NODE, POS_ATTR, pt), new Attribute(Grappa.NODE, LABEL_ATTR, "Node" + subg.getGraph().getId(Grappa.NODE)) };                 } else {                 attrs = new Attribute[] { new Attribute(Grappa.NODE, POS_ATTR, pt) };                 }                 Element el = subg.createElement(Grappa.NODE,null,attrs);                 if(el != null) {                 el.buildShape();                 subg.getGraph().repaint();                 }                 subg.getGraph().repaint();             } else if(pressedElem != elem && pressedElem.isNode() && elem.isNode()) {                 // create edge                 Object[] info = new Object[] { elem, null, pressedElem };                 Attribute[] attrs = new Attribute[] { new Attribute(Grappa.EDGE, POS_ATTR, new GrappaLine(new GrappaPoint[] { ((Node)pressedElem).getCenterPoint(), ((Node)elem).getCenterPoint() }, subg.getGraph().isDirected()?GrappaLine.TAIL_ARROW_EDGE:GrappaLine.NONE_ARROW_EDGE) ) };                 Element el = subg.createElement(Grappa.EDGE,info,attrs);                 if(el != null) {                 el.buildShape();                 subg.getGraph().repaint();                 }             }             }         }         }     }     }   /**    * The method called when a mouse drag occurs on a displayed subgraph.    *    * @param subg displayed subgraph where action occurred    * @param currentPt the current drag point    * @param currentModifiers the current drag mouse modifiers    * @param pressedElem subgraph element in which the most recent mouse press occurred    * @param pressedPt the point where the most recent mouse press occurred (graph coordinates)    * @param pressedModifiers mouse modifiers in effect when the most recent mouse press occurred    * @param outline enclosing box specification from the previous drag position (for XOR reset purposes)    * @param panel specific panel where the action occurred    */     public void grappaDragged(Subgraph subg, GrappaPoint currentPt, int currentModifiers, Element pressedElem, GrappaPoint pressedPt, int pressedModifiers, GrappaBox outline, GrappaPanel panel) {     if((currentModifiers&InputEvent.BUTTON1_MASK) == InputEvent.BUTTON1_MASK) {         if(currentModifiers == InputEvent.BUTTON1_MASK || currentModifiers == (InputEvent.BUTTON1_MASK|InputEvent.CTRL_MASK)) {         Graphics2D g2d = (Graphics2D)(panel.getGraphics());         AffineTransform orig = g2d.getTransform();         g2d.setTransform(panel.getTransform());         g2d.setXORMode(Color.darkGray);         if(outline != null) {             g2d.draw(outline);         }         GrappaBox box = GrappaSupport.boxFromCorners(pressedPt.x, pressedPt.y, currentPt.x, currentPt.y);         g2d.draw(box);         g2d.setPaintMode();         g2d.setTransform(orig);         }     }     }   /**    * The method called when a element tooltip is needed.    *    * @param subg displayed subgraph where action occurred    * @param elem subgraph element in which action occurred    * @param pt the point where the action occurred (graph coordinates)    * @param modifiers mouse modifiers in effect    * @param panel specific panel where the action occurred    *    * @return the tip to be displayed or null; in this implementation,    * if the mouse is in a graph element that    * has its tip attribute defined, then that text is returned.
   * If that attribute is not set, the element name is returned.
   * If the mouse is outside the graph bounds, then the text supplied
   * to the graph setToolTipText method is supplied.
   */
    public String grappaTip(Subgraph subg, Element elem, GrappaPoint pt, int modifiers, GrappaPanel panel) {
    String tip = null;

    if(elem == null) {
        if((tip = panel.getToolTipText()) == null) {
        if((tip = subg.getGraph().getToolTipText()) == null) {
            tip = Grappa.getToolTipText();
        }
        }
    } else {
        switch(elem.getType()) {
        case SUBGRAPH:
        Subgraph sg = (Subgraph)elem;
        if((tip = (String)sg.getAttributeValue(TIP_ATTR)) == null) {
            if(subg.getShowSubgraphLabels()) {
            tip = sg.getName();
            } else {
            if((tip = (String)sg.getAttributeValue(LABEL_ATTR)) == null) {
                tip = sg.getName();
            }
            }
            tip = “Subgraph: ” + tip;
        }
        break;
        case EDGE:
        Edge edge = (Edge)elem;
        if((tip = (String)edge.getAttributeValue(TIP_ATTR)) == null) {
            if(subg.getShowEdgeLabels()) {
            tip = edge.toString();
            } else {
            if((tip = (String)edge.getAttributeValue(LABEL_ATTR)) == null) {
                tip = edge.toString();
            }
            }
            tip = “Edge: ” + tip;
        }
        break;
        case NODE:
        Node node = (Node)elem;
        if((tip = (String)node.getAttributeValue(TIP_ATTR)) == null) {
            if(subg.getShowNodeLabels()) {
            tip = node.getName();
            } else {
            if((tip = (String)node.getAttributeValue(LABEL_ATTR)) == null || tip.equals(“\\N”)) {
                tip = node.getName();
            }
            }
            tip = “Node: ” + tip;
        }
        break;
        default:
        throw new RuntimeException(“unexpected type (” + elem.getType() + “)”);
        }
    }

    return(tip);
    }

    ///////////////////////////////////////////////////////////////////////////
    //
    // ActionListener
    //
    ///////////////////////////////////////////////////////////////////////////

    /**
     * Invoked when an action occurs.
     *
     * @param aev the action event trigger.
     */
    public void actionPerformed(ActionEvent aev) {
    Object src = aev.getSource();
    if(src instanceof javax.swing.JMenuItem) {
        Object parent = ((javax.swing.JMenuItem)src).getParent();
        if(parent instanceof javax.swing.JPopupMenu) {
        Object invoker = ((javax.swing.JPopupMenu)(((javax.swing.JMenuItem)src).getParent())).getInvoker();
        if(invoker instanceof GrappaPanel) {
            GrappaPanel gp = (GrappaPanel)invoker;
            Subgraph subg = gp.getSubgraph();
            String text = ((javax.swing.JMenuItem)src).getText();
            if(text.startsWith(“Cancel”)) {
                if(subg.currentSelection == null) return;
            if(subg.currentSelection instanceof Element) {
                GrappaSupport.setHighlight((Element)(subg.currentSelection), DELETION_MASK, HIGHLIGHT_OFF); 
            } else {
                Vector vec = (Vector)(subg.currentSelection);
                for(int i = 0; i < vec.size(); i++) {                 GrappaSupport.setHighlight((Element)(vec.elementAt(i)), DELETION_MASK, HIGHLIGHT_OFF);                  }             }             subg.getGraph().repaint();             } else if(text.startsWith("Clear")) {                 if(subg.currentSelection == null) return;             if(subg.currentSelection instanceof Element) {                 GrappaSupport.setHighlight((Element)(subg.currentSelection), 0, HIGHLIGHT_OFF);              } else {                 Vector vec = (Vector)(subg.currentSelection);                 for(int i = 0; i < vec.size(); i++) {                 GrappaSupport.setHighlight((Element)(vec.elementAt(i)), 0, HIGHLIGHT_OFF);                  }             }             subg.currentSelection = null;             subg.getGraph().repaint();             } else if(text.startsWith("Select")) {                 if(subg.currentSelection == null) return;             if(!(subg.currentSelection instanceof Element)) return;             Element elem = ((Element)subg.currentSelection).getSubgraph();             if(elem == null || subg.currentSelection == elem || !(elem instanceof Subgraph)) return;             ((Element)(subg.currentSelection)).highlight &= ~HIGHLIGHT_MASK;             Vector elems = new Vector();             Enumeration enm = ((Subgraph)elem).nodeElements();             while(enm.hasMoreElements())                 elems.add(enm.nextElement());             enm = ((Subgraph)elem).edgeElements();             while(enm.hasMoreElements())                 elems.add(enm.nextElement());             subg.currentSelection = null;             if(elems != null && elems.size() > 0)
                drillDown(subg, elems, SELECTION_MASK, HIGHLIGHT_ON);
            subg.getGraph().repaint();
            } else if(text.startsWith(“Enclose”)) {
                if(subg.currentSelection == null || subg.currentSelection == subg) return;
            Subgraph newsubg = new Subgraph(subg);
            if(subg.currentSelection instanceof Element) {
                GrappaSupport.setHighlight((Element)(subg.currentSelection), 0, HIGHLIGHT_OFF); 
                ((Element)(subg.currentSelection)).setSubgraph(newsubg);
            } else {
                Vector vec = (Vector)(subg.currentSelection);
                for(int i = 0; i < vec.size(); i++) {                 GrappaSupport.setHighlight((Element)(vec.elementAt(i)), 0, HIGHLIGHT_OFF);                  if(((Element)(vec.elementAt(i))) == subg) continue;                 ((Element)(vec.elementAt(i))).setSubgraph(newsubg);                 }             }             subg.currentSelection = null;             subg.getGraph().repaint();             } else if(text.startsWith("Preview")) {                 if(subg.currentSelection == null) return;             if(subg.currentSelection instanceof Element) {                 GrappaSupport.setHighlight((Element)(subg.currentSelection), DELETION_MASK, HIGHLIGHT_ON);              } else {                 Vector vec = (Vector)(subg.currentSelection);                 for(int i = 0; i < vec.size(); i++) {                 GrappaSupport.setHighlight((Element)(vec.elementAt(i)), DELETION_MASK, HIGHLIGHT_ON);                  }             }             subg.getGraph().repaint();             } else if(text.startsWith("Perform")) {                 if(subg.currentSelection == null) return;             if(subg.currentSelection instanceof Element) {                 ((Element)(subg.currentSelection)).delete();             } else {                 Vector vec = (Vector)(subg.currentSelection);                 for(int i = 0; i < vec.size(); i++) {                 ((Element)(vec.elementAt(i))).delete();                 }             }             subg.currentSelection = null;             subg.getGraph().repaint();             } else if(text.startsWith("Remove")) {             subg.removeEmptySubgraphs();             } else if(text.startsWith("Reset")) {             gp.setScaleToFit(false);             gp.setScaleToSize(null);             gp.resetZoom();             gp.clearOutline();             } else if(text.startsWith("Scale")) {             gp.setScaleToFit(true);             } else if(text.startsWith("Print")) {             PageFormat pf = new PageFormat();             Rectangle2D bb = subg.getBoundingBox();             if(bb.getWidth() > bb.getHeight())
                pf.setOrientation(PageFormat.LANDSCAPE);
            try {
                PrinterJob printJob = PrinterJob.getPrinterJob();
                printJob.setPrintable(gp, pf);
                if (printJob.printDialog()) {
                printJob.print();
                }
            } catch (Exception ex) {
                Grappa.displayException(ex, “Problem with print request”);
            }
            } else if(text.startsWith(“ToolTips”)) {
            if(text.indexOf(“Off”) > 0) {
                gp.setToolTipText(null);
            } else {
                String tip = subg.getGraph().getToolTipText();
                if(tip == null) {
                tip = Grappa.getToolTipText();
                }
                gp.setToolTipText(tip);
            }
            } else if(text.startsWith(“Zoom In”)) {
            gp.setScaleToFit(false);
            gp.setScaleToSize(null);
            gp.multiplyScaleFactor(1.25);
            gp.clearOutline();
            } else if(text.startsWith(“Zoom Out”)) {
            gp.setScaleToFit(false);
            gp.setScaleToSize(null);
            gp.multiplyScaleFactor(0.8);
            gp.clearOutline();
            } else if(text.startsWith(“Zoom to”)) {
                //if(subg.currentSelection == null) return;
            gp.setScaleToFit(false);
            gp.setScaleToSize(null);
            gp.zoomToOutline();
            gp.clearOutline();
            }
        }
        }
    }
    }

    ///////////////////////////////////////////////////////////////////////////

    protected void drillDown(Subgraph subg, Vector elems, int mode, int setting) {
    Object obj = null;
    for(int i = 0; i < elems.size(); i++) {         obj = elems.elementAt(i);         if(obj instanceof Vector) {         drillDown(subg, (Vector)obj, mode, setting);         } else {         GrappaSupport.setHighlight(((Element)obj), mode, setting);         switch(setting) {         case HIGHLIGHT_TOGGLE:             if((((Element)obj).highlight&mode) == mode) {             if(subg.currentSelection == null) {                 subg.currentSelection = obj;             } else if(subg.currentSelection instanceof Element) {                 Object crnt = subg.currentSelection;                 subg.currentSelection = new Vector();                 ((Vector)(subg.currentSelection)).add(crnt);                 ((Vector)(subg.currentSelection)).add(obj);             } else {                 ((Vector)(subg.currentSelection)).add(obj);             }             } else {             if(subg.currentSelection == obj) {                 subg.currentSelection = null;             } else if(subg.currentSelection instanceof Vector) {                 ((Vector)(subg.currentSelection)).remove(obj);             }             }             break;         case HIGHLIGHT_ON:             if(subg.currentSelection == null) {             subg.currentSelection = obj;             } else if(subg.currentSelection instanceof Element) {             Object crnt = subg.currentSelection;             subg.currentSelection = new Vector();             ((Vector)(subg.currentSelection)).add(crnt);             ((Vector)(subg.currentSelection)).add(obj);             } else {             ((Vector)(subg.currentSelection)).add(obj);             }             break;         case HIGHLIGHT_OFF:             if(subg.currentSelection != null) {             if(subg.currentSelection == obj) {                 subg.currentSelection = null;             } else if(subg.currentSelection instanceof Vector) {                 ((Vector)(subg.currentSelection)).remove(obj);             }             }             break;         }         }     }     } } att/grappa/GrappaBacker.java att/grappa/GrappaBacker.java/*  *  This software may only be used by you under license from AT&T Corp.  *  ("AT&T").  A copy of AT&T's Source Code Agreement is available at  *  AT&T's Internet website having the URL:  *  
 *  If you received this software without first entering into a license
 *  with AT&T, you have an infringing copy of this software and cannot use
 *  it without violating AT&T’s intellectual property rights.
 */

package att.grappa;

/**
 * An interface for defining an image drawing method to be used for
 * painting the background of the graph.
 *
 * @version 1.2, 26 May 2005; Copyright 1996 – 2005 by AT&T Corp.
 * @author  John Mocenigo, Research @ AT&T Labs
 */
public interface GrappaBacker
{
  /**
   * The method for drawing the background image.
   *
   * @param g2d the graphics context.
   * @param graph the graph being drawn in the foreground.
   * @param bbox the bounding box of the graph.
   * @param clip the clipping shape defining the limits of the area to be drawn.
   */
  public void drawBackground(java.awt.Graphics2D g2d, Graph graph, java.awt.geom.Rectangle2D bbox, java.awt.Shape clip);
}

att/grappa/GrappaBox.java
att/grappa/GrappaBox.java/*
 *  This software may only be used by you under license from AT&T Corp.
 *  (“AT&T”).  A copy of AT&T’s Source Code Agreement is available at
 *  AT&T’s Internet website having the URL:
 *  
 *  If you received this software without first entering into a license
 *  with AT&T, you have an infringing copy of this software and cannot use
 *  it without violating AT&T’s intellectual property rights.
 */

package att.grappa;

/**
 * This class extends java.awt.geom.Rectangle2D.Double and provides built-in
 * string-to-Rectangle2D and Rectangle2D-to-string conversions suitable for Grappa.
 *
 * @version 1.2, 26 May 2005; Copyright 1996 – 2005 by AT&T Corp.
 * @author  John Mocenigo, Research @ AT&T Labs
 */
public class GrappaBox extends java.awt.geom.Rectangle2D.Double
{
    private boolean dimensioned = true;

    /**
     * Constructs and initializes a GrappaBox with
     * upper-left coordinates (0, 0) and zero width and height.
     */
    public GrappaBox() {
    }

    /**
     * Constructs and initializes a GrappaBox from a
     * Rectangle2D
     * @param r the rectangle defining the box (1-to-1 correspondence)
     */
    public GrappaBox(java.awt.geom.Rectangle2D r) {
    this(r.getX(),r.getY(),r.getWidth(),r.getHeight());
    }

    /**
     * Constructs and initializes a GrappaBox with the
     * specified coordinates.
     * @param x, y the upper-left position coordinates of the box
     * @param width, height the size of the box
     */
    public GrappaBox(double x, double y, double width, double height) {
    this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;
    }

    /**
     * Constructs and initializes a GrappaBox with the
     * coordinates derived from the specified String representation.
     * When the dimensioned parameter is true, then the String
     * format should be:
     *    “x-coord,y-coord,width,height
     * otherwise it should be:
     *    “x1-coord,y1-coord,x2-coord,y2-coord
     * @param coordString String representing the coordinates to which to
     * set the newly constructed GrappaBox
     * @param dimensioned a boolean indicating the format of the string
     */
    public GrappaBox(String coordString, boolean dimensioned) {
    this.dimensioned = dimensioned;

    double[] coords = null;
    try {
        coords = GrappaSupport.arrayForTuple(coordString);
    }
    catch(NumberFormatException nfe) {
        throw new IllegalArgumentException(“coordinate string (” + coordString + “) has a bad number format (” + nfe.getMessage() + “)”);
    }
    if(coords == null || coords.length != 4) {
        throw new IllegalArgumentException(“coordinate string (” + coordString + “) does not contain 4 valid coordinates”);
    }
    if(dimensioned) { // x1, y2, width, height
        this.x = coords[0];
        this.y = (Grappa.negateStringYCoord?-coords[1]:coords[1]);
        this.width = coords[2];
        this.height = coords[3];
    } else { // x1, y1, x2, y2
        double tmp;
        if(Grappa.negateStringYCoord) {
        coords[1] = -coords[1];
        coords[3] = -coords[3];
        }
        if(coords[0] > coords[2]) {
        tmp = coords[0];
        coords[0] = coords[2];
        coords[2] = tmp;
        }
        if(coords[1] > coords[3]) {
        tmp = coords[1];
        coords[1] = coords[3];
        coords[3] = tmp;
        }
        this.x = coords[0];
        this.y = coords[1];
        this.width = coords[2] – coords[0];
        this.height = coords[3] – coords[1];
    }
    }

    /**
     * Constructs and initializes a GrappaBox with the
     * coordinates derived from the specified String representation.
     * The String format should be: “x-coord,y-coord,width,height“”
     * @param coordString String representing the coordinates to which to
     * set the newly constructed GrappaBox
     */
    public GrappaBox(String coordString) {
    this(coordString, true);
    }

    /**
     * Provides a string representation of this object consistent 
     * with Grappa attributes.
     *
     * @return attribute-suitable string representation of this GrappaBox.
     */
    public String toAttributeString() {
    return(toFormattedString(“%b”));
    }

    /**
     * Provides a formatted string representation of this object.
     * 
     * @param format the format used to build the string (%b is the base directive for a GrappaBox).
     * @return a string representation of this GrappaBox. 
     */
    public String toFormattedString(String format) {
    return(GrappaSupportPrintf.sprintf(new Object[] { format, this }));
    }

    /**
     * Provides a generic string representation of this object.
     * 
     * @return a generic string representation of this GrappaBox. 
     */
    public String toString() {
    return(x+”,”+y+”,”+width+”,”+height);
    }

    /**
     * Returns true if the String format will be “x1,x2,width,height”
     * or false if the format will be “x1,y1,x2,y2”. The value is
     * determined at creation time.
     * 
     * @return a boolean indicating the format of this Object’s string representation
     */
    public boolean isDimensioned() {
    return(dimensioned);
    }
}

att/grappa/GrappaColor.java
att/grappa/GrappaColor.java/*
 *  This software may only be used by you under license from AT&T Corp.
 *  (“AT&T”).  A copy of AT&T’s Source Code Agreement is available at
 *  AT&T’s Internet website having the URL:
 *  
 *  If you received this software without first entering into a license
 *  with AT&T, you have an infringing copy of this software and cannot use
 *  it without violating AT&T’s intellectual property rights.
 */

package att.grappa;

import java.awt.*;
import java.util.*;

/**
 * This abstract class sets up and provides name-to-color and color-to-name
 * mappings and some associated class methods.
 *
 * @version 1.2, 26 May 2005; Copyright 1996 – 2005 by AT&T Corp.
 * @author  John Mocenigo, Research @ AT&T Labs
 */
public abstract class GrappaColor
{
  // given name, get color
  private static Hashtable colorTable  = new Hashtable(660,10);
  // given color, get name
  private static Hashtable colorLookUp = new Hashtable(660,10);

  // initialize colorTable
  static {
    doAddColor(“aliceblue”,new Color(240,248,255));
    doAddColor(“antiquewhite”,new Color(250,235,215));
    doAddColor(“antiquewhite1”,new Color(255,239,219),false);
    doAddColor(“antiquewhite2”,new Color(238,223,204),false);
    doAddColor(“antiquewhite3”,new Color(205,192,176),false);
    doAddColor(“antiquewhite4”,new Color(139,131,120),false);
    doAddColor(“aquamarine”,new Color(127,255,212));
    doAddColor(“aquamarine1”,new Color(127,255,212),false);
    doAddColor(“aquamarine2”,new Color(118,238,198),false);
    doAddColor(“aquamarine3”,new Color(102,205,170),false);
    doAddColor(“aquamarine4”,new Color(69,139,116),false);
    doAddColor(“azure”,new Color(240,255,255));
    doAddColor(“azure1”,new Color(240,255,255),false);
    doAddColor(“azure2”,new Color(224,238,238),false);
    doAddColor(“azure3”,new Color(193,205,205),false);
    doAddColor(“azure4”,new Color(131,139,139),false);
    doAddColor(“beige”,new Color(245,245,220));
    doAddColor(“bisque”,new Color(255,228,196));
    doAddColor(“bisque1”,new Color(255,228,196),false);
    doAddColor(“bisque2”,new Color(238,213,183),false);
    doAddColor(“bisque3”,new Color(205,183,158),false);
    doAddColor(“bisque4”,new Color(139,125,107),false);
    doAddColor(“black”,new Color(0,0,0));
    doAddColor(“blanchedalmond”,new Color(255,235,205));
    doAddColor(“blue”,new Color(0,0,255));
    doAddColor(“blue1”,new Color(0,0,255),false);
    doAddColor(“blue2”,new Color(0,0,238),false);
    doAddColor(“blue3”,new Color(0,0,205),false);
    doAddColor(“blue4”,new Color(0,0,139),false);
    doAddColor(“blueviolet”,new Color(138,43,226));
    doAddColor(“brown”,new Color(165,42,42));
    doAddColor(“brown1”,new Color(255,64,64),false);
    doAddColor(“brown2”,new Color(238,59,59),false);
    doAddColor(“brown3”,new Color(205,51,51),false);
    doAddColor(“brown4”,new Color(139,35,35),false);
    doAddColor(“burlywood”,new Color(222,184,135));
    doAddColor(“burlywood1”,new Color(255,211,155),false);
    doAddColor(“burlywood2”,new Color(238,197,145),false);
    doAddColor(“burlywood3”,new Color(205,170,125),false);
    doAddColor(“burlywood4”,new Color(139,115,85),false);
    doAddColor(“cadetblue”,new Color(95,158,160));
    doAddColor(“cadetblue1”,new Color(152,245,255),false);
    doAddColor(“cadetblue2”,new Color(142,229,238),false);
    doAddColor(“cadetblue3”,new Color(122,197,205),false);
    doAddColor(“cadetblue4”,new Color(83,134,139),false);
    doAddColor(“chartreuse”,new Color(127,255,0));
    doAddColor(“chartreuse1”,new Color(127,255,0),false);
    doAddColor(“chartreuse2”,new Color(118,238,0),false);
    doAddColor(“chartreuse3”,new Color(102,205,0),false);
    doAddColor(“chartreuse4”,new Color(69,139,0),false);
    doAddColor(“chocolate”,new Color(210,105,30));
    doAddColor(“chocolate1”,new Color(255,127,36),false);
    doAddColor(“chocolate2”,new Color(238,118,33),false);
    doAddColor(“chocolate3”,new Color(205,102,29),false);
    doAddColor(“chocolate4”,new Color(139,69,19),false);
    doAddColor(“coral”,new Color(255,127,80));
    doAddColor(“coral1”,new Color(255,114,86),false);
    doAddColor(“coral2”,new Color(238,106,80),false);
    doAddColor(“coral3”,new Color(205,91,69),false);
    doAddColor(“coral4”,new Color(139,62,47),false);
    doAddColor(“cornflowerblue”,new Color(100,149,237));
    doAddColor(“cornsilk”,new Color(255,248,220));
    doAddColor(“cornsilk1”,new Color(255,248,220),false);
    doAddColor(“cornsilk2”,new Color(238,232,205),false);
    doAddColor(“cornsilk3”,new Color(205,200,177),false);
    doAddColor(“cornsilk4”,new Color(139,136,120),false);
    doAddColor(“crimson”,new Color(220,20,60));
    doAddColor(“cyan”,new Color(0,255,255));
    doAddColor(“cyan1”,new Color(0,255,255),false);
    doAddColor(“cyan2”,new Color(0,238,238),false);
    doAddColor(“cyan3”,new Color(0,205,205),false);
    doAddColor(“cyan4”,new Color(0,139,139),false);
    doAddColor(“darkblue”,new Color(0,0,139));
    doAddColor(“darkcyan”,new Color(0,139,139));
    doAddColor(“darkgoldenrod”,new Color(184,134,11));
    doAddColor(“darkgoldenrod1”,new Color(255,185,15),false);
    doAddColor(“darkgoldenrod2”,new Color(238,173,14),false);
    doAddColor(“darkgoldenrod3”,new Color(205,149,12),false);
    doAddColor(“darkgoldenrod4”,new Color(139,101,8),false);
    doAddColor(“darkgray”,new Color(169,169,169));
    doAddColor(“darkgreen”,new Color(0,100,0));
    doAddColor(“darkgrey”,new Color(169,169,169),false);
    doAddColor(“darkkhaki”,new Color(189,183,107));
    doAddColor(“darkmagenta”,new Color(139,0,139));
    doAddColor(“darkolivegreen”,new Color(85,107,47));
    doAddColor(“darkolivegreen1”,new Color(202,255,112),false);
    doAddColor(“darkolivegreen2”,new Color(188,238,104),false);
    doAddColor(“darkolivegreen3”,new Color(162,205,90),false);
    doAddColor(“darkolivegreen4”,new Color(110,139,61),false);
    doAddColor(“darkorange”,new Color(255,140,0));
    doAddColor(“darkorange1”,new Color(255,127,0),false);
    doAddColor(“darkorange2”,new Color(238,118,0),false);
    doAddColor(“darkorange3”,new Color(205,102,0),false);
    doAddColor(“darkorange4”,new Color(139,69,0),false);
    doAddColor(“darkorchid”,new Color(153,50,204));
    doAddColor(“darkorchid1”,new Color(191,62,255),false);
    doAddColor(“darkorchid2”,new Color(178,58,238),false);
    doAddColor(“darkorchid3”,new Color(154,50,205),false);
    doAddColor(“darkorchid4”,new Color(104,34,139),false);
    doAddColor(“darkred”,new Color(139,0,0));
    doAddColor(“darksalmon”,new Color(233,150,122));
    doAddColor(“darkseagreen”,new Color(143,188,143));
    doAddColor(“darkseagreen1”,new Color(193,255,193),false);
    doAddColor(“darkseagreen2”,new Color(180,238,180),false);
    doAddColor(“darkseagreen3”,new Color(155,205,155),false);
    doAddColor(“darkseagreen4”,new Color(105,139,105),false);
    doAddColor(“darkslateblue”,new Color(72,61,139));
    doAddColor(“darkslategray”,new Color(47,79,79));
    doAddColor(“darkslategray1”,new Color(151,255,255),false);
    doAddColor(“darkslategray2”,new Color(141,238,238),false);
    doAddColor(“darkslategray3”,new Color(121,205,205),false);
    doAddColor(“darkslategray4”,new Color(82,139,139),false);
    doAddColor(“darkslategrey”,new Color(47,79,79),false);
    doAddColor(“darkturquoise”,new Color(0,206,209));
    doAddColor(“darkviolet”,new Color(148,0,211));
    doAddColor(“deeppink”,new Color(255,20,147));
    doAddColor(“deeppink1”,new Color(255,20,147),false);
    doAddColor(“deeppink2”,new Color(238,18,137),false);
    doAddColor(“deeppink3”,new Color(205,16,118),false);
    doAddColor(“deeppink4”,new Color(139,10,80),false);
    doAddColor(“deepskyblue”,new Color(0,191,255));
    doAddColor(“deepskyblue1”,new Color(0,191,255),false);
    doAddColor(“deepskyblue2”,new Color(0,178,238),false);
    doAddColor(“deepskyblue3”,new Color(0,154,205),false);
    doAddColor(“deepskyblue4”,new Color(0,104,139),false);
    doAddColor(“dimgray”,new Color(105,105,105));
    doAddColor(“dimgrey”,new Color(105,105,105),false);
    doAddColor(“dodgerblue”,new Color(30,144,255));
    doAddColor(“dodgerblue1”,new Color(30,144,255),false);
    doAddColor(“dodgerblue2”,new Color(28,134,238),false);
    doAddColor(“dodgerblue3”,new Color(24,116,205),false);
    doAddColor(“dodgerblue4”,new Color(16,78,139),false);
    doAddColor(“firebrick”,new Color(178,34,34));
    doAddColor(“firebrick1”,new Color(255,48,48),false);
    doAddColor(“firebrick2”,new Color(238,44,44),false);
    doAddColor(“firebrick3”,new Color(205,38,38),false);
    doAddColor(“firebrick4”,new Color(139,26,26),false);
    doAddColor(“floralwhite”,new Color(255,250,240));
    doAddColor(“forestgreen”,new Color(34,139,34));
    doAddColor(“gainsboro”,new Color(220,220,220));
    doAddColor(“ghostwhite”,new Color(248,248,255));
    doAddColor(“gold”,new Color(255,215,0));
    doAddColor(“gold1”,new Color(255,215,0),false);
    doAddColor(“gold2”,new Color(238,201,0),false);
    doAddColor(“gold3”,new Color(205,173,0),false);
    doAddColor(“gold4”,new Color(139,117,0),false);
    doAddColor(“goldenrod”,new Color(218,165,32));
    doAddColor(“goldenrod1”,new Color(255,193,37),false);
    doAddColor(“goldenrod2”,new Color(238,180,34),false);
    doAddColor(“goldenrod3”,new Color(205,155,29),false);
    doAddColor(“goldenrod4”,new Color(139,105,20),false);
    doAddColor(“green”,new Color(0,255,0));
    doAddColor(“green1”,new Color(0,255,0),false);
    doAddColor(“green2”,new Color(0,238,0),false);
    doAddColor(“green3”,new Color(0,205,0),false);
    doAddColor(“green4”,new Color(0,139,0),false);
    doAddColor(“greenyellow”,new Color(173,255,47));
    doAddColor(“gray”,new Color(190,190,190));
    doAddColor(“grey”,new Color(190,190,190),false);
    doAddColor(“gray0”,new Color(0,0,0),false);
    doAddColor(“grey0”,new Color(0,0,0),false);
    doAddColor(“gray1”,new Color(3,3,3),false);
    doAddColor(“grey1”,new Color(3,3,3),false);
    doAddColor(“gray2”,new Color(5,5,5),false);
    doAddColor(“grey2”,new Color(5,5,5),false);
    doAddColor(“gray3”,new Color(8,8,8),false);
    doAddColor(“grey3”,new Color(8,8,8),false);
    doAddColor(“gray4”,new Color(10,10,10),false);
    doAddColor(“grey4”,new Color(10,10,10),false);
    doAddColor(“gray5”,new Color(13,13,13),false);
    doAddColor(“grey5”,new Color(13,13,13),false);
    doAddColor(“gray6”,new Color(15,15,15),false);
    doAddColor(“grey6”,new Color(15,15,15),false);
    doAddColor(“gray7”,new Color(18,18,18),false);
    doAddColor(“grey7”,new Color(18,18,18),false);
    doAddColor(“gray8”,new Color(20,20,20),false);
    doAddColor(“grey8”,new Color(20,20,20),false);
    doAddColor(“gray9”,new Color(23,23,23),false);
    doAddColor(“grey9”,new Color(23,23,23),false);
    doAddColor(“gray10”,new Color(26,26,26),false);
    doAddColor(“grey10”,new Color(26,26,26),false);
    doAddColor(“gray11”,new Color(28,28,28),false);
    doAddColor(“grey11”,new Color(28,28,28),false);
    doAddColor(“gray12”,new Color(31,31,31),false);
    doAddColor(“grey12”,new Color(31,31,31),false);
    doAddColor(“gray13”,new Color(33,33,33),false);
    doAddColor(“grey13”,new Color(33,33,33),false);
    doAddColor(“gray14”,new Color(36,36,36),false);
    doAddColor(“grey14”,new Color(36,36,36),false);
    doAddColor(“gray15”,new Color(38,38,38),false);
    doAddColor(“grey15”,new Color(38,38,38),false);
    doAddColor(“gray16”,new Color(41,41,41),false);
    doAddColor(“grey16”,new Color(41,41,41),false);
    doAddColor(“gray17”,new Color(43,43,43),false);
    doAddColor(“grey17”,new Color(43,43,43),false);
    doAddColor(“gray18”,new Color(46,46,46),false);
    doAddColor(“grey18”,new Color(46,46,46),false);
    doAddColor(“gray19”,new Color(48,48,48),false);
    doAddColor(“grey19”,new Color(48,48,48),false);
    doAddColor(“gray20”,new Color(51,51,51),false);
    doAddColor(“grey20”,new Color(51,51,51),false);
    doAddColor(“gray21”,new Color(54,54,54),false);
    doAddColor(“grey21”,new Color(54,54,54),false);
    doAddColor(“gray22”,new Color(56,56,56),false);
    doAddColor(“grey22”,new Color(56,56,56),false);
    doAddColor(“gray23”,new Color(59,59,59),false);
    doAddColor(“grey23”,new Color(59,59,59),false);
    doAddColor(“gray24”,new Color(61,61,61),false);
    doAddColor(“grey24”,new Color(61,61,61),false);
    doAddColor(“gray25”,new Color(64,64,64),false);
    doAddColor(“grey25”,new Color(64,64,64),false);
    doAddColor(“gray26”,new Color(66,66,66),false);
    doAddColor(“grey26”,new Color(66,66,66),false);
    doAddColor(“gray27”,new Color(69,69,69),false);
    doAddColor(“grey27”,new Color(69,69,69),false);
    doAddColor(“gray28”,new Color(71,71,71),false);
    doAddColor(“grey28”,new Color(71,71,71),false);
    doAddColor(“gray29”,new Color(74,74,74),false);
    doAddColor(“grey29”,new Color(74,74,74),false);
    doAddColor(“gray30”,new Color(77,77,77),false);
    doAddColor(“grey30”,new Color(77,77,77),false);
    doAddColor(“gray31”,new Color(79,79,79),false);
    doAddColor(“grey31”,new Color(79,79,79),false);
    doAddColor(“gray32”,new Color(82,82,82),false);
    doAddColor(“grey32”,new Color(82,82,82),false);
    doAddColor(“gray33”,new Color(84,84,84),false);
    doAddColor(“grey33”,new Color(84,84,84),false);
    doAddColor(“gray34”,new Color(87,87,87),false);
    doAddColor(“grey34”,new Color(87,87,87),false);
    doAddColor(“gray35”,new Color(89,89,89),false);
    doAddColor(“grey35”,new Color(89,89,89),false);
    doAddColor(“gray36”,new Color(92,92,92),false);
    doAddColor(“grey36”,new Color(92,92,92),false);
    doAddColor(“gray37”,new Color(94,94,94),false);
    doAddColor(“grey37”,new Color(94,94,94),false);
    doAddColor(“gray38”,new Color(97,97,97),false);
    doAddColor(“grey38”,new Color(97,97,97),false);
    doAddColor(“gray39”,new Color(99,99,99),false);
    doAddColor(“grey39”,new Color(99,99,99),false);
    doAddColor(“gray40”,new Color(102,102,102),false);
    doAddColor(“grey40”,new Color(102,102,102),false);
    doAddColor(“gray41”,new Color(105,105,105),false);
    doAddColor(“grey41”,new Color(105,105,105),false);
    doAddColor(“gray42”,new Color(107,107,107),false);
    doAddColor(“grey42”,new Color(107,107,107),false);
    doAddColor(“gray43”,new Color(110,110,110),false);
    doAddColor(“grey43”,new Color(110,110,110),false);
    doAddColor(“gray44”,new Color(112,112,112),false);
    doAddColor(“grey44”,new Color(112,112,112),false);
    doAddColor(“gray45”,new Color(115,115,115),false);
    doAddColor(“grey45”,new Color(115,115,115),false);
    doAddColor(“gray46”,new Color(117,117,117),false);
    doAddColor(“grey46”,new Color(117,117,117),false);
    doAddColor(“gray47”,new Color(120,120,120),false);
    doAddColor(“grey47”,new Color(120,120,120),false);
    doAddColor(“gray48”,new Color(122,122,122),false);
    doAddColor(“grey48”,new Color(122,122,122),false);
    doAddColor(“gray49”,new Color(125,125,125),false);
    doAddColor(“grey49”,new Color(125,125,125),false);
    doAddColor(“gray50”,new Color(127,127,127),false);
    doAddColor(“grey50”,new Color(127,127,127),false);
    doAddColor(“gray51”,new Color(130,130,130),false);
    doAddColor(“grey51”,new Color(130,130,130),false);
    doAddColor(“gray52”,new Color(133,133,133),false);
    doAddColor(“grey52”,new Color(133,133,133),false);
    doAddColor(“gray53”,new Color(135,135,135),false);
    doAddColor(“grey53”,new Color(135,135,135),false);
    doAddColor(“gray54”,new Color(138,138,138),false);
    doAddColor(“grey54”,new Color(138,138,138),false);
    doAddColor(“gray55”,new Color(140,140,140),false);
    doAddColor(“grey55”,new Color(140,140,140),false);
    doAddColor(“gray56”,new Color(143,143,143),false);
    doAddColor(“grey56”,new Color(143,143,143),false);
    doAddColor(“gray57”,new Color(145,145,145),false);
    doAddColor(“grey57”,new Color(145,145,145),false);
    doAddColor(“gray58”,new Color(148,148,148),false);
    doAddColor(“grey58”,new Color(148,148,148),false);
    doAddColor(“gray59”,new Color(150,150,150),false);
    doAddColor(“grey59”,new Color(150,150,150),false);
    doAddColor(“gray60”,new Color(153,153,153),false);
    doAddColor(“grey60”,new Color(153,153,153),false);
    doAddColor(“gray61”,new Color(156,156,156),false);
    doAddColor(“grey61”,new Color(156,156,156),false);
    doAddColor(“gray62”,new Color(158,158,158),false);
    doAddColor(“grey62”,new Color(158,158,158),false);
    doAddColor(“gray63”,new Color(161,161,161),false);
    doAddColor(“grey63”,new Color(161,161,161),false);
    doAddColor(“gray64”,new Color(163,163,163),false);
    doAddColor(“grey64”,new Color(163,163,163),false);
    doAddColor(“gray65”,new Color(166,166,166),false);
    doAddColor(“grey65”,new Color(166,166,166),false);
    doAddColor(“gray66”,new Color(168,168,168),false);
    doAddColor(“grey66”,new Color(168,168,168),false);
    doAddColor(“gray67”,new Color(171,171,171),false);
    doAddColor(“grey67”,new Color(171,171,171),false);
    doAddColor(“gray68”,new Color(173,173,173),false);
    doAddColor(“grey68”,new Color(173,173,173),false);
    doAddColor(“gray69”,new Color(176,176,176),false);
    doAddColor(“grey69”,new Color(176,176,176),false);
    doAddColor(“gray70”,new Color(179,179,179),false);
    doAddColor(“grey70”,new Color(179,179,179),false);
    doAddColor(“gray71”,new Color(181,181,181),false);
    doAddColor(“grey71”,new Color(181,181,181),false);
    doAddColor(“gray72”,new Color(184,184,184),false);
    doAddColor(“grey72”,new Color(184,184,184),false);
    doAddColor(“gray73”,new Color(186,186,186),false);
    doAddColor(“grey73”,new Color(186,186,186),false);
    doAddColor(“gray74”,new Color(189,189,189),false);
    doAddColor(“grey74”,new Color(189,189,189),false);
    doAddColor(“gray75”,new Color(191,191,191),false);
    doAddColor(“grey75”,new Color(191,191,191),false);
    doAddColor(“gray76”,new Color(194,194,194),false);
    doAddColor(“grey76”,new Color(194,194,194),false);
    doAddColor(“gray77”,new Color(196,196,196),false);
    doAddColor(“grey77”,new Color(196,196,196),false);
    doAddColor(“gray78”,new Color(199,199,199),false);
    doAddColor(“grey78”,new Color(199,199,199),false);
    doAddColor(“gray79”,new Color(201,201,201),false);
    doAddColor(“grey79”,new Color(201,201,201),false);
    doAddColor(“gray80”,new Color(204,204,204),false);
    doAddColor(“grey80”,new Color(204,204,204),false);
    doAddColor(“gray81”,new Color(207,207,207),false);
    doAddColor(“grey81”,new Color(207,207,207),false);
    doAddColor(“gray82”,new Color(209,209,209),false);
    doAddColor(“grey82”,new Color(209,209,209),false);
    doAddColor(“gray83”,new Color(212,212,212),false);
    doAddColor(“grey83”,new Color(212,212,212),false);
    doAddColor(“gray84”,new Color(214,214,214),false);
    doAddColor(“grey84”,new Color(214,214,214),false);
    doAddColor(“gray85”,new Color(217,217,217),false);
    doAddColor(“grey85”,new Color(217,217,217),false);
    doAddColor(“gray86”,new Color(219,219,219),false);
    doAddColor(“grey86”,new Color(219,219,219),false);
    doAddColor(“gray87”,new Color(222,222,222),false);
    doAddColor(“grey87”,new Color(222,222,222),false);
    doAddColor(“gray88”,new Color(224,224,224),false);
    doAddColor(“grey88”,new Color(224,224,224),false);
    doAddColor(“gray89”,new Color(227,227,227),false);
    doAddColor(“grey89”,new Color(227,227,227),false);
    doAddColor(“gray90”,new Color(229,229,229),false);
    doAddColor(“grey90”,new Color(229,229,229),false);
    doAddColor(“gray91”,new Color(232,232,232),false);
    doAddColor(“grey91”,new Color(232,232,232),false);
    doAddColor(“gray92”,new Color(235,235,235),false);
    doAddColor(“grey92”,new Color(235,235,235),false);
    doAddColor(“gray93”,new Color(237,237,237),false);
    doAddColor(“grey93”,new Color(237,237,237),false);
    doAddColor(“gray94”,new Color(240,240,240),false);
    doAddColor(“grey94”,new Color(240,240,240),false);
    doAddColor(“gray95”,new Color(242,242,242),false);
    doAddColor(“grey95”,new Color(242,242,242),false);
    doAddColor(“gray96”,new Color(245,245,245),false);
    doAddColor(“grey96”,new Color(245,245,245),false);
    doAddColor(“gray97”,new Color(247,247,247),false);
    doAddColor(“grey97”,new Color(247,247,247),false);
    doAddColor(“gray98”,new Color(250,250,250),false);
    doAddColor(“grey98”,new Color(250,250,250),false);
    doAddColor(“gray99”,new Color(252,252,252),false);
    doAddColor(“grey99”,new Color(252,252,252),false);
    doAddColor(“gray100”,new Color(255,255,255),false);
    doAddColor(“grey100”,new Color(255,255,255),false);
    doAddColor(“honeydew”,new Color(240,255,240));
    doAddColor(“honeydew1”,new Color(240,255,240),false);
    doAddColor(“honeydew2”,new Color(224,238,224),false);
    doAddColor(“honeydew3”,new Color(193,205,193),false);
    doAddColor(“honeydew4”,new Color(131,139,131),false);
    doAddColor(“hotpink”,new Color(255,105,180));
    doAddColor(“hotpink1”,new Color(255,110,180),false);
    doAddColor(“hotpink2”,new Color(238,106,167),false);
    doAddColor(“hotpink3”,new Color(205,96,144),false);
    doAddColor(“hotpink4”,new Color(139,58,98),false);
    doAddColor(“indianred”,new Color(205,92,92));
    doAddColor(“indianred1”,new Color(255,106,106),false);
    doAddColor(“indianred2”,new Color(238,99,99),false);
    doAddColor(“indianred3”,new Color(205,85,85),false);
    doAddColor(“indianred4”,new Color(139,58,58),false);
    doAddColor(“indigo”,new Color(75,0,130));
    doAddColor(“ivory”,new Color(255,255,240));
    doAddColor(“ivory1”,new Color(255,255,240),false);
    doAddColor(“ivory2”,new Color(238,238,224),false);
    doAddColor(“ivory3”,new Color(205,205,193),false);
    doAddColor(“ivory4”,new Color(139,139,131),false);
    doAddColor(“khaki”,new Color(240,230,140));
    doAddColor(“khaki1”,new Color(255,246,143),false);
    doAddColor(“khaki2”,new Color(238,230,133),false);
    doAddColor(“khaki3”,new Color(205,198,115),false);
    doAddColor(“khaki4”,new Color(139,134,78),false);
    doAddColor(“lavender”,new Color(230,230,250));
    doAddColor(“lavenderblush”,new Color(255,240,245));
    doAddColor(“lavenderblush1”,new Color(255,240,245),false);
    doAddColor(“lavenderblush2”,new Color(238,224,229),false);
    doAddColor(“lavenderblush3”,new Color(205,193,197),false);
    doAddColor(“lavenderblush4”,new Color(139,131,134),false);
    doAddColor(“lawngreen”,new Color(124,252,0));
    doAddColor(“lemonchiffon”,new Color(255,250,205));
    doAddColor(“lemonchiffon1”,new Color(255,250,205),false);
    doAddColor(“lemonchiffon2”,new Color(238,233,191),false);
    doAddColor(“lemonchiffon3”,new Color(205,201,165),false);
    doAddColor(“lemonchiffon4”,new Color(139,137,112),false);
    doAddColor(“lightblue”,new Color(173,216,230));
    doAddColor(“lightblue1”,new Color(191,239,255),false);
    doAddColor(“lightblue2”,new Color(178,223,238),false);
    doAddColor(“lightblue3”,new Color(154,192,205),false);
    doAddColor(“lightblue4”,new Color(104,131,139),false);
    doAddColor(“lightcoral”,new Color(240,128,128));
    doAddColor(“lightcyan”,new Color(224,255,255));
    doAddColor(“lightcyan1”,new Color(224,255,255),false);
    doAddColor(“lightcyan2”,new Color(209,238,238),false);
    doAddColor(“lightcyan3”,new Color(180,205,205),false);
    doAddColor(“lightcyan4”,new Color(122,139,139),false);
    doAddColor(“lightgoldenrod”,new Color(238,221,130));
    doAddColor(“lightgoldenrod1”,new Color(255,236,139),false);
    doAddColor(“lightgoldenrod2”,new Color(238,220,130),false);
    doAddColor(“lightgoldenrod3”,new Color(205,190,112),false);
    doAddColor(“lightgoldenrod4”,new Color(139,129,76),false);
    doAddColor(“lightgoldenrodyellow”,new Color(250,250,210));
    doAddColor(“lightgray”,new Color(211,211,211));
    doAddColor(“lightgreen”,new Color(144,238,144));
    doAddColor(“lightgrey”,new Color(211,211,211),false);
    doAddColor(“lightpink”,new Color(255,182,193));
    doAddColor(“lightpink1”,new Color(255,174,185),false);
    doAddColor(“lightpink2”,new Color(238,162,173),false);
    doAddColor(“lightpink3”,new Color(205,140,149),false);
    doAddColor(“lightpink4”,new Color(139,95,101),false);
    doAddColor(“lightsalmon”,new Color(255,160,122));
    doAddColor(“lightsalmon1”,new Color(255,160,122),false);
    doAddColor(“lightsalmon2”,new Color(238,149,114),false);
    doAddColor(“lightsalmon3”,new Color(205,129,98),false);
    doAddColor(“lightsalmon4”,new Color(139,87,66),false);
    doAddColor(“lightseagreen”,new Color(32,178,170));
    doAddColor(“lightskyblue”,new Color(135,206,250));
    doAddColor(“lightskyblue1”,new Color(176,226,255),false);
    doAddColor(“lightskyblue2”,new Color(164,211,238),false);
    doAddColor(“lightskyblue3”,new Color(141,182,205),false);
    doAddColor(“lightskyblue4”,new Color(96,123,139),false);
    doAddColor(“lightslateblue”,new Color(132,112,255));
    doAddColor(“lightslategray”,new Color(119,136,153));
    doAddColor(“lightslategrey”,new Color(119,136,153),false);
    doAddColor(“lightsteelblue”,new Color(176,196,222));
    doAddColor(“lightsteelblue1”,new Color(202,225,255),false);
    doAddColor(“lightsteelblue2”,new Color(188,210,238),false);
    doAddColor(“lightsteelblue3”,new Color(162,181,205),false);
    doAddColor(“lightsteelblue4”,new Color(110,123,139),false);
    doAddColor(“lightyellow”,new Color(255,255,224));
    doAddColor(“lightyellow1”,new Color(255,255,224),false);
    doAddColor(“lightyellow2”,new Color(238,238,209),false);
    doAddColor(“lightyellow3”,new Color(205,205,180),false);
    doAddColor(“lightyellow4”,new Color(139,139,122),false);
    doAddColor(“limegreen”,new Color(50,205,50));
    doAddColor(“linen”,new Color(250,240,230));
    doAddColor(“magenta”,new Color(255,0,255));
    doAddColor(“magenta1”,new Color(255,0,255),false);
    doAddColor(“magenta2”,new Color(238,0,238),false);
    doAddColor(“magenta3”,new Color(205,0,205),false);
    doAddColor(“magenta4”,new Color(139,0,139),false);
    doAddColor(“maroon”,new Color(176,48,96));
    doAddColor(“maroon1”,new Color(255,52,179),false);
    doAddColor(“maroon2”,new Color(238,48,167),false);
    doAddColor(“maroon3”,new Color(205,41,144),false);
    doAddColor(“maroon4”,new Color(139,28,98),false);
    doAddColor(“mediumaquamarine”,new Color(102,205,170));
    doAddColor(“mediumblue”,new Color(0,0,205));
    doAddColor(“mediumorchid”,new Color(186,85,211));
    doAddColor(“mediumorchid1”,new Color(224,102,255),false);
    doAddColor(“mediumorchid2”,new Color(209,95,238),false);
    doAddColor(“mediumorchid3”,new Color(180,82,205),false);
    doAddColor(“mediumorchid4”,new Color(122,55,139),false);
    doAddColor(“mediumpurple”,new Color(147,112,219));
    doAddColor(“mediumpurple1”,new Color(171,130,255),false);
    doAddColor(“mediumpurple2”,new Color(159,121,238),false);
    doAddColor(“mediumpurple3”,new Color(137,104,205),false);
    doAddColor(“mediumpurple4”,new Color(93,71,139),false);
    doAddColor(“mediumseagreen”,new Color(60,179,113));
    doAddColor(“mediumslateblue”,new Color(123,104,238));
    doAddColor(“mediumspringgreen”,new Color(0,250,154));
    doAddColor(“mediumturquoise”,new Color(72,209,204));
    doAddColor(“mediumvioletred”,new Color(199,21,133));
    doAddColor(“midnightblue”,new Color(25,25,112));
    doAddColor(“mintcream”,new Color(245,255,250));
    doAddColor(“mistyrose”,new Color(255,228,225));
    doAddColor(“mistyrose1”,new Color(255,228,225),false);
    doAddColor(“mistyrose2”,new Color(238,213,210),false);
    doAddColor(“mistyrose3”,new Color(205,183,181),false);
    doAddColor(“mistyrose4”,new Color(139,125,123),false);
    doAddColor(“moccasin”,new Color(255,228,181));
    doAddColor(“navajowhite”,new Color(255,222,173));
    doAddColor(“navajowhite1”,new Color(255,222,173),false);
    doAddColor(“navajowhite2”,new Color(238,207,161),false);
    doAddColor(“navajowhite3”,new Color(205,179,139),false);
    doAddColor(“navajowhite4”,new Color(139,121,94),false);
    doAddColor(“navy”,new Color(0,0,128));
    doAddColor(“navyblue”,new Color(0,0,128),false);
    doAddColor(“oldlace”,new Color(253,245,230));
    doAddColor(“olivedrab”,new Color(107,142,35));
    doAddColor(“olivedrab1”,new Color(192,255,62),false);
    doAddColor(“olivedrab2”,new Color(179,238,58),false);
    doAddColor(“olivedrab3”,new Color(154,205,50),false);
    doAddColor(“olivedrab4”,new Color(105,139,34),false);
    doAddColor(“orange”,new Color(255,165,0));
    doAddColor(“orange1”,new Color(255,165,0),false);
    doAddColor(“orange2”,new Color(238,154,0),false);
    doAddColor(“orange3”,new Color(205,133,0),false);
    doAddColor(“orange4”,new Color(139,90,0),false);
    doAddColor(“orangered”,new Color(255,69,0));
    doAddColor(“orangered1”,new Color(255,69,0),false);
    doAddColor(“orangered2”,new Color(238,64,0),false);
    doAddColor(“orangered3”,new Color(205,55,0),false);
    doAddColor(“orangered4”,new Color(139,37,0),false);
    doAddColor(“orchid”,new Color(218,112,214));
    doAddColor(“orchid1”,new Color(255,131,250),false);
    doAddColor(“orchid2”,new Color(238,122,233),false);
    doAddColor(“orchid3”,new Color(205,105,201),false);
    doAddColor(“orchid4”,new Color(139,71,137),false);
    doAddColor(“palegoldenrod”,new Color(238,232,170));
    doAddColor(“palegreen”,new Color(152,251,152));
    doAddColor(“palegreen1”,new Color(154,255,154),false);
    doAddColor(“palegreen2”,new Color(144,238,144),false);
    doAddColor(“palegreen3”,new Color(124,205,124),false);
    doAddColor(“palegreen4”,new Color(84,139,84),false);
    doAddColor(“paleturquoise”,new Color(175,238,238));
    doAddColor(“paleturquoise1”,new Color(187,255,255),false);
    doAddColor(“paleturquoise2”,new Color(174,238,238),false);
    doAddColor(“paleturquoise3”,new Color(150,205,205),false);
    doAddColor(“paleturquoise4”,new Color(102,139,139),false);
    doAddColor(“palevioletred”,new Color(219,112,147));
    doAddColor(“palevioletred1”,new Color(255,130,171),false);
    doAddColor(“palevioletred2”,new Color(238,121,159),false);
    doAddColor(“palevioletred3”,new Color(205,104,137),false);
    doAddColor(“palevioletred4”,new Color(139,71,93),false);
    doAddColor(“papayawhip”,new Color(255,239,213));
    doAddColor(“peachpuff”,new Color(255,218,185));
    doAddColor(“peachpuff1”,new Color(255,218,185),false);
    doAddColor(“peachpuff2”,new Color(238,203,173),false);
    doAddColor(“peachpuff3”,new Color(205,175,149),false);
    doAddColor(“peachpuff4”,new Color(139,119,101),false);
    doAddColor(“peru”,new Color(205,133,63));
    doAddColor(“pink”,new Color(255,192,203));
    doAddColor(“pink1”,new Color(255,181,197),false);
    doAddColor(“pink2”,new Color(238,169,184),false);
    doAddColor(“pink3”,new Color(205,145,158),false);
    doAddColor(“pink4”,new Color(139,99,108),false);
    doAddColor(“plum”,new Color(221,160,221));
    doAddColor(“plum1”,new Color(255,187,255),false);
    doAddColor(“plum2”,new Color(238,174,238),false);
    doAddColor(“plum3”,new Color(205,150,205),false);
    doAddColor(“plum4”,new Color(139,102,139),false);
    doAddColor(“powderblue”,new Color(176,224,230));
    doAddColor(“purple”,new Color(160,32,240));
    doAddColor(“purple1”,new Color(155,48,255),false);
    doAddColor(“purple2”,new Color(145,44,238),false);
    doAddColor(“purple3”,new Color(125,38,205),false);
    doAddColor(“purple4”,new Color(85,26,139),false);
    doAddColor(“red”,new Color(255,0,0));
    doAddColor(“red1”,new Color(255,0,0),false);
    doAddColor(“red2”,new Color(238,0,0),false);
    doAddColor(“red3”,new Color(205,0,0),false);
    doAddColor(“red4”,new Color(139,0,0),false);
    doAddColor(“rosybrown”,new Color(188,143,143));
    doAddColor(“rosybrown1”,new Color(255,193,193),false);
    doAddColor(“rosybrown2”,new Color(238,180,180),false);
    doAddColor(“rosybrown3”,new Color(205,155,155),false);
    doAddColor(“rosybrown4”,new Color(139,105,105),false);
    doAddColor(“royalblue”,new Color(65,105,225));
    doAddColor(“royalblue1”,new Color(72,118,255),false);
    doAddColor(“royalblue2”,new Color(67,110,238),false);
    doAddColor(“royalblue3”,new Color(58,95,205),false);
    doAddColor(“royalblue4”,new Color(39,64,139),false);
    doAddColor(“saddlebrown”,new Color(139,69,19));
    doAddColor(“salmon”,new Color(250,128,114));
    doAddColor(“salmon1”,new Color(255,140,105),false);
    doAddColor(“salmon2”,new Color(238,130,98),false);
    doAddColor(“salmon3”,new Color(205,112,84),false);
    doAddColor(“salmon4”,new Color(139,76,57),false);
    doAddColor(“sandybrown”,new Color(244,164,96));
    doAddColor(“seagreen”,new Color(46,139,87));
    doAddColor(“seagreen1”,new Color(84,255,159),false);
    doAddColor(“seagreen2”,new Color(78,238,148),false);
    doAddColor(“seagreen3”,new Color(67,205,128),false);
    doAddColor(“seagreen4”,new Color(46,139,87),false);
    doAddColor(“seashell”,new Color(255,245,238));
    doAddColor(“seashell1”,new Color(255,245,238),false);
    doAddColor(“seashell2”,new Color(238,229,222),false);
    doAddColor(“seashell3”,new Color(205,197,191),false);
    doAddColor(“seashell4”,new Color(139,134,130),false);
    doAddColor(“sgiindigo2”,new Color(33,136,104),false);
    doAddColor(“sienna”,new Color(160,82,45));
    doAddColor(“sienna1”,new Color(255,130,71),false);
    doAddColor(“sienna2”,new Color(238,121,66),false);
    doAddColor(“sienna3”,new Color(205,104,57),false);
    doAddColor(“sienna4”,new Color(139,71,38),false);
    doAddColor(“skyblue”,new Color(135,206,235));
    doAddColor(“skyblue1”,new Color(135,206,255),false);
    doAddColor(“skyblue2”,new Color(126,192,238),false);
    doAddColor(“skyblue3”,new Color(108,166,205),false);
    doAddColor(“skyblue4”,new Color(74,112,139),false);
    doAddColor(“slateblue”,new Color(106,90,205));
    doAddColor(“slateblue1”,new Color(131,111,255),false);
    doAddColor(“slateblue2”,new Color(122,103,238),false);
    doAddColor(“slateblue3”,new Color(105,89,205),false);
    doAddColor(“slateblue4”,new Color(71,60,139),false);
    doAddColor(“slategray”,new Color(112,128,144));
    doAddColor(“slategray1”,new Color(198,226,255),false);
    doAddColor(“slategray2”,new Color(185,211,238),false);
    doAddColor(“slategray3”,new Color(159,182,205),false);
    doAddColor(“slategray4”,new Color(108,123,139),false);
    doAddColor(“slategrey”,new Color(112,128,144),false);
    doAddColor(“snow”,new Color(255,250,250));
    doAddColor(“snow1”,new Color(255,250,250),false);
    doAddColor(“snow2”,new Color(238,233,233),false);
    doAddColor(“snow3”,new Color(205,201,201),false);
    doAddColor(“snow4”,new Color(139,137,137),false);
    doAddColor(“springgreen”,new Color(0,255,127));
    doAddColor(“springgreen1”,new Color(0,255,127),false);
    doAddColor(“springgreen2”,new Color(0,238,118),false);
    doAddColor(“springgreen3”,new Color(0,205,102),false);
    doAddColor(“springgreen4”,new Color(0,139,69),false);
    doAddColor(“steelblue”,new Color(70,130,180));
    doAddColor(“steelblue1”,new Color(99,184,255),false);
    doAddColor(“steelblue2”,new Color(92,172,238),false);
    doAddColor(“steelblue3”,new Color(79,148,205),false);
    doAddColor(“steelblue4”,new Color(54,100,139),false);
    doAddColor(“tan”,new Color(210,180,140));
    doAddColor(“tan1”,new Color(255,165,79),false);
    doAddColor(“tan2”,new Color(238,154,73),false);
    doAddColor(“tan3”,new Color(205,133,63),false);
    doAddColor(“tan4”,new Color(139,90,43),false);
    doAddColor(“thistle”,new Color(216,191,216));
    doAddColor(“thistle1”,new Color(255,225,255),false);
    doAddColor(“thistle2”,new Color(238,210,238),false);
    doAddColor(“thistle3”,new Color(205,181,205),false);
    doAddColor(“thistle4”,new Color(139,123,139),false);
    doAddColor(“tomato”,new Color(255,99,71));
    doAddColor(“tomato1”,new Color(255,99,71),false);
    doAddColor(“tomato2”,new Color(238,92,66),false);
    doAddColor(“tomato3”,new Color(205,79,57),false);
    doAddColor(“tomato4”,new Color(139,54,38),false);
    doAddColor(“turquoise”,new Color(64,224,208));
    doAddColor(“turquoise1”,new Color(0,245,255),false);
    doAddColor(“turquoise2”,new Color(0,229,238),false);
    doAddColor(“turquoise3”,new Color(0,197,205),false);
    doAddColor(“turquoise4”,new Color(0,134,139),false);
    doAddColor(“violet”,new Color(238,130,238));
    doAddColor(“violetred”,new Color(208,32,144));
    doAddColor(“violetred1”,new Color(255,62,150),false);
    doAddColor(“violetred2”,new Color(238,58,140),false);
    doAddColor(“violetred3”,new Color(205,50,120),false);
    doAddColor(“violetred4”,new Color(139,34,82),false);
    doAddColor(“wheat”,new Color(245,222,179));
    doAddColor(“wheat1”,new Color(255,231,186),false);
    doAddColor(“wheat2”,new Color(238,216,174),false);
    doAddColor(“wheat3”,new Color(205,186,150),false);
    doAddColor(“wheat4”,new Color(139,126,102),false);
    doAddColor(“white”,new Color(255,255,255));
    doAddColor(“whitesmoke”,new Color(245,245,245));
    doAddColor(“yellow”,new Color(255,255,0));
    doAddColor(“yellow1”,new Color(255,255,0),false);
    doAddColor(“yellow2”,new Color(238,238,0),false);
    doAddColor(“yellow3”,new Color(205,205,0),false);
    doAddColor(“yellow4”,new Color(139,139,0),false);
    doAddColor(“yellowgreen”,new Color(154,205,50));
  }

  // be sure to specify colors that exist initially in the colorTable
  /**
   * The default foreground color (black).
   */
  public static final Color  defaultForeground = getColor(“black”,null);
  /**
   * The default background color (white).
   */
  public static final Color  defaultBackground = getColor(“white”,null);
  /**
   * The default XOR color (light gray).
   */
  public static final Color  defaultXOR        = getColor(“light gray”,null);
  /**
   * The default font color (black).
   */
  public static final Color  defaultFontcolor  = getColor(“black”,null);
  /**
   * The default color of last resort in all cases (black).
   */
  public static final Color  defaultColor      = getColor(“black”,null);

  /**
   * Adds a color to the application color table. For search purposes, names
   * are canonicalized by converting to lower case and stripping
   * non-alphanumerics.  A name must contains at least one alphabetic.
   * Once in the table, colors can be set by name, and names can be
   * retrieved by color (although a single color referred to by multiple names
   * only causes the retrieval of the last name mapped to that color).
   *
   * @param name the name to be used to reference the color.
   * @param color the Color value.
   */
  public static void addColor(String name, Color color) throws IllegalArgumentException {
    if(name == null || color == null) {
      throw new IllegalArgumentException(“supplied name or color is null”);
    }
    String canonName = canonColor(name, null);
    if(canonName == null) {
      throw new IllegalArgumentException(“supplied name does not contain alphabetics (” + name + “)”);
    }
    doAddColor(canonName,color);
  }

  // performs actual color table puts
  private static void doAddColor(String name, Color color, boolean override) {
    colorTable.put(name,color);
    if(override || colorLookUp.get(color) == null) colorLookUp.put(color,name);
  }

  // convenience version
  private static void doAddColor(String name, Color color) {
      doAddColor(name,color,true);
  }

  // canonicalizes color string (removes non-alphanumeric and lowers case)
  private static String canonColor(String name, float[] hsb) {
    if(hsb != null) {
      hsb[0] = hsb[1] = hsb[2] = -1;
    }
    if(name == null) return null;
    char[] array = name.toCharArray();
    int len = 0;
    int commas = 0;
    int[] commaSpots = new int[3];
    int dots = 0;
    boolean allDigits = true;
    for(int i = 0; i < array.length; i++) {       if(Character.isUpperCase(array[i])) {     array[len++] = Character.toLowerCase(array[i]);     allDigits = false;       } else if(Character.isLowerCase(array[i])) {     array[len++] = array[i];     allDigits = false;       } else if(Character.isDigit(array[i])) {     array[len++] = array[i];       } else if(array[i] == ',') {     if(commas < 2) {       commaSpots[commas] = i;     }     commas++;     array[len++] = array[i];       } else if(array[i] == '.') {     dots++;     array[len++] = array[i];       }     }     if(hsb != null && allDigits && commas == 2 && dots <= 3) {       commaSpots[2] = array.length;       int prev = 0;       try {     for(int i = 0;  i < 3; i++) {       hsb[i] = Float.valueOf(name.substring(prev,commaSpots[i])).floatValue();       prev = commaSpots[i] + 1;     }       } catch(NumberFormatException nfe) {     return null;       }       return new String(array,0,len);     }     if(len == 0 || allDigits) return null;     if(commas > 0 || dots > 0) {
      int l = len;
      len = 0;
      for(int i = 0; i < l; i++) {     if(array[i] != '.' && array[i] != ',') {       array[len++] = array[i];     }       }     }     return new String(array,0,len);   }   /**    * Return the color in the color table with the given name.    * If the color is not found, the supplied default is returned.    * If the supplied default is null, the class default is returned.    * If the name consists of three comma or space separated floating    * point numbers in the range 0 to 1 inclusive, then it is assumed    * to represent an HSB color specification and generated directly.    * The name search is case insensitive and looks at alphanumerics only.    *    * @param name the name of the color to be retrieved.    * @param color the color value to return if requested color    *              is not found.    *    * @return the color matching the name or the default.    */   public static Color getColor(String name, Color color) {     if(color == null) color = defaultColor;          if(name == null) return color;     float[] hsb = new float[3];     String canonName = canonColor(name, hsb);     Color retColor = (Color)colorTable.get(canonName);     if(retColor == null) {       if(hsb[0] < 0) {     retColor = color;       } else {     retColor = Color.getHSBColor(hsb[0],hsb[1],hsb[2]);     if(retColor == null) {       retColor = color;     } else {       doAddColor(canonName,retColor);     }       }     }     return retColor;   }   /**    * Return the name of the supplied color.    *    * @param color the color whose name is to be retrieved.    *    * @return the color's (most recently entered) name, if it is in the    *         color table, or its HSB value string otherwise.    */   public static String getColorName(Color color) {       if(color == null) return null;       String name = (String)(colorLookUp.get(color));       if(name == null) {       float[] hsb = Color.RGBtoHSB(color.getRed(),color.getGreen(),color.getBlue(),null);       name = hsb[0]+","+hsb[1]+","+hsb[2];       }       return(name);   } } att/grappa/GrappaConstants.java att/grappa/GrappaConstants.java/*  *  This software may only be used by you under license from AT&T Corp.  *  ("AT&T").  A copy of AT&T's Source Code Agreement is available at  *  AT&T's Internet website having the URL:  *  
 *  If you received this software without first entering into a license
 *  with AT&T, you have an infringing copy of this software and cannot use
 *  it without violating AT&T’s intellectual property rights.
 */

package att.grappa;

/**
 * This class provides a common set of constant, class variables
 * used by the classes in the grappa package.
 *
 * @version 1.2, 26 May 2005; Copyright 1996 – 2005 by AT&T Corp.
 * @author  John Mocenigo, Research @ AT&T Labs
 */
public interface GrappaConstants
{
    /**
     * Package prefix string (“att.grappa.”) if anyone needs it.
     */
    public final static String PACKAGE_PREFIX = “att.grappa.”;

    /**
     * Package name as an up-low string.
     */
    public final static String PKG_UPLOW = “Grappa”;
    /**
     * Package name as an upper-case string (as a convenience).
     */
    public final static String PKG_UPPER = “GRAPPA”;
    /**
     * Package name as an lower-case string (as a convenience).
     */
    public final static String PKG_LOWER = “grappa”;

    /**
     * The new-line string for this system (as specified by the line.spearator property).
     */
    public final static String NEW_LINE = System.getProperty(“line.separator”);

    /**
     * The unicode no-break space character.
     */
    public final static char NBSP = ‘\u00a0’;

    /**
     * The identity transform (as a convenience).
     */
    public final static java.awt.geom.AffineTransform IDENTXFRM = new java.awt.geom.AffineTransform();

    /*
     * edit operations (NOT IMPLEMENTED YET)
     public final static int EDIT_UNDO              = 0;
     public final static int EDIT_CUT               = 1;
     public final static int EDIT_PASTE             = 2;
     public final static int EDIT_COPY              = 3;
     public final static int EDIT_DELETE            = 4;
     public final static int EDIT_ADD               = 5;
    */

    /**
     * Element type value indicating a node.
     */
    public final static int NODE               = 1;
    /**
     * Element type value indicating an edge.
     */
    public final static int EDGE               = 2;
    /**
     * Element type value indicating a graph (or subgraph).
     */
    public final static int SUBGRAPH           = 4;
    /**
     * System attribute indicator.
     */
    final static int SYSTEM                    = 8;

    /**
     * Natural log of 10 (as a convenience).
     */
    final static double LOG10                  = Math.log(10);

    /**
     * Bit indicator that selection highlight is active.
     */
    public final static int SELECTION_MASK      = 1;
    /**
     * Bit indicator that deletion highlight is active.
     */
    public final static int DELETION_MASK       = 2;
    /**
     * Bit mask for highlight bits.
     */
    public final static int HIGHLIGHT_MASK      = SELECTION_MASK|DELETION_MASK;
    /**
     * Bit indicator that an element should be highlighted.
     */
    public final static int HIGHLIGHT_ON        = 4;
    /**
     * Bit indicator that an element should not be highlighted.
     */
    public final static int HIGHLIGHT_OFF       = 8;
    /**
     * Bit indicator that an element’s highlight should be toggled.
     */
    public final static int HIGHLIGHT_TOGGLE        = 16;

    /**
     * Maximum number of bits needed to represet the Element types.
     * Element type is merged with the element id number (a sequentially
     * assigned number) to ensure a unique identifier (within an invocation of
     * the package).
     *
     */
    public final static int TYPES_SHIFT            = 3;

    /**
     * Points per inch (72).
     */
    public final static double PointsPerInch = 72;

    /**
     * Default gap in pixels between peripheries.
     */
    public final static int PERIPHERY_GAP = 4;

    /**
     * Name prefix for name generation of unnamed subgraphs.
     */
    public final static String ANONYMOUS_PREFIX    = “_anonymous_”;

    /**
     * String name for bounding box attribute (bb).
     */
    public final static String BBOX_ATTR        = “bb”;
    /**
     * String name for cluster rank attribute (clusterrank).
     */
    public final static String CLUSTERRANK_ATTR       = “clusterrank”;
    /**
     * String name for color attribute (color).
     */
    public final static String COLOR_ATTR       = “color”;
    /**
     * String name for custom class used to draw custom shapes (custom).
     */
    public final static String CUSTOM_ATTR       = “custom”;
    /**
     * String name for direction attribute (dir).
     */
    public final static String DIR_ATTR  = “dir”;
    /**
     * String name for distortion attribute (distortion).
     */
    public final static String DISTORTION_ATTR  = “distortion”;
    /**
     * String name for fill color attribute (fillcolor).
     */
    public final static String FILLCOLOR_ATTR   = “fillcolor”;
    /**
     * String name for fontcolor attribute (fontcolor).
     */
    public final static String FONTCOLOR_ATTR   = “fontcolor”;
    /**
     * String name for fontname attribute (fontname).
     */
    public final static String FONTNAME_ATTR    = “fontname”;
    /**
     * String name for fontsize attribute (fontsize).
     */
    public final static String FONTSIZE_ATTR    = “fontsize”;
    /**
     * String name for fontstyle attribute (fontstyle).
     */
    public final static String FONTSTYLE_ATTR   = “fontstyle”;
    /**
     * String name for background color attribute (grappaBackgroundColor).
     */
    public final static String GRAPPA_BACKGROUND_COLOR_ATTR   = Grappa.PKG_LOWER+”BackgroundColor”;
    /**
     * String name for selection color attribute (grappaSelectionColor).
     */
    public final static String GRAPPA_SELECTION_STYLE_ATTR   = Grappa.PKG_LOWER+”SelectionColor”;
    /**
     * String name for deletion color attribute (grappaDeletionColor).
     */
    public final static String GRAPPA_DELETION_STYLE_ATTR   = Grappa.PKG_LOWER+”DeletionColor”;
    /**
     * String name for fontsize adjustment attribute (grappaFontsizeAdjustment).
     */
    public final static String GRAPPA_FONTSIZE_ADJUSTMENT_ATTR   = Grappa.PKG_LOWER+”FontsizeAdjustment”;
    /**
     * String name for height attribute (height).
     */
    public final static String HEIGHT_ATTR      = “height”;
    /**
     * String name for image attribute (image).
     */
    public final static String IMAGE_ATTR      = “image”;
    /**
     * String name for label attribute (label).
     */
    public final static String LABEL_ATTR       = “label”;
    /**
     * String name for label position attribute (lp).
     */
    public final static String LP_ATTR          = “lp”;
    /**
     * String name for head label attribute (headlabel).
     */
    public final static String HEADLABEL_ATTR       = “headlabel”;
    /**
     * String name for head label position attribute (head_lp).
     */
    public final static String HEADLP_ATTR          = “head_lp”;
    /**
     * String name for tail label attribute (taillabel).
     */
    public final static String TAILLABEL_ATTR       = “taillabel”;
    /**
     * String name for tail label position attribute (tail_lp).
     */
    public final static String TAILLP_ATTR          = “tail_lp”;
    /**
     * String name for label position attribute (margin).
     */
    public final static String MARGIN_ATTR      = “margin”;
    /**
     * String name for mincross limit attribute [unused] (mclimit).
     */
    public final static String MCLIMIT_ATTR      = “mclimit”;
    /**
     * String name for minimum subgraph bounding box attribute (minbox).
     */
    public final static String MINBOX_ATTR      = “minbox”;
    /**
     * String name for minimum rank distance between head and tail of edges attribute [unused] (minlen).
     */
    public final static String MINLEN_ATTR      = “minlen”;
    /**
     * String name for minimum subgraph size attribute (minsize).
     */
    public final static String MINSIZE_ATTR      = “minsize”;
    /**
     * String name for node separation attribute [unused] (nodesep).
     */
    public final static String NODESEP_ATTR     = “nodesep”;
    /**
     * String name for orientation angle attribute (orientation).
     */
    public final static String ORIENTATION_ATTR = “orientation”;
    /**
     * String name for patch work attribute (patch).
     */
    public final static String PATCH_ATTR         = “patch”;
    /**
     * String name for peripheries attribute (peripheries).
     */
    public final static String PERIPHERIES_ATTR = “peripheries”;
    /**
     * String name for position attribute (pos).
     */
    public final static String POS_ATTR         = “pos”;
    /**
     * String name for print list attribute (printlist).
     */
    public final static String PRINTLIST_ATTR      = “printlist”;
    /**
     * String name for rank direction attribute [unused] (rankdir).
     */
    public final static String RANKDIR_ATTR     = “rankdir”;
    /**
     * String name for rank separation attribute [unused] (ranksep)
     */
    public final static String RANKSEP_ATTR      = “ranksep”;
    /**
     * String name for rectangles attribute (rects).
     */
    public final static String RECTS_ATTR       = “rects”;
    /**
     * String name for rotation attribute (rotation).
     */
    public final static String ROTATION_ATTR       = “rotation”;
    /**
     * String name for shape attribute (shape).
     */
    public final static String SHAPE_ATTR       = “shape”;
    /**
     * String name for sides attribute (sides).
     */
    public final static String SIDES_ATTR       = “sides”;
    /**
     * String name for size attribute [unused] (size).
     */
    public final static String SIZE_ATTR        = “size”;
    /**
     * String name for skew attribute (skew).
     */
    public final static String SKEW_ATTR        = “skew”;
    /**
     * String name for style attribute (style).
     */
    public final static String STYLE_ATTR       = “style”;
    /**
     * String name for tag attribute (tag).
     */
    public final static String TAG_ATTR         = “tag”;
    /**
     * String name for tip attribute (tip).
     */
    public final static String TIP_ATTR         = “tip”;
    /**
     * String name for weight attribute [unused] (weight).
     */
    public final static String WEIGHT_ATTR      = “weight”;
    /**
     * String name for width attribute (width).
     */
    public final static String WIDTH_ATTR       = “width”;

    /**
     * Hash code for bounding box attribute (bb).
     */
    public final static int BBOX_HASH        = BBOX_ATTR.hashCode();
    /**
     * Hash code for color attribute (color).
     */
    public final static int COLOR_HASH       = COLOR_ATTR.hashCode();
    /**
     * Hash code for custom attribute (custom).
     */
    public final static int CUSTOM_HASH       = CUSTOM_ATTR.hashCode();
    /**
     * Hash code for edge direction attribute (dir).
     */
    public final static int DIR_HASH       = DIR_ATTR.hashCode();
    /**
     * Hash code for distortion attribute (distortion).
     */
    public final static int DISTORTION_HASH  = DISTORTION_ATTR.hashCode();
    /**
     * Hash code for fillcolor attribute (fillcolor).
     */
    public final static int FILLCOLOR_HASH   = FILLCOLOR_ATTR.hashCode();
    /**
     * Hash code for fontcolor attribute (fontcolor).
     */
    public final static int FONTCOLOR_HASH   = FONTCOLOR_ATTR.hashCode();
    /**
     * Hash code for fontname attribute (fontname).
     */
    public final static int FONTNAME_HASH    = FONTNAME_ATTR.hashCode();
    /**
     * Hash code for fontsize attribute (fontsize).
     */
    public final static int FONTSIZE_HASH    = FONTSIZE_ATTR.hashCode();
    /**
     * Hash code for fontstyle attribute (fontstyle).
     */
    public final static int FONTSTYLE_HASH   = FONTSTYLE_ATTR.hashCode();
    /**
     * Hash code for background color attribute (grappaBackgroundColor).
     */
    public final static int GRAPPA_BACKGROUND_COLOR_HASH   = GRAPPA_BACKGROUND_COLOR_ATTR.hashCode();
    /**
     * Hash code for selection color attribute (grappaSelectionColor).
     */
    public final static int GRAPPA_SELECTION_STYLE_HASH   = GRAPPA_SELECTION_STYLE_ATTR.hashCode();
    /**
     * Hash code for deletion color attribute (grappaDeletionColor).
     */
    public final static int GRAPPA_DELETION_STYLE_HASH   = GRAPPA_DELETION_STYLE_ATTR.hashCode();
    /**
     * Hash code for fontsize adjustment attribute (grappaFontsizeAdjustment).
     */
    public final static int GRAPPA_FONTSIZE_ADJUSTMENT_HASH   = GRAPPA_FONTSIZE_ADJUSTMENT_ATTR.hashCode();
    /**
     * Hash code for height attribute (height).
     */
    public final static int HEIGHT_HASH      = HEIGHT_ATTR.hashCode();
    /**
     * Hash code for image attribute (image).
     */
    public final static int IMAGE_HASH       = IMAGE_ATTR.hashCode();
    /**
     * Hash code for label attribute (label).
     */
    public final static int LABEL_HASH       = LABEL_ATTR.hashCode();
    /**
     * Hash code for label position attribute (lp).
     */
    public final static int LP_HASH          = LP_ATTR.hashCode();
    /**
     * Hash code for head label attribute (headlabel).
     */
    public final static int HEADLABEL_HASH       = HEADLABEL_ATTR.hashCode();
    /**
     * Hash code for head label position attribute (head_lp).
     */
    public final static int HEADLP_HASH          = HEADLP_ATTR.hashCode();
    /**
     * Hash code for tail label attribute (taillabel).
     */
    public final static int TAILLABEL_HASH       = TAILLABEL_ATTR.hashCode();
    /**
     * Hash code for tail label position attribute (tail_lp).
     */
    public final static int TAILLP_HASH          = TAILLP_ATTR.hashCode();
    /**
     * Hash code for margin attribute (margin).
     */
    public final static int MARGIN_HASH      = MARGIN_ATTR.hashCode();
    /**
     * Hash code for mincross limit attribute (mclimit).
     */
    public final static int MCLIMIT_HASH     = MCLIMIT_ATTR.hashCode();
    /**
     * Hash code for minimum subgraph bounding box attribute (minbox).
     */
    public final static int MINBOX_HASH      = MINBOX_ATTR.hashCode();
    /**
     * Hash code for minimum rank distance between head and tail of edges attribute (minlen).
     */
    public final static int MINLEN_HASH      = MINLEN_ATTR.hashCode();
    /**
     * Hash code for minimum subgraph size attribute (minsize).
     */
    public final static int MINSIZE_HASH      = MINSIZE_ATTR.hashCode();
    /**
     * Hash code for node separation attribute (nodesep).
     */
    public final static int NODESEP_HASH     = NODESEP_ATTR.hashCode();
    /**
     * Hash code for orientation attribute (orientation).
     */
    public final static int ORIENTATION_HASH = ORIENTATION_ATTR.hashCode();
    /**
     * Hash code for patch work attribute (patch).
     */
    public final static int PATCH_HASH = PATCH_ATTR.hashCode();
    /**
     * Hash code for peripheries attribute (peripheries).
     */
    public final static int PERIPHERIES_HASH = PERIPHERIES_ATTR.hashCode();
    /**
     * Hash code for position attribute (pos).
     */
    public final static int POS_HASH         = POS_ATTR.hashCode();
    /**
     * Hash code for rank direction attribute (rankdir).
     */
    /**
     * Hash code for print list attribute (printlist).
     */
    public final static int PRINTLIST_HASH      = PRINTLIST_ATTR.hashCode();
    public final static int RANKDIR_HASH     = RANKDIR_ATTR.hashCode();
    /**
     * Hash code for rank separation attribute (ranksep).
     */
    public final static int RANKSEP_HASH     = RANKSEP_ATTR.hashCode();
    /**
     * Hash code for rectangles attribute (rects).
     */
    public final static int RECTS_HASH       = RECTS_ATTR.hashCode();
    /**
     * Hash code for rotation attribute (rotation).
     */
    public final static int ROTATION_HASH       = ROTATION_ATTR.hashCode();
    /**
     * Hash code for shape attribute (shape).
     */
    public final static int SHAPE_HASH       = SHAPE_ATTR.hashCode();
    /**
     * Hash code for sides attribute (sides).
     */
    public final static int SIDES_HASH       = SIDES_ATTR.hashCode();
    /**
     * Hash code for size attribute (size).
     */
    public final static int SIZE_HASH        = SIZE_ATTR.hashCode();
    /**
     * Hash code for skew attribute (skew).
     */
    public final static int SKEW_HASH        = SKEW_ATTR.hashCode();
    /**
     * Hash code for style attribute (style).
     */
    public final static int STYLE_HASH       = STYLE_ATTR.hashCode();
    /**
     *  Hash code for tag attribute (tag).
     */
    public final static int TAG_HASH         = TAG_ATTR.hashCode();
    /**
     *  Hash code for tip attribute (tip).
     */
    public final static int TIP_HASH         = TIP_ATTR.hashCode();
    /**
     * Hash code for weight attribute (weight).
     */
    public final static int WEIGHT_HASH      = WEIGHT_ATTR.hashCode();
    /**
     * Hash code for width attribute (width).
     */
    public final static int WIDTH_HASH       = WIDTH_ATTR.hashCode();

    //
    // Attribute types
    //

    /**
     * Indicator that no attribute value type is specified.
     * When no attribute type is specified, an error results.
     */
    public final static int  _NO_TYPE           = 0x00;
    /**
     * Indicator that attribute value is an instance of GrappaBox.
     */
    public final static int  BOX_TYPE           = 0x01;
    /**
     * Indicator that attribute value is an instance of java.awt.Color.
     */
    public final static int  COLOR_TYPE     = 0x02;
    /**
     * Indicator that attribute value is an instance of java.lang.Integer representing an edge direction.
     */
    public final static int DIR_TYPE        = 0x03;
    /**
     * Indicator that attribute value is an instance of java.lang.Double.
     */
    public final static int DOUBLE_TYPE     = 0x04;
    /**
     * Indicator that attribute value is a java.lang.Integer representing a font style.
     */
    public final static int FONTSTYLE_TYPE      = 0x05;
    /**
     * Indicator that attribute value is a java.lang.Hashtable whose keys provide a list of values
     */
    public final static int HASHLIST_TYPE       = 0x06;
    /**
     * Indicator that attribute value is an instance of java.lang.Integer.
     */
    public final static int INTEGER_TYPE        = 0x07;
    /**
     * Indicator that attribute value is an instance of GrappaLine.
     */
    public final static int  LINE_TYPE      = 0x08;
    /**
     * Indicator that attribute value is an instance of GrappaPoint.
     */
    public final static int  POINT_TYPE     = 0x09;
    /**
     * Indicator that attribute value is a java.lang.Integer representing a Grappa shape.
     */
    public final static int SHAPE_TYPE      = 0x0A;
    /**
     * Indicator that attribute value is an instance of GrappaSize.
     */
    public final static int SIZE_TYPE           = 0x0B;
    /**
     * Indicator that attribute value is an instance of java.lang.String.
     */
    public final static int STRING_TYPE     = 0x0C;
    /**
     * Indicator that attribute value is an instance of GrappaStyle.
     */
    public final static int STYLE_TYPE      = 0x0D;

    /*
     * The indicators used to define the underlying Shape of this object.
     */

    /**
     * Indicator that a valid shape was not specified for a graph element.
     */
    public final static int NO_SHAPE            = 0;
    /**
     * Indicator that the element has a line shape.
     */
    public final static int LINE_SHAPE          = 1;
    /**
     * Indicator that the element has a box shape.
     */
    public final static int BOX_SHAPE           = 2;
    /**
     * Indicator that the element has a diamond shape.
     */
    public final static int DIAMOND_SHAPE       = 3;
    /**
     * Indicator that the element has a double circle shape.
     */
    public final static int DOUBLECIRCLE_SHAPE      = 4;
    /**
     * Indicator that the element has a double octagon shape.
     */
    public final static int DOUBLEOCTAGON_SHAPE     = 5;
    /**
     * Indicator that the element has a egg shape.
     */
    public final static int EGG_SHAPE           = 6;
    /**
     * Indicator that the element has a hexagon shape.
     */
    public final static int HEXAGON_SHAPE       = 7;
    /**
     * Indicator that the element has a house shape.
     */
    public final static int HOUSE_SHAPE         = 8;
    /**
     * Indicator that the element has a upside-down house shape.
     */
    public final static int INVERTEDHOUSE_SHAPE     = 9;
    /**
     * Indicator that the element has a upside-down trapezium shape.
     */
    public final static int INVERTEDTRAPEZIUM_SHAPE = 10;
    /**
     * Indicator that the element has a upside-down triangle shape.
     */
    public final static int INVERTEDTRIANGLE_SHAPE  = 11;
    /**
     * Indicator that the element has a octagon shape.
     */
    public final static int OCTAGON_SHAPE       = 12;
    /**
     * Indicator that the element has a oval shape.
     */
    public final static int OVAL_SHAPE          = 13;
    /**
     * Indicator that the element has a parallelogram shape.
     */
    public final static int PARALLELOGRAM_SHAPE     = 14;
    /**
     * Indicator that the element has a pentagon shape.
     */
    public final static int PENTAGON_SHAPE      = 15;
    /**
     * Indicator that the element has no shape, but rather is just a text label.
     */
    public final static int PLAINTEXT_SHAPE     = 16;
    /**
     * Indicator that the element has a general polygonal shape.
     */
    public final static int POINT_SHAPE             = 17;
    /**
     * Indicator that the element has a general polygonal shape.
     */
    public final static int POLYGON_SHAPE       = 18;
    /**
     * Indicator that the element has a record shape.
     * A record shape is of a box shape that contains one or more labelled
     * sub-partitions within it.
     */
    public final static int RECORD_SHAPE        = 19;
    /**
     * Indicator that the element has a box shape with rounded corners.
     */
    public final static int ROUNDEDBOX_SHAPE        = 20;
    /**
     * Indicator that the element has a trapezium shape.
     */
    public final static int TRAPEZIUM_SHAPE     = 21;
    /**
     * Indicator that the element has a triangle shape.
     */
    public final static int TRIANGLE_SHAPE      = 22;
    /**
     * Indicator that the element has a triple octagon shape.
     */
    public final static int TRIPLEOCTAGON_SHAPE     = 23;
    /**
     * Indicator that the element has a circular shape with parallel chords top and bottom.
     */
    public final static int MCIRCLE_SHAPE       = 24;
    /**
     * Indicator that the element has a diamond shape with triangles inset in each corner.
     */
    public final static int MDIAMOND_SHAPE      = 25;
    /**
     * Indicator that the element has a record shape with triangles inset in each of its four outer corners.
     */
    public final static int MRECORD_SHAPE       = 26;
    /**
     * Indicator that the element has a square shape with triangles inset in each of its corners.
     */
    public final static int MSQUARE_SHAPE       = 27;
    /**
     * Indicator that the element shape is determined by a user-supplied class defined by the “custom” attribute
     */
    public final static int CUSTOM_SHAPE        = 28;

    // or’ed with others shape types
    /**
     * Bit mask for extracting shape information.
     */
    public final static int SHAPE_MASK          = 1023;
    /**
     * Bit flag indicating that the shape path needs to be generated by
     * by Grappa rather than relying on Java built-ins.
     */
    public final static int GRAPPA_SHAPE        = 1024;
}

att/grappa/GrappaLine.java
att/grappa/GrappaLine.java/*
 *  This software may only be used by you under license from AT&T Corp.
 *  (“AT&T”).  A copy of AT&T’s Source Code Agreement is available at
 *  AT&T’s Internet website having the URL:
 *  
 *  If you received this software without first entering into a license
 *  with AT&T, you have an infringing copy of this software and cannot use
 *  it without violating AT&T’s intellectual property rights.
 */

package att.grappa;

import java.awt.*;
import java.awt.geom.*;

/**
 * This class provides line and bezier-curve support for Grappa.
 *
 * @version 1.2, 26 May 2005; Copyright 1996 – 2005 by AT&T Corp.
 * @author  John Mocenigo, Research @ AT&T Labs
 */
public class GrappaLine
    implements
    GrappaConstants,
    Cloneable, Shape
{
    /**
     * Arrow head length
     */
    public final static double arrowLength     = 10;

    /**
     * Arrow head width
     */
    public final static double arrowWidth      = 5;

    /**
     * Bit flag to indicate that line has no arrow heads.
     */
    public static final int NONE_ARROW_EDGE     = 0;
    /**
     * Bit flag to indicate that line has an arrow head at its head end.
     */
    public static final int HEAD_ARROW_EDGE     = 1;
    /**
     * Bit flag to indicate that line has an arrow head at its tail end.
     */
    public static final int TAIL_ARROW_EDGE     = 2;
    /**
     * Bit flag to indicate that line has arrow heads at both ends.
     * Note that
     * NONE_ARROW_EDGE + HEAD_ARROW_EDGE + TAIL_ARROW_EDGE = BOTH_ARROW_EDGE
     */
    public static final int BOTH_ARROW_EDGE     = 3;

    // the general path describing this line (including arrow heads)
    private GeneralPath path = null;
    // fatter path for contains and intersects tests
    private GeneralPath testpath = null;
    // arrow head info
    private int arrow = NONE_ARROW_EDGE;
    // the point set for this line (not including arrow heads)
    private GrappaPoint[] gpts = null;

    // fix winding rule at instantiation time
    private int windingRule = Grappa.windingRule;

    ////////////////////////////////////////////////////////////////////////
    //
    // Constructors
    //
    ////////////////////////////////////////////////////////////////////////

    /**
     * Constructs a new GrappaLine object from an array of 
     * (cubic) curve points.
     * The winding rule for this path is defaulted (from Grappa.windingRule).
     * @param pts the GrappaPoint array used to describe the curve
     * @param type indicates arrow type (NONE_ARROW_EDGE,HEAD_ARROW_EDGE,
     *             TAIL_ARROW_EDGE,BOTH_ARROW_EDGE)
     */
    public GrappaLine(GrappaPoint[] pts, int type) {
    updateLine(pts,type);
    }

    /**
     * Constructs a new GrappaLine object from a string of 
     * (cubic) curve points as used by “dot”.
     * All of the initial geometry and the winding rule for this path are
     * defaulted.
     * @param curve the String that specifies the point list; the
     *              format is: [s,x0,y0|e,xN,yN] [x1,y2] … [xN-1,yN-1]
     */
    public GrappaLine(String curve) {
    updateLine(curve);
    }

    ////////////////////////////////////////////////////////////////////////
    //
    // Public methods
    //
    ////////////////////////////////////////////////////////////////////////

    /**
     * Check for equality of this object with the supplied object.
     *
     * @param the object to be checked for equality
     * @return true, when equal
     */
    public boolean equals(Object obj) {
    if(obj == null || !(obj instanceof GrappaLine)) return(false);
    GrappaLine cmp = (GrappaLine)obj;
    if(cmp == this) return(true);
    if(cmp.getArrowType() != arrow) return(false);
    if(cmp.gpts.length != gpts.length || !gpts.equals(cmp.gpts)) return(false);
    // should be sufficient, should be no need to compare path
    //if(!path.equals(cmp.path)) return(false);
    return(true);
    }

    /**
     * Return the arrow type for this line.
     * @return one of NONE_ARROW_EDGE,HEAD_ARROW_EDGE, TAIL_ARROW_EDGE, or BOTH_ARROW_EDGE
     */
    public int getArrowType() {
    return arrow;
    }

    /**
     * Return the winding rule for this line.
     * @return one of WIND_NON_ZERO or WIND_EVEN_ODD
     */
    public int getWindingRule() {
    return windingRule;
    }

    /**
     * Check is the line is oriented away from the given point. 
     *
     * @return true if the line is oriented so that its starting point is
     *  nearer to the supplied point than its ending point.
     */
    public boolean startsNear(Point2D pt) {
    return(gpts[0].distance(pt) < gpts[gpts.length-1].distance(pt));     }     /**      * Provides a string representation of this object consistent       * with Grappa attributes.      *      * @return attribute-suitable string representation of this GrappaLine.      */     public String toAttributeString() {     return(toFormattedString("%p"));     }     /**      * Provides a formatted string representation of this object.      *       * @param pointFormat the specific format directive to use for each point in the line (%p is the base directive).
     * @return a string representation of this GrappaLine. 
     */
    public String toFormattedString(String pointFormat) {
    int ps = 0;
    int pe = gpts.length – 1;
    boolean spacer = false;
    StringBuffer buf = new StringBuffer();
    if((arrow&HEAD_ARROW_EDGE) != 0) {
        buf.append(“s,”);
        buf.append(gpts[ps++].toFormattedString(pointFormat));
        spacer = true;
    }
    if((arrow&TAIL_ARROW_EDGE) != 0) {
        if(spacer) {
        buf.append(” e,”);
        } else {
        buf.append(“e,”);
        spacer = true;
        }
        buf.append(gpts[pe–].toFormattedString(pointFormat));
    }
    while(ps <= pe) {         if(spacer) {         buf.append(" ");         } else {         spacer = true;         }         buf.append(gpts[ps++].toFormattedString(pointFormat));     }     return(buf.toString());     }     /**      * Provides a generic string representation of this object.      *       * @return a generic string representation of this GrappaLine.       */     public String toString() {     int ps = 0;     int pe = gpts.length - 1;     boolean spacer = false;     StringBuffer buf = new StringBuffer();     if((arrow&HEAD_ARROW_EDGE) != 0) {         buf.append("s,");         buf.append(gpts[ps].x);         buf.append(",");         buf.append(gpts[ps++].y);         spacer = true;     }     if((arrow&TAIL_ARROW_EDGE) != 0) {         if(spacer) {         buf.append(" e,");         } else {         buf.append("e,");         spacer = true;         }         buf.append(gpts[pe].x);         buf.append(",");         buf.append(gpts[pe--].y);     }     while(ps <= pe) {         if(spacer) {         buf.append(" ");         } else {         spacer = true;         }         buf.append(gpts[ps].x);         buf.append(",");         buf.append(gpts[ps++].y);     }     return(buf.toString());     }     /**      * Changes the arrow type for this line.      *      * @param new_type indicates arrow type (NONE_ARROW_EDGE,HEAD_ARROW_EDGE,      *                 TAIL_ARROW_EDGE,BOTH_ARROW_EDGE)      *       * @return true if the type changed, false otherwise.      */     public boolean changeArrowType(int new_type) {     boolean changed = false;     if(arrow != new_type && (new_type&(~(BOTH_ARROW_EDGE))) == 0) {         changed = true;         updateLine(gpts, new_type);     }     return(changed);     }     ////////////////////////////////////////////////////////////////////////     //     // Private methods     //     ////////////////////////////////////////////////////////////////////////     // add an arrow to the path of this line     private void addArrow(GeneralPath path, GeneralPath testpath, GrappaPoint tip, GrappaPoint shaft, double length, double width) {     double theta = Math.atan2((tip.y - shaft.y), (tip.x - shaft.x));     double half_width = width / 2.0;     float x, y;     path.lineTo(             x = (float) (tip.x - (length * Math.cos(theta) - half_width * Math.sin(theta))),             y = (float) (tip.y - (length * Math.sin(theta) + half_width * Math.cos(theta)))             );     testpath.lineTo(x,y);     path.lineTo(             x = (float) (tip.x - (length * Math.cos(theta) + half_width * Math.sin(theta))),             y = (float) (tip.y - (length * Math.sin(theta) - half_width * Math.cos(theta)))             );     testpath.lineTo(x,y);     path.lineTo(             x = (float) tip.x,             y = (float) tip.y             );     testpath.lineTo(x,y);     }      // translate the supplied string into the points of this line     private void updateLine(String curve) {     int type = NONE_ARROW_EDGE;     int i, j, k;     int len = curve.length();     int pts = 1;     boolean wasSpace = true;     GrappaPoint[] grpts = null;     Integer attr_type;     // first pass is mostly sizing and basic validity check     for(i = 0, j = len; i < len; i++) {         switch((int)curve.charAt(i)) {         case 's':         wasSpace = false;         type += HEAD_ARROW_EDGE;         break;         case 'e':         wasSpace = false;         type += TAIL_ARROW_EDGE;         break;         case ' ':         if(!wasSpace) {             if(j == len) j = i; // first space (used later)             pts++;             wasSpace = true;         }         break;         default:         wasSpace = false;         break;         }     }     if(wasSpace) pts--;     if(pts < 2 || type > BOTH_ARROW_EDGE) {
        throw new IllegalArgumentException(“bad curve specifier string (” + curve + “)”);
    }

    grpts = new GrappaPoint[pts];

    for(i = 0; i < len; i++) {         if(curve.charAt(i) != ' ') {         break;         }     }     pts = 0;     if(curve.charAt(i) == 's') {         grpts[pts++] = new GrappaPoint(curve.substring(i+2,j));         for(i = ++j; i < len; i++) {         if(curve.charAt(i) != ' ') {             break;         }         }         for(k = j, j = len; k < j; k++) {         if(curve.charAt(k) == ' ') {             j = k;             break;         }         }     }     if(curve.charAt(i) == 'e') {         grpts[grpts.length-1] = new GrappaPoint(curve.substring(i+2,j));         for(i = ++j; i < len; i++) {         if(curve.charAt(i) != ' ') {             break;         }         }         for(k = j, j = len; k < j; k++) {         if(curve.charAt(k) == ' ') {             j = k;             break;         }         }     }     if(curve.charAt(i) == 's') {         grpts[pts++] = new GrappaPoint(curve.substring(i+2,j));         for(i = ++j; i < len; i++) {         if(curve.charAt(i) != ' ') {             break;         }         }         for(k = j, j = len; k < j; k++) {         if(curve.charAt(k) == ' ') {             j = k;             break;         }         }     }     while(i < len) {         grpts[pts++] = new GrappaPoint(curve.substring(i,j));         for(i = ++j; i < len; i++) {         if(curve.charAt(i) != ' ') {             break;         }         }         for(k = j, j = len; k < j; k++) {         if(curve.charAt(k) == ' ') {             j = k;             break;         }         }     }     updateLine(grpts, type);     }     // given points and an arrow type, generate the path of this line     private void updateLine(GrappaPoint[] grpts, int type) {     int pts = 0;     int xpts = 0;     float x, y, x2, y2, x3, y3, z = -2;     if((type&HEAD_ARROW_EDGE) != 0) xpts += 3;     if((type&TAIL_ARROW_EDGE) != 0) xpts += 3;     GeneralPath grpath = new GeneralPath(windingRule, grpts.length+xpts+grpts.length-1);     GeneralPath grtestpath = new GeneralPath(windingRule, grpts.length+xpts+grpts.length-1);     if(grpts.length < 2) {         throw new IllegalArgumentException("need at least two supplied points");     }     grpath.moveTo(x = (float)grpts[pts].x, y = (float)grpts[pts++].y);     grtestpath.moveTo(x+z,y+z);     if((type&HEAD_ARROW_EDGE) != 0) {         grtestpath.moveTo(x+z,y+z);         addArrow(grpath, grtestpath, grpts[pts-1], grpts[pts], arrowLength, arrowWidth);         grpath.lineTo(x = (float)grpts[pts].x, y = (float)grpts[pts++].y);         grtestpath.lineTo(x-z, y-z);     } else grtestpath.lineTo(x-z,y-z);     boolean lastWasLine = false;     while(pts < grpts.length) {         lastWasLine = false;         if(pts+3 <= grpts.length) {         grpath.curveTo(                    x = (float)grpts[pts].x, y = (float)grpts[pts++].y,                    x2 = (float)grpts[pts].x, y2 = (float)grpts[pts++].y,                    x3 = (float)grpts[pts].x, y3 = (float)grpts[pts++].y                    );         grtestpath.curveTo(x-z,y-z,x2-z,y2-z,x3-z,y3-z);         } else {         lastWasLine = true;         grpath.lineTo(x = (float)grpts[pts].x, y = (float)grpts[pts++].y);         grtestpath.lineTo(x-z,y-z);         }     }     if((type&TAIL_ARROW_EDGE) != 0) {         addArrow(grpath, grtestpath,  grpts[pts-1], grpts[pts-2], arrowLength, arrowWidth);     }     pts--;     while(pts > 0) {
        if(!lastWasLine && pts-3 >= 0) {
        grpath.curveTo(
                   x = (float)grpts[–pts].x, y = (float)grpts[pts].y,
                   x2 = (float)grpts[–pts].x, y2 = (float)grpts[pts].y,
                   x3 = (float)grpts[–pts].x, y3 = (float)grpts[pts].y
                   );
        grtestpath.curveTo(x+z,y+z,x2+z,y2+z,x3+z,y3+z);
        } else {
        lastWasLine = false;
        grpath.lineTo(x = (float)grpts[–pts].x, y = (float)grpts[pts].y);
        grtestpath.lineTo(x+z,y+z);
        }
    }

    this.gpts = grpts;
    this.path = grpath;
    this.testpath = grtestpath;
    this.arrow = type;
    }

    ////////////////////////////////////////////////////////////////////////
    //
    // Cloneable interface
    //
    ////////////////////////////////////////////////////////////////////////
 
    /**
     * Creates a new object of the same class as this object.
     *
     * @return     a clone of this instance.
     * @exception  OutOfMemoryError            if there is not enough memory.
     * @see        java.lang.Cloneable
     */
    public Object clone() {
    try {
        GrappaLine copy = (GrappaLine) super.clone();
        copy.path = (GeneralPath) path.clone();
        if(gpts != null) {
        copy.gpts = (GrappaPoint[])(gpts.clone());
        }
        return copy;
    } catch (CloneNotSupportedException e) {
        // this shouldn’t happen, since we are Cloneable
        throw new InternalError();
    }
    }

    ////////////////////////////////////////////////////////////////////////
    //
    // Shape interface
    //
    ////////////////////////////////////////////////////////////////////////

    public final boolean contains(double x, double y) {
    return(testpath.contains(x, y));
    }

    public final boolean contains(double x, double y, double width, double height) {
    return(testpath.contains(x, y, width, height));
    }

    public final boolean contains(Point2D p) {
    return(testpath.contains(p));
    }

    public final boolean contains(Rectangle2D r) {
    return(testpath.contains(r));
    }

    public final Rectangle getBounds() {
    return(path.getBounds2D().getBounds());
    }

    public final Rectangle2D getBounds2D() {
    return(path.getBounds2D());
    }

    /**
     * Equivalent to getPathIterator(null).
     *
     * @see getPathIterator(AffineTransform)
     */
    public final PathIterator getPathIterator() {
    return path.getPathIterator(null);
    }

    public final PathIterator getPathIterator(AffineTransform at) {
    return path.getPathIterator(at);
    }

    public final PathIterator getPathIterator(AffineTransform at, double flatness) {
    return new FlatteningPathIterator(path.getPathIterator(at), flatness);
    }

    public final boolean intersects(double x, double y, double width, double height) {
    return(testpath.intersects(x, y, width, height));
    }

    public final boolean intersects(Rectangle2D r) {
    return(testpath.intersects(r));
    }
}

att/grappa/GrappaListener.java
att/grappa/GrappaListener.java/*
 *  This software may only be used by you under license from AT&T Corp.
 *  (“AT&T”).  A copy of AT&T’s Source Code Agreement is available at
 *  AT&T’s Internet website having the URL:
 *  
 *  If you received this software without first entering into a license
 *  with AT&T, you have an infringing copy of this software and cannot use
 *  it without violating AT&T’s intellectual property rights.
 */

package att.grappa;

/**
 * An interface for handling mouse-related activity that occurs on a graph.
 *
 * @version 1.2, 26 May 2005; Copyright 1996 – 2005 by AT&T Corp.
 * @author  John Mocenigo, Research @ AT&T Labs
 */
public interface GrappaListener
{
  /**
   * The method called when a single mouse click occurs on a displayed subgraph.
   *
   * @param subg displayed subgraph where action occurred
   * @param elem subgraph element in which action occurred
   * @param pt the point where the action occurred (graph coordinates)
   * @param modifiers mouse modifiers in effect
   * @param panel specific panel where the action occurred
   */
    public void grappaClicked(Subgraph subg, Element elem, GrappaPoint pt, int modifiers, int clickCount, GrappaPanel panel);

  /**
   * The method called when a mouse press occurs on a displayed subgraph.
   *
   * @param subg displayed subgraph where action occurred
   * @param elem subgraph element in which action occurred
   * @param pt the point where the action occurred (graph coordinates)
   * @param modifiers mouse modifiers in effect
   * @param panel specific panel where the action occurred
   */
    public void grappaPressed(Subgraph subg, Element elem, GrappaPoint pt, int modifiers, GrappaPanel panel);

  /**
   * The method called when a mouse release occurs on a displayed subgraph.
   *
   * @param subg displayed subgraph where action occurred
   * @param elem subgraph element in which action occurred
   * @param pt the point where the action occurred (graph coordinates)
   * @param modifiers mouse modifiers in effect
   * @param pressedElem subgraph element in which the most recent mouse press occurred
   * @param pressedPt the point where the most recent mouse press occurred (graph coordinates)
   * @param pressedModifiers mouse modifiers in effect when the most recent mouse press occurred
   * @param outline enclosing box specification from the previous drag position (for XOR reset purposes)
   * @param panel specific panel where the action occurred
   */
    public void grappaReleased(Subgraph subg, Element elem, GrappaPoint pt, int modifiers, Element pressedElem, GrappaPoint pressedPt, int pressedModifiers, GrappaBox outline, GrappaPanel panel);

  /**
   * The method called when a mouse drag occurs on a displayed subgraph.
   *
   * @param subg displayed subgraph where action occurred
   * @param currentPt the current drag point
   * @param currentModifiers the current drag mouse modifiers
   * @param pressedElem subgraph element in which the most recent mouse press occurred
   * @param pressedPt the point where the most recent mouse press occurred (graph coordinates)
   * @param pressedModifiers mouse modifiers in effect when the most recent mouse press occurred
   * @param outline enclosing box specification from the previous drag position (for XOR reset purposes)
   * @param panel specific panel where the action occurred
   */
    public void grappaDragged(Subgraph subg, GrappaPoint currentPt, int currentModifiers, Element pressedElem, GrappaPoint pressedPt, int pressedModifiers, GrappaBox outline, GrappaPanel panel);

  /**
   * The method called when a element tooltip is needed.
   *
   * @param subg displayed subgraph where action occurred
   * @param elem subgraph element in which action occurred
   * @param pt the point where the action occurred (graph coordinates)
   * @param modifiers mouse modifiers in effect
   * @param panel specific panel where the action occurred
   *
   * @return the tip to be displayed or null
   */
    public String grappaTip(Subgraph subg, Element elem, GrappaPoint pt, int modifiers, GrappaPanel panel);
}

att/grappa/GrappaNexus.java
att/grappa/GrappaNexus.java/*
 *  This software may only be used by you under license from AT&T Corp.
 *  (“AT&T”).  A copy of AT&T’s Source Code Agreement is available at
 *  AT&T’s Internet website having the URL:
 *  
 *  If you received this software without first entering into a license
 *  with AT&T, you have an infringing copy of this software and cannot use
 *  it without violating AT&T’s intellectual property rights.
 */

package att.grappa;

import java.lang.reflect.*;
import java.awt.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.net.*;
import java.util.Observer;
import java.util.Hashtable;

/**
 * This class brings together shape, text and attribute information
 * related to bounding and drawing an element.
 *
 * @version 1.2, 26 May 2005; Copyright 1996 – 2005 by AT&T Corp.
 * @author  John Mocenigo, Research @ AT&T Labs
 */
public class GrappaNexus
    implements
    GrappaConstants,
    Cloneable, ImageObserver, Observer, Shape
{
    /**
     * RoundRectangle arc height factor
     */
    public static double arcHeightFactor = 0.05;

    /**
     * RoundRectangle arc width factor
     */
    public static double arcWidthFactor = 0.05;

    Area textArea = null;
    Shape shape = null;
    int shapeType = NO_SHAPE;
    Rectangle2D bbox = null;
    GrappaStyle style = null;
    Color fillcolor = null;
    Color color = null;
    Image image = null;
    boolean imageLoading = false;

    boolean dirty = false; // just for cluster subgraphs, now

    Stroke stroke = null;

    // used for RECORD_SHAPE/MRECORD_SHAPE only, so far
    private Object[] objs = null;

    // used when SHAPE_ATTR is CUSTOM_SHAPE
    private Object custom_shape = null;

    /**
     * Indicates if element text should be included in the element
     * bounding box. By default, it is.
     *
     * @see Grappa#shapeBoundText
     */
    public boolean boundText = true;
    /**
     * Indicates if the area bounding the element text should be
     * filled/outlined along with the element when being drawn.
     * By default, it is not.
     *
     * @see Grappa#shapeClearText
     */
    public boolean clearText = false;
    /**
     * Indicates if element text should be drawn when drawing the
     * element. By default, it is.
     *
     * @see Grappa#shapeDrawText
     */
    public boolean drawText  = true;

    Element element = null;

    long lastUpdate = 0;
    private long lastShapeUpdate = 0;
    private long lastTextUpdate = 0;
    private long lastStyleUpdate = 0;
    private long lastDecorationUpdate = 0;
    private long lastImageUpdate = 0;

    Font font = null;
    String[] lstr = null;
    GrappaPoint[] lpos = null;
    Color font_color = null;

    // fix winding rule at instantiation time
    private int windingRule = Grappa.windingRule;

    ////////////////////////////////////////////////////////////////////////
    //
    // Constructors
    //
    ////////////////////////////////////////////////////////////////////////

    /**
     * Constructs a new GrappaNexus object from an element.
     * @param elem the Element needing a GrappaNexus object.
     */
    public GrappaNexus(Element elem) {
    this.element = elem;
    rebuild();
    }

    ////////////////////////////////////////////////////////////////////////
    //
    // Public methods
    //
    ////////////////////////////////////////////////////////////////////////

    /**
     * Get the underlying element.
     *
     * @return the element underlying this GrappaNexus.
     */
    public Element getElement() {
    return element;
    }

    /**
     * Return the image, if any, loaded for this element
     * @return an image or null
     */
    public Image getImage() {
    return image;
    }

    /**
     * Return status of image loading.
     * Returns true whenever an image has begun loading for this
     * element, but has not yet completed.
     *
     * @return true, during image loading; false, otherwise
     */
    public boolean isImageLoading() {
    return imageLoading;
    }

    /**
     * Return the winding rule for this line.
     * @return one of WIND_NON_ZERO or WIND_EVEN_ODD
     */
    public int getWindingRule() {
    return windingRule;
    }

    /**
     * Recompute the components of this GrappaNexus.
     *
     * @see updateStyle
     * @see updateDecoration
     * @see updateShape
     * @see updateText
     * @see updateImage
     */
    public void rebuild() {
    updateStyle();
    updateDecoration();
    updateShape();
    updateText();
    updateImage();
    }

    /**
     * Update the shape information for the underlying element.
     * For nodes, the distortionheightorientationperipheriesposrotationshapesidesskew and width attributes are examined.
     * For edges, the pos attribute is examined.
     * For subgraph, the bounding box is recomputed.
     */
    public void updateShape() {
    long thisShapeUpdate = System.currentTimeMillis();
    switch(element.getType()) {
    case NODE:
        // re-initialize some values
        custom_shape = null;
        objs = null;

        if(element.getSubgraph().isCluster() && element.getSubgraph().grappaNexus != null)
        element.getSubgraph().grappaNexus.dirty = true;

        Node node = (Node)element;
        GrappaPoint pos = (GrappaPoint)node.getAttributeValue(POS_ATTR);
        Double Width = (Double)node.getAttributeValue(WIDTH_ATTR);
        Double Height = (Double)node.getAttributeValue(HEIGHT_ATTR);
        Integer Type = (Integer)node.getAttributeValue(SHAPE_ATTR);

        double width = PointsPerInch * Width.doubleValue();
        double height = PointsPerInch * Height.doubleValue();
        int type = Type.intValue();

        // the above attributes are sure to be there since they are defaulted,
        // but these could return null values, so be sure to account for that
        Integer Peripheries = (Integer)node.getAttributeValue(PERIPHERIES_ATTR);
        Integer Sides = (Integer)node.getAttributeValue(SIDES_ATTR);
        Double Distortion = (Double)node.getAttributeValue(DISTORTION_ATTR);
        Double Skew = (Double)node.getAttributeValue(SKEW_ATTR);
        Double Orientation = (Double)node.getAttributeValue(ORIENTATION_ATTR);
        Double Rotation = (Double)node.getAttributeValue(ROTATION_ATTR);

        int peripheries = Peripheries == null ? -1 : Peripheries.intValue();
        int sides = Sides == null ? -1 : Sides.intValue();
        double distortion = Distortion == null ? 0 : Distortion.doubleValue();
        double skew = Skew == null ? 0 : Skew.doubleValue();
        double orientation = Orientation == null ? 0 : Orientation.doubleValue();
        double rotation = Rotation == null ? 0 : Rotation.doubleValue();

        if(Orientation != null && orientation != 0 && Grappa.orientationInDegrees) {
        orientation = Math.PI * orientation / 180.0;
        }

        GeneralPath path;

        switch(type) {
        case CUSTOM_SHAPE:
        String custom = (String)node.getAttributeValue(CUSTOM_ATTR);
        if(custom == null) {
            throw new IllegalArgumentException(“custom attibuted null for node (” + node.getName() + “) with custom shape”);
        }
        Class custom_class;
        try {
            custom_class = Class.forName(custom);
        }
        catch(Exception e) {
            throw new IllegalArgumentException(“custom class unavailable for custom shape ‘” + custom + “‘”);
        }
        if(!(GrappaShape.class.isAssignableFrom(custom_class))) {
            throw new IllegalArgumentException(“custom class ‘” + custom + “‘ does not extend the GrappaShape class”);
        }
        Constructor ccustom;
        try {
            ccustom= custom_class.getConstructor(new Class[] { Element.class, double.class, double.class, double.class, double.class });
        }
        catch(Exception e) {
            throw new IllegalArgumentException(“constructor for custom class shape ‘” + custom + “‘ not found”);
        }
        try {
            if(Grappa.centerPointNodes) {
            shape = (Shape)(custom_shape = ccustom.newInstance(new Object[] { node,  new Double(pos.x – (width/2.0)), new Double(pos.y – (height/2.0)), new Double(width), new Double(height) }));
            } else {
            shape = (Shape)(custom_shape = ccustom.newInstance(new Object[] { node, new Double(pos.x), new Double(pos.y), new Double(width), new Double(height) }));
            }
        }
        catch(Exception e) {
            if(e instanceof InvocationTargetException) {
            Throwable t = ((InvocationTargetException)e).getTargetException();
            Grappa.displayException((Exception)t);
            } else if(e instanceof UndeclaredThrowableException) {
            throw new IllegalArgumentException(“cannot instantiate custom shape ‘” + custom + “‘ for node ‘” + node.getName() + “‘ because2: ” + ((UndeclaredThrowableException)e).getUndeclaredThrowable().getMessage());
            } else {
            throw new IllegalArgumentException(“cannot instantiate custom shape ‘” + custom + “‘ for node ‘” + node.getName() + “‘ because3: ” + e.getMessage());
            }
        }
        shapeType = CUSTOM_SHAPE;
        break;
        case BOX_SHAPE:
        if(
           (Distortion == null || distortion == 0)
           &&
           (Skew == null || skew == 0)
           &&
           (Orientation == null || orientation == 0)
           ) {
            shapeType = BOX_SHAPE;
            if(Grappa.centerPointNodes) {
            shape = new Rectangle2D.Double(pos.x – (width/2.0), pos.y – (height/2.0), width, height);
            } else {
            shape = new Rectangle2D.Double(pos.x, pos.y, width, height);
            }
            if(Peripheries != null && peripheries > 1) {
            path = new GeneralPath(shape);
            for(int i = 1; i < peripheries; i++) {                 if(Grappa.centerPointNodes) {                 path.append                     (                      new Rectangle2D.Double                      (                       (pos.x - width/2.0) + (double)(i * PERIPHERY_GAP),                       (pos.y - height/2.0) + (double)(i * PERIPHERY_GAP),                       width - (double)(2 * i * PERIPHERY_GAP),                       height - (double)(2 * i * PERIPHERY_GAP)                       ),                      false                      );                 } else {                 path.append                     (                      new Rectangle2D.Double                      (                       pos.x + (double)(i * PERIPHERY_GAP),                       pos.y + (double)(i * PERIPHERY_GAP),                       width - (double)(2 * i * PERIPHERY_GAP),                       height - (double)(2 * i * PERIPHERY_GAP)                       ),                      false                      );                 }             }             shape = path;             }         } else {             shapeType = BOX_SHAPE|GRAPPA_SHAPE;             if(Grappa.centerPointNodes) {             shape = new GrappaShape(shapeType, pos.x, pos.y, width, height, sides, peripheries, distortion, skew, orientation, style.rounded, style.diagonals, null);             } else {             shape = new GrappaShape(shapeType, pos.x + (width/2.0), pos.y + (height/2.0), width, height, sides, peripheries, distortion, skew, orientation, style.rounded, style.diagonals, null);             }         }         break;         case ROUNDEDBOX_SHAPE:         if(            (Distortion == null || distortion == 0)            &&            (Skew == null || skew == 0)            &&            (Orientation == null || orientation == 0)            ) {             shapeType = ROUNDEDBOX_SHAPE;             if(Grappa.centerPointNodes) {             shape = new RoundRectangle2D.Double(pos.x - (width/2.0), pos.y - (height/2.0), width, height, arcWidthFactor * width, arcHeightFactor * height);             } else {             shape = new RoundRectangle2D.Double(pos.x, pos.y, width, height, arcWidthFactor * width, arcHeightFactor * height);             }             if(Peripheries != null && peripheries > 1) {
            path = new GeneralPath(shape);
            for(int i = 1; i < peripheries; i++) {                 if(Grappa.centerPointNodes) {                 path.append                     (                      new RoundRectangle2D.Double                      (                       (pos.x - width/2.0) + (double)(i * PERIPHERY_GAP),                       (pos.y - height/2.0) + (double)(i * PERIPHERY_GAP),                       width - (double)(2 * i * PERIPHERY_GAP),                       height - (double)(2 * i * PERIPHERY_GAP),                       arcWidthFactor * (width - (double)(2 * i * PERIPHERY_GAP)),                       arcHeightFactor * (height - (double)(2 * i * PERIPHERY_GAP))                       ),                      false                      );                 } else {                 path.append                     (                      new RoundRectangle2D.Double                      (                       pos.x + (double)(i * PERIPHERY_GAP),                       pos.y + (double)(i * PERIPHERY_GAP),                       width - (double)(2 * i * PERIPHERY_GAP),                       height - (double)(2 * i * PERIPHERY_GAP),                       arcWidthFactor * (width - (double)(2 * i * PERIPHERY_GAP)),                       arcHeightFactor * (height - (double)(2 * i * PERIPHERY_GAP))                       ),                      false                      );                 }             }             shape = path;             }         } else {             shapeType = ROUNDEDBOX_SHAPE|GRAPPA_SHAPE;             if(Grappa.centerPointNodes) {             shape = new GrappaShape(shapeType, pos.x, pos.y, width, height, sides, peripheries, distortion, skew, orientation, style.rounded, style.diagonals, null);             } else {             shape = new GrappaShape(shapeType, pos.x + (width/2.0), pos.y + (height/2.0), width, height, sides, peripheries, distortion, skew, orientation, style.rounded, style.diagonals, null);             }         }         break;         case OVAL_SHAPE:         if(            (Distortion == null || distortion == 0)            &&            (Skew == null || skew == 0)            &&            (Orientation == null || orientation == 0)            ) {             shapeType = OVAL_SHAPE;             if(Grappa.centerPointNodes) {             shape = new Ellipse2D.Double(pos.x - (width/2.0), pos.y - (height/2.0), width, height);             } else {             shape = new Ellipse2D.Double(pos.x, pos.y, width, height);             }             if(Peripheries != null && peripheries > 1) {
            path = new GeneralPath(shape);
            for(int i = 1; i < peripheries; i++) {                 if(Grappa.centerPointNodes) {                 path.append                     (                      new Ellipse2D.Double                      (                       (pos.x - width/2.0) + (double)(i * PERIPHERY_GAP),                       (pos.y - height/2.0) + (double)(i * PERIPHERY_GAP),                       width - (double)(2 * i * PERIPHERY_GAP),                       height - (double)(2 * i * PERIPHERY_GAP)                       ),                      false                      );                 } else {                 path.append                     (                      new Ellipse2D.Double                      (                       pos.x + (double)(i * PERIPHERY_GAP),                       pos.y + (double)(i * PERIPHERY_GAP),                       width - (double)(2 * i * PERIPHERY_GAP),                       height - (double)(2 * i * PERIPHERY_GAP)                       ),                      false                      );                 }             }             shape = path;             }         } else {             shapeType = OVAL_SHAPE|GRAPPA_SHAPE;             if(Grappa.centerPointNodes) {             shape = new GrappaShape(shapeType, pos.x, pos.y, width, height, sides, peripheries, distortion, skew, orientation, style.rounded, style.diagonals, null);             } else {             shape = new GrappaShape(shapeType, pos.x + (width/2.0), pos.y + (height/2.0), width, height, sides, peripheries, distortion, skew, orientation, style.rounded, style.diagonals, null);             }         }         break;         case DIAMOND_SHAPE:         case DOUBLECIRCLE_SHAPE:         case DOUBLEOCTAGON_SHAPE:         case EGG_SHAPE:         case HEXAGON_SHAPE:         case HOUSE_SHAPE:         case INVERTEDHOUSE_SHAPE:         case INVERTEDTRAPEZIUM_SHAPE:         case INVERTEDTRIANGLE_SHAPE:         case OCTAGON_SHAPE:         case PARALLELOGRAM_SHAPE:         case PENTAGON_SHAPE:         case PLAINTEXT_SHAPE:         case POINT_SHAPE:         case POLYGON_SHAPE:         case TRAPEZIUM_SHAPE:         case TRIANGLE_SHAPE:         case TRIPLEOCTAGON_SHAPE:         case MCIRCLE_SHAPE:         case MDIAMOND_SHAPE:         case MSQUARE_SHAPE:         shapeType = type|GRAPPA_SHAPE;         if(Grappa.centerPointNodes) {             shape = new GrappaShape(shapeType, pos.x, pos.y, width, height, sides, peripheries, distortion, skew, orientation, style.rounded, style.diagonals, null);         } else {             shape = new GrappaShape(shapeType, pos.x + (width/2.0), pos.y + (height/2.0), width, height, sides, peripheries, distortion, skew, orientation, style.rounded, style.diagonals, null);         }         break;         case RECORD_SHAPE:         case MRECORD_SHAPE:         shapeType = type|GRAPPA_SHAPE;         objs = GrappaSupportRects.parseRecordInfo(node);         String rects = null;         if(objs != null)             rects = (String)(objs[2]);         if(Grappa.centerPointNodes) {             shape = new GrappaShape(shapeType, pos.x, pos.y, width, height, sides, peripheries, distortion, skew, orientation, style.rounded, style.diagonals, rects);         } else {             shape = new GrappaShape(shapeType, pos.x + (width/2.0), pos.y + (height/2.0), width, height, sides, peripheries, distortion, skew, orientation, style.rounded, style.diagonals, rects);         }         break;         default:         throw new IllegalArgumentException("unsupported type for this constructor (" + type + ")");         }         // handle rotation (rotation just spins the node,         // orientation spins it within a fixed bounding box         if(Rotation != null && rotation != 0 && shape != null) {         double theta = rotation;         if(Grappa.rotationInDegrees) {             theta = Math.PI * theta / 180.0;         }         if(Grappa.centerPointNodes)             shape = AffineTransform.getRotateInstance(theta,pos.x,pos.y).createTransformedShape(shape);         else             shape = AffineTransform.getRotateInstance(theta,pos.x+(width/2.0),pos.y+(height/2.0)).createTransformedShape(shape);         }         break;     case EDGE:         Edge edge = (Edge)element;         shapeType = LINE_SHAPE;         if(element.getSubgraph().isCluster() && element.getSubgraph().grappaNexus != null)         element.getSubgraph().grappaNexus.dirty = true;         if((shape = (Shape)edge.getAttributeValue(POS_ATTR)) == null) {         Integer attr_type = (Integer)(edge.getAttributeValue(DIR_ATTR));         edge.direction = (attr_type != null ? attr_type.intValue() : (edge.getGraph().isDirected()?GrappaLine.TAIL_ARROW_EDGE:GrappaLine.NONE_ARROW_EDGE));         // create a default straight line connecting the two         // node centers         edge.setAttribute(                   "pos",                   new GrappaLine(new GrappaPoint[] { (GrappaPoint)(edge.getTail().getAttributeValue(POS_ATTR)), (GrappaPoint)(edge.getHead().getAttributeValue(POS_ATTR)) }, edge.direction)                       );         shape = (Shape)edge.getAttributeValue(POS_ATTR);         }         break;     case SUBGRAPH:         Subgraph subgraph = (Subgraph)element;         shapeType = BOX_SHAPE;         dirty = false;         // cannot call subgraph.getBoundingBox() because it would recurse,         // so just put the guts here         Rectangle2D sgbox = null;         Element elem = null;         GraphEnumeration enm = subgraph.elements();         while(enm.hasMoreElements()) {         elem = enm.nextGraphElement();         if(elem == element) continue;         switch(elem.getType()) {         case Grappa.NODE:         case Grappa.EDGE:             elem.buildShape();             if(sgbox == null) {             sgbox = elem.grappaNexus.getBounds2D();             } else {             sgbox.add(elem.grappaNexus.rawBounds2D());             }             break;         case Grappa.SUBGRAPH:             if(sgbox == null) {             sgbox = ((Subgraph)elem).getBoundingBox();             } else {             sgbox.add(((Subgraph)elem).getBoundingBox());             }             break;         default: // cannot happen             throw new InternalError("unknown type (" + elem.getType() + ")");         }         }         GrappaSize minSize = (GrappaSize)element.getAttributeValue(MINSIZE_ATTR);         if(minSize != null) {         if(sgbox == null) {             sgbox = new java.awt.geom.Rectangle2D.Double(0,0,minSize.getWidth(),minSize.getHeight());         } else {             sgbox.add(new java.awt.geom.Rectangle2D.Double(sgbox.getCenterX()-(minSize.getWidth()/2.0),sgbox.getCenterY()-(minSize.getHeight()/2.0),minSize.getWidth(),minSize.getHeight()));         }         }         GrappaBox minBox = (GrappaBox)element.getAttributeValue(MINBOX_ATTR);         if(minBox != null) {         if(sgbox == null) {             sgbox = new java.awt.geom.Rectangle2D.Double(minBox.x,minBox.y,minBox.width,minBox.height);         } else {             sgbox.add(new java.awt.geom.Rectangle2D.Double(minBox.x,minBox.y,minBox.width,minBox.height));         }         }         if(sgbox == null) {         sgbox = new java.awt.geom.Rectangle2D.Double(0,0,0,0);         }         shape = sgbox;         break;     default:         throw new IllegalArgumentException("unrecognized element type (" + element.getType() + ") for " + element.getName());     }     bboxCheckSet();     lastUpdate = lastShapeUpdate = thisShapeUpdate;     }     /**      * Update the shape information for the underlying element.      * The style attribute is examined.
     */
    public void updateStyle() {
    long thisStyleUpdate = System.currentTimeMillis();
    if((style = (GrappaStyle)element.getAttributeValue(STYLE_ATTR)) == null) {
        throw new InternalError(“style defaults not properly set in Graph.java”);
    }

    // an attempt to handle font info passed via style instead of fontstyle
    if(
       style.font_style != null
       &&
       style.font_style != (Integer)element.getAttributeValue(FONTSTYLE_ATTR)
       ) {
        element.setAttribute(FONTSTYLE_ATTR,style.font_style);
        style.font_style = null;
    }
    lastUpdate = lastStyleUpdate = thisStyleUpdate;
    }

    /**
     * Update the text information for the underlying element.
     * The fontcolorfontnamefontsizefontstyle, and label attributes are examined.
     * The lp attribute is also examined for edges and subgraphs.
     */
    public void updateText() {
    String[] tstr = null;
    GrappaPoint[] tpos = null;
    Area area = null;
    Font tfont = null;
    boolean makeAdjustment = false;
    double signum = -1;
    int offset = 0;
    String headstr, tailstr;
    GrappaPoint headpt, tailpt;
    int lcnt = 0;
    boolean hasEdgeLabel = false;
    Attribute attr = null;

    long thisTextUpdate = System.currentTimeMillis();

    String[] labels;
    GrappaPoint[] lps;

    String labelAttr = (String)element.getAttributeValue(LABEL_ATTR);

    if(labelAttr != null && labelAttr.equals(“\\N”))
        labelAttr = element.getName();

    if(labelAttr != null && labelAttr.length() > 0) {
        lcnt++;
    } else labelAttr = null;

    headstr = tailstr = null;
    headpt = tailpt = null;

    if (element.isEdge()) {
        if ((headstr = (String)element.getAttributeValue(HEADLABEL_ATTR)) != null && (attr = element.getLocalAttribute(HEADLP_ATTR)) != null) {
        headpt = (GrappaPoint)(attr.getValue());
        lcnt++;
        hasEdgeLabel = true;
        } else headstr = null;
        if ((tailstr = (String)element.getAttributeValue(TAILLABEL_ATTR)) != null && (attr = element.getLocalAttribute(TAILLP_ATTR)) != null) {
        tailpt = (GrappaPoint)(attr.getValue());
        lcnt++;
        hasEdgeLabel = true;
        } else tailstr = null;
    }

    // all string attributes are trimmed when they are stored
    // if(labelAttr != null)
    //     labelAttr = labelAttr.trim();

    if(labelAttr != null || hasEdgeLabel) {
        if(
           element.isNode()
           &&
           (
        shapeType == (RECORD_SHAPE|GRAPPA_SHAPE)
        ||
        shapeType == (MRECORD_SHAPE|GRAPPA_SHAPE)
        )
           &&
           labelAttr.indexOf(‘|’) < 0            &&            labelAttr.indexOf('{') == 0            &&            labelAttr.lastIndexOf('}') == labelAttr.length() - 1            ) {         labelAttr = labelAttr.substring(1,labelAttr.length()-1).trim();         }         if(hasEdgeLabel || labelAttr.length() > 0) {

        String fontname = (String)element.getAttributeValue(FONTNAME_ATTR);
        Integer fontstyle = (Integer)element.getAttributeValue(FONTSTYLE_ATTR);
        Integer fontsize = (Integer)element.getAttributeValue(FONTSIZE_ATTR);
        Integer fontadj = (Integer)(element.getGraph()).getGrappaAttributeValue(GRAPPA_FONTSIZE_ADJUSTMENT_ATTR);

        // set font
        tfont = new Font(fontname,fontstyle.intValue(),fontsize.intValue() + fontadj.intValue());

        String rectString = null;

        int lines;
        int i;
        char[] array;
        int[] justification;
        Rectangle2D[] bnds;
        java.awt.font.LineMetrics[] mtrc;
        int start;
        char ch;
        String str;
        double wdinfo, htinfo;
        double top;
        double x;

        if(
           element.isNode()
           &&
           (
            shapeType == (RECORD_SHAPE|GRAPPA_SHAPE)
            ||
            shapeType == (MRECORD_SHAPE|GRAPPA_SHAPE)
            )
           &&
           labelAttr.indexOf(‘|’) >= 0
           ) {
            if(objs == null)
            updateShape();
            if(objs != null && objs[0] != null && objs[1] != null) {
            labels = (String[])objs[0];
            lps = (GrappaPoint[])objs[1];
            } else {
            labels = new String[1];
            labels[0] = labelAttr;
            lps = new GrappaPoint[1];
            lps[0] = ((Node)element).getCenterPoint();
            }
        } else {
            Subgraph sg;

            labels = new String[lcnt];
            lps = new GrappaPoint[lcnt];

            lcnt = 0;
            if (labelAttr != null)
            labels[lcnt++] = labelAttr;
            if (headstr != null) {
            labels[lcnt] = headstr;
            lps[lcnt] = headpt;
            lcnt++;
            }
            if (tailstr != null) {
            labels[lcnt] = tailstr;
            lps[lcnt] = tailpt;
            }

            if (labelAttr != null) {
            if(Grappa.autoPositionNodeLabel && element.isNode()) {
                lps[0] = ((Node)element).getCenterPoint();
            } else if((attr = element.getLocalAttribute(LP_ATTR)) == null || ((sg = element.getSubgraph()) != null && attr == sg.getLocalAttribute(LP_ATTR))) {
                Rectangle2D lbox;

                if((lbox = (Rectangle2D)element.getAttributeValue(BBOX_ATTR)) == null) {
                lbox = bbox;
                }

                if(element.isSubgraph() && lbox != null) {
                lps[0] = new GrappaPoint(lbox.getX() + lbox.getWidth()/2.0, (Grappa.labelGraphBottom?lbox.getMaxY():lbox.getMinY()));
                element.setAttribute(LP_ATTR, lps[0].clone());
                } else {
                lps[0] = null;
                }
            } else {
                lps[0] = (GrappaPoint)(attr.getValue());
            }
            if(element.isSubgraph() && attr == null) {
                if(Grappa.labelGraphBottom) {
                signum = 1;
                } else {
                signum = -1;
                }
                makeAdjustment = true;
            }
            }
        }

        for(int l = 0; l < labels.length; l++) {             if(labels[l] != null && lps[l] != null && labels[l].length() > 0) {

            if(labels[l].equals(“\\N”)) {
                labels[l] = element.getName();
                if(labels[l] == null) continue;
            }

            // break label into multiple lines as indicated by line-breaks
            lines = 1;
            array = labels[l].toCharArray();

            // first count lines
            for(i=0; i wdinfo) wdinfo = bnds[lines].getWidth();
                //htinfo += bnds[lines].getHeight();
                htinfo += 2 + tfont.getSize();
                if(ch == ‘l’) justification[lines++] = -1;
                else if(ch == ‘r’) justification[lines++] = 1;
                else justification[lines++] = 0;
                start = (i+1);
                }
            }
            if(start < array.length) {                 tstr[offset+lines] = new String(array,start,array.length - start);                 bnds[lines] = tfont.getStringBounds(tstr[offset+lines],element.getGraph().REFCNTXT);                 mtrc[lines] = tfont.getLineMetrics(tstr[offset+lines],element.getGraph().REFCNTXT);                 if(bnds[lines].getWidth() > wdinfo) wdinfo = bnds[lines].getWidth();
                //htinfo += bnds[lines].getHeight();
                htinfo += 2 + tfont.getSize();
                if(ch == ‘l’) justification[lines] = -1;
                else if(ch == ‘r’) justification[lines] = 1;
                else justification[lines] = 0;
            }

            //htinfo += mtrc[lines].getLeading();
            //htinfo += (mtrc[lines].getLeading()) * tfont.getSize2D() / mtrc[lines].getHeight();

            if(makeAdjustment) {
                if(Grappa.labelGraphOutside) {
                lps[l].y += signum * htinfo;
                } else {
                lps[l].y -= signum * htinfo;
                }
            }

            // half these as that’s how they will be used
            wdinfo /= 2.0;
            htinfo /= 2.0;

            // figure out textArea and positioning of each line of text
            // doing it now instead of at rendering time means some
            // approximation, but text rendering is iffy anyway and this
            // will be close enough and more efficient (if you call this
            // efficient)

            // first, find top of text bounding box
            top = lps[l].y – htinfo;

            // now, for each line:
            // 1. determine left-side position and create (add to) textArea
            // 2. getAscent() to determine actually draw position
            // 3. shift down hieght of line
            x = 0;
            for(i = 0; i < bnds.length; i++) {                 if(justification[i] < 0) {                 // left                 x = lps[l].x - wdinfo;                 } else if(justification[i] > 0) {
                // right
                x = lps[l].x + wdinfo – bnds[i].getWidth();
                } else {
                x = lps[l].x – bnds[i].getWidth()/2.0;
                }
                bnds[i].setRect(x,top,bnds[i].getWidth(),bnds[i].getHeight());
                if(area == null) {
                area = new Area(bnds[i]);
                } else {
                area.add(new Area(bnds[i]));
                }
                //tpos[offset+i] = new GrappaPoint(x, top + mtrc[i].getAscent());
                //tpos[offset+i] = new GrappaPoint(x, top + Math.ceil((mtrc[i].getAscent()+mtrc[i].getLeading())*tfont.getSize()/mtrc[i].getHeight()));
                tpos[offset+i] = new GrappaPoint(x, top + tfont.getSize() – 1);

                //top += bnds[i].getHeight();
                top += 2 + tfont.getSize();
            }
            }
        }
        }
    }

    // commit changes
    font = tfont;
    lpos = tpos;
    lstr = tstr;
    textArea = area;
    bboxCheckSet();
    lastUpdate = lastTextUpdate = thisTextUpdate;
    }

    /**
     * Update the decoration information for the underlying element.
     * The color and fontcolor attributes are examined.
     * For edges, the dir attribute is examined.
     */
    public void updateDecoration() {
    long thisDecorationUpdate = System.currentTimeMillis();
    color = (Color)(element.getAttributeValue(COLOR_ATTR));
    fillcolor = (Color)(element.getAttributeValue(FILLCOLOR_ATTR));
    font_color = (Color)(element.getAttributeValue(FONTCOLOR_ATTR));
    if(element.isEdge() && shape != null && shape instanceof GrappaLine) {
        Edge edge = (Edge)element;
        int graph_dir = edge.getGraph().isDirected() ? GrappaLine.TAIL_ARROW_EDGE : GrappaLine.NONE_ARROW_EDGE;
        int dir = graph_dir;
        Integer attr_type = (Integer)(edge.getThisAttributeValue(DIR_ATTR));
        if(attr_type != null)
        dir = attr_type.intValue(); 

        edge.direction = dir;

        GrappaLine gline = (GrappaLine)shape;
        boolean forward = gline.startsNear((Point2D)(edge.getTail().getAttributeValue(POS_ATTR))); 
        // basically, it edge loops on same node, assume it is always
        // in the forward orientation
        if(!forward && edge.getHead() == edge.getTail())
        forward = true;

        int line_dir;
        if(forward) {
        line_dir = gline.getArrowType();
        } else {
        switch(gline.getArrowType()) {
        case GrappaLine.HEAD_ARROW_EDGE:
            line_dir = GrappaLine.TAIL_ARROW_EDGE;
        case GrappaLine.TAIL_ARROW_EDGE:
            line_dir = GrappaLine.HEAD_ARROW_EDGE;
            break;
        default:
            line_dir = gline.getArrowType();
            break;
        }
        }
        if(line_dir != dir) {
        if(forward) {
            line_dir = dir;
        } else {
            switch(dir) {
            case GrappaLine.HEAD_ARROW_EDGE:
            line_dir = GrappaLine.TAIL_ARROW_EDGE;
            case GrappaLine.TAIL_ARROW_EDGE:
            line_dir = GrappaLine.HEAD_ARROW_EDGE;
            break;
            default:
            line_dir = dir;
            break;
            }
        }
        gline.changeArrowType(line_dir);
        edge.setAttribute(POS_ATTR, gline);
        }
    }
    lastUpdate = lastDecorationUpdate = thisDecorationUpdate;
    }

    /**
     * Update the image information for the underlying element.
     */
    public void updateImage() {
    long thisImageUpdate = System.currentTimeMillis();
    String path = (String)(element.getAttributeValue(IMAGE_ATTR));

    if(path != null && Grappa.toolkit != null) {

        this.image = null;
        imageLoading = true;

        Image raw_image = null;

        try {
        URL url = new URL(path);
        raw_image = Grappa.toolkit.getImage(url);
        }
        catch(Exception ex) {}

        if(raw_image == null) {
        try {
            raw_image = Grappa.toolkit.getImage(path);
        }
        catch(Exception ex) {}
        }

        if(raw_image != null) {
        if(Grappa.toolkit.prepareImage(raw_image,-1,-1,this)) {
            this.image = raw_image;
            imageLoading = false;
        }
        } else {
        imageLoading = false;
        }
    } else {
        this.image = null;
        imageLoading = false;
    }

    lastUpdate = lastImageUpdate = thisImageUpdate;
    }

    public final boolean
    imageUpdate(Image image, int flags, int x, int y, int width, int height) {

    boolean ret = true;

    synchronized(this) {
        if((flags&ALLBITS) == ALLBITS) {
        ret = false;
        this.image = image;
        imageLoading = false;
        notifyAll();
        } else if((flags&(ABORT|ERROR)) != 0) {
        ret = false;
        imageLoading = false;
        notifyAll();
        }
    }

    return(ret);
    }

    ////////////////////////////////////////////////////////////////////////
    //
    // Private methods
    //
    ////////////////////////////////////////////////////////////////////////

    private void bboxCheckSet() {
    Rectangle2D oldbox = bbox;
    bbox = null;
    Rectangle2D newbox = null;
    try {
        newbox = rawBounds2D();
    }
    catch(Exception ex) {
        throw (RuntimeException)(ex.fillInStackTrace());
    }
    finally {
        bbox = oldbox;
    }

    if(newbox == null) {
        if(element.isSubgraph() && ((Subgraph)element).countOfElements(SUBGRAPH|NODE|EDGE) == 0) {
        newbox = new Rectangle2D.Double();
        } else {
        throw new InternalError(“new bounding box of \”” + element.getName() + “\” is null”);
        }
    }
    if(
       (oldbox == null && newbox != null)
       ||
       (oldbox != null && newbox == null)
       ||
       (newbox != null && !newbox.equals(oldbox))
       ) {
        // bounding box has changed so null out existing bboxes of enclosing subgraphs
        Subgraph prnt = element.getSubgraph();
        while(prnt != null) {
        if(prnt.grappaNexus != null) {
          prnt.grappaNexus.bbox = null;
        }
        prnt = prnt.getSubgraph();
        }

        // commit
        bbox = newbox;
        lastUpdate = System.currentTimeMillis();
    }
    }

    ////////////////////////////////////////////////////////////////////////
    //
    // Cloneable interface
    //
    ////////////////////////////////////////////////////////////////////////
 
    /**
     * Creates a new object of the same class as this object.
     *
     * @return     a clone of this instance.
     * @exception  OutOfMemoryError            if there is not enough memory.
     * @see        java.lang.Cloneable
     */
    public Object clone() {
    try {
        GrappaNexus copy = (GrappaNexus) super.clone();
        if(shape != null) {
        if(shapeType == LINE_SHAPE) {
            copy.shape = (Shape) ((GrappaLine)shape).clone();
        } else if(shapeType == BOX_SHAPE) {
            copy.shape = (Shape) ((Rectangle2D)shape).clone();
        } else if(shapeType == ROUNDEDBOX_SHAPE) {
            copy.shape = (Shape) ((RoundRectangle2D)shape).clone();
        } else if(shapeType == OVAL_SHAPE) {
            copy.shape = (Shape) ((Ellipse2D)shape).clone();
        } else if((shapeType&GRAPPA_SHAPE) != 0) {
            copy.shape = (Shape) ((GrappaShape)shape).clone();
        } else {
            copy.shape = (Shape) ((GeneralPath)shape).clone();
        }
        }
        if(textArea != null) {
        copy.textArea = (Area) textArea.clone();
        }
        return copy;
    } catch (CloneNotSupportedException e) {
        // this shouldn’t happen, since we are Cloneable
        throw new InternalError();
    }
    }

    ////////////////////////////////////////////////////////////////////////
    //
    // Shape interface
    //
    ////////////////////////////////////////////////////////////////////////

    public boolean contains(double x, double y) {

    boolean contains = false;

    if(shape != null) {
        contains = shape.contains(x, y);
    }

    if(
       textArea != null && !contains && !clearText && drawText
       &&
       (
        (element.isNode() && element.getGraph().getShowNodeLabels())
        ||
        (element.isEdge() && element.getGraph().getShowEdgeLabels())
        ||
        (element.isSubgraph() && element.getGraph().getShowSubgraphLabels())
        )
       ) {
        contains = textArea.contains(x, y);
    }

    return(contains);
    }

    public boolean contains(double x, double y, double width, double height) {

    boolean contains = false;

    if(shape != null) {
        contains = shape.contains(x, y, width, height);
    }

    if(
       textArea != null && !contains && !clearText && drawText
       &&
       (
        (element.isNode() && element.getGraph().getShowNodeLabels())
        ||
        (element.isEdge() && element.getGraph().getShowEdgeLabels())
        ||
        (element.isSubgraph() && element.getGraph().getShowSubgraphLabels())
        )
       ) {
        contains = textArea.contains(x, y, width, height);
    }

    return(contains);
    }

    public boolean contains(Point2D p) {

    return(contains(p.getX(),p.getY()));
    }

    public boolean contains(Rectangle2D r) {

    return(contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()));
    }

    public Rectangle getBounds() {

    return(getBounds2D().getBounds());
    }

    public Rectangle2D getBounds2D() {

    if(dirty) {
        bbox = null;
        updateShape();
    }

    if(bbox == null) {

        if(shape != null) {
        bbox = shape.getBounds2D();
        }

        if(textArea != null && Grappa.shapeBoundText && boundText) {
        if(bbox == null) {
            bbox = textArea.getBounds();
        } else {
            bbox.add(textArea.getBounds());
        }
        }

        if(bbox.getHeight() == 0)
        bbox.setRect(bbox.getX(),bbox.getY(),bbox.getWidth(),0.01);
        if(bbox.getWidth() == 0)
        bbox.setRect(bbox.getX(),bbox.getY(),0.01,bbox.getHeight());

    }
    // return a clone so no one can mess with the real values
    return((Rectangle2D)(bbox.clone()));
    }

    // variation for use in Grappa
    Rectangle2D rawBounds2D() {

    if(dirty) {
        bbox = null;
        updateShape();
    }

    if(bbox == null) {

        if(shape != null) {
        bbox = shape.getBounds2D();
        }

        if(textArea != null && Grappa.shapeBoundText && boundText) {
        if(bbox == null) {
            bbox = textArea.getBounds();
        } else {
            bbox.add(textArea.getBounds());
        }
        }

        if(bbox.getHeight() == 0)
        bbox.setRect(bbox.getX(),bbox.getY(),bbox.getWidth(),0.01);
        if(bbox.getWidth() == 0)
        bbox.setRect(bbox.getX(),bbox.getY(),0.01,bbox.getHeight());
    }
    return(bbox);
    }

    /**
     * Equivalent to getPathIterator(null).
     *
     * @see getPathIterator(AffineTransform)
     */
    public PathIterator getPathIterator() {
    return new GrappaPathIterator(this, null);
    }

    public PathIterator getPathIterator(AffineTransform at) {
    return new GrappaPathIterator(this, at);
    }

    public PathIterator getPathIterator(AffineTransform at, double flatness) {
    return new FlatteningPathIterator(new GrappaPathIterator(this, at), flatness);
    }

    public boolean intersects(double x, double y, double width, double height) {

    boolean intersects = false;

    if(shape != null) {
        intersects = shape.intersects(x, y, width, height);
    }

    if(
       textArea != null && !intersects && !clearText && drawText
       &&
       (
        (element.isNode() && element.getGraph().getShowNodeLabels())
        ||
        (element.isEdge() && element.getGraph().getShowEdgeLabels())
        ||
        (element.isSubgraph() && element.getGraph().getShowSubgraphLabels())
        )
       ) {
        intersects = textArea.intersects(x, y, width, height);
    }

    return(intersects);
    }

    public boolean intersects(Rectangle2D r) {

    return(intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()));
    }
   
    ////////////////////////////////////////////////////////////////////////
    //
    // Observer interface
    //
    ////////////////////////////////////////////////////////////////////////

    /**
     * This method is called whenever the observed object is changed.
     * When certain observed attributes (attributes of interest)
     * are changed, this method will update the GrappaNexus as needed.
     *
     * @param obs the Observable must be an Attribute
     * @param arg either a Long giving the update time of the Attribute as returned by System.getTimeInMillis() or it is a two element Object array, where the first element is a new Attribute to be observed in place of that passed via obs and the second element is the update time of this new Attribute.
     */
    public void update(java.util.Observable obs, Object arg) {

    // begin boilerplate
    if(!(obs instanceof Attribute)) {
        throw new IllegalArgumentException(“expected to be observing attributes only (obs) for \”” + element.getName() + “\””);
    }
    Attribute attr = (Attribute)obs;
    if(arg instanceof Object[]) {
        Object[] args = (Object[])arg;
        if(args.length == 2 && args[0] instanceof Attribute && args[1] instanceof Long) {
        attr.deleteObserver(this);
        attr = (Attribute)args[0];
        attr.addObserver(this);
        // in case we call: super.update(obs,arg)
        obs = attr;
        arg = args[1];
        } else {
        throw new IllegalArgumentException(“badly formated update information for \”” + element.getName() + “\””);
        }
    }
    // end boilerplate

    // when this object is created it should register with the
    // appropriate Attributes based on how it was created;
    // this method  will then see what has been updated
    // and set flags in this object (and put tokens in an update
    // stack in Graph is autoUpdate is true) so that the appropriate
    // parts will be updated before any drawing occurs.
    if(arg instanceof Long) {

        String attrName = attr.getName();
        int attrHash = attr.getNameHash();
        long thisUpdate = ((Long)arg).longValue() + 1L;

        if(element == null || !element.reserve()) return;

        // reset
        objs = null;

        switch(element.getType()) {
        case NODE:
        if(
           (POS_HASH == attrHash && POS_ATTR.equals(attrName))
           ||
           (WIDTH_HASH == attrHash && WIDTH_ATTR.equals(attrName))
           ||
           (HEIGHT_HASH == attrHash && HEIGHT_ATTR.equals(attrName))
           ||
           (SHAPE_HASH == attrHash && SHAPE_ATTR.equals(attrName))
           ) {
            if(lastShapeUpdate < thisUpdate) {             updateShape();             if(Grappa.autoPositionNodeLabel) {                 updateText();             }             }         } else if(               (LABEL_HASH == attrHash && LABEL_ATTR.equals(attrName))               ||               (                !Grappa.autoPositionNodeLabel                &&                (LP_HASH == attrHash && LP_ATTR.equals(attrName)) // in case it is used                )               ||               (FONTSIZE_HASH == attrHash && FONTSIZE_ATTR.equals(attrName))               ||               (FONTNAME_HASH == attrHash && FONTNAME_ATTR.equals(attrName))               ||               (FONTSTYLE_HASH == attrHash && FONTSTYLE_ATTR.equals(attrName))               ) {             if(lastTextUpdate < thisUpdate) {             updateText();             }         } else if(               (STYLE_HASH == attrHash && STYLE_ATTR.equals(attrName))               ) {             if(lastStyleUpdate < thisUpdate) {             updateStyle();             }         } else if(               (COLOR_HASH == attrHash && COLOR_ATTR.equals(attrName))               ||               (FONTCOLOR_HASH == attrHash && FONTCOLOR_ATTR.equals(attrName))               ) {             if(lastDecorationUpdate < thisUpdate) {             updateDecoration();             }         } else if(               (IMAGE_HASH == attrHash && IMAGE_ATTR.equals(attrName))               ) {             if(lastImageUpdate < thisUpdate) {             updateImage();             }         } else {             throw new InternalError("update called for \"" + element.getName() + "\" with an unmonitored attribute: " + attrName);         }         break;         case EDGE:         if(            (POS_HASH == attrHash && POS_ATTR.equals(attrName))            ) {             if(lastShapeUpdate < thisUpdate) {             updateShape();             }         } else if(               (LABEL_HASH == attrHash && LABEL_ATTR.equals(attrName))               ||               (LP_HASH == attrHash && LP_ATTR.equals(attrName))               ||               (HEADLABEL_HASH == attrHash && HEADLABEL_ATTR.equals(attrName))               ||               (HEADLP_HASH == attrHash && HEADLP_ATTR.equals(attrName))               ||               (TAILLABEL_HASH == attrHash && TAILLABEL_ATTR.equals(attrName))               ||               (TAILLP_HASH == attrHash && TAILLP_ATTR.equals(attrName))               ||               (FONTSIZE_HASH == attrHash && FONTSIZE_ATTR.equals(attrName))               ||               (FONTNAME_HASH == attrHash && FONTNAME_ATTR.equals(attrName))               ||               (FONTSTYLE_HASH == attrHash && FONTSTYLE_ATTR.equals(attrName))               ) {             if(lastTextUpdate < thisUpdate) {             updateText();             }         } else if(               (STYLE_HASH == attrHash && STYLE_ATTR.equals(attrName))               ) {             if(lastStyleUpdate < thisUpdate) {             updateStyle();             }         } else if(               (COLOR_HASH == attrHash && COLOR_ATTR.equals(attrName))               ||               (DIR_HASH == attrHash && DIR_ATTR.equals(attrName))               ||               (FONTCOLOR_HASH == attrHash && FONTCOLOR_ATTR.equals(attrName))               ) {             if(lastDecorationUpdate < thisUpdate) {             updateDecoration();             }         } else if(               (IMAGE_HASH == attrHash && IMAGE_ATTR.equals(attrName))               ) {             if(lastImageUpdate < thisUpdate) {             updateImage();             }         } else {             throw new InternalError("update called for \"" + element.getName() + "\" with an unmonitored attribute: " + attrName);         }         break;         case SUBGRAPH:         if(            (LABEL_HASH == attrHash && LABEL_ATTR.equals(attrName))            ||            (LP_HASH == attrHash && LP_ATTR.equals(attrName))            ||            (FONTSIZE_HASH == attrHash && FONTSIZE_ATTR.equals(attrName))            ||            (FONTNAME_HASH == attrHash && FONTNAME_ATTR.equals(attrName))            ||            (FONTSTYLE_HASH == attrHash && FONTSTYLE_ATTR.equals(attrName))            ) {             if(lastTextUpdate < thisUpdate) {             updateText();             }         } else if(               (STYLE_HASH == attrHash && STYLE_ATTR.equals(attrName))               ) {             if(lastStyleUpdate < thisUpdate) {             updateStyle();             }         } else if(               (COLOR_HASH == attrHash && COLOR_ATTR.equals(attrName))               ||               (FONTCOLOR_HASH == attrHash && FONTCOLOR_ATTR.equals(attrName))               ) {             if(lastDecorationUpdate < thisUpdate) {             updateDecoration();             }         } else if(               (IMAGE_HASH == attrHash && IMAGE_ATTR.equals(attrName))               ) {             if(lastImageUpdate < thisUpdate) {             updateImage();             }         } else if(               (MINBOX_HASH == attrHash && MINBOX_ATTR.equals(attrName))               ||               (MINSIZE_HASH == attrHash && MINSIZE_ATTR.equals(attrName))               ) {             bbox = null;         } else {             throw new InternalError("update called for \"" + element.getName() + "\" with an unmonitored attribute: " + attrName);         }         break;         }         element.release();     } else {         throw new InternalError("update called for shape of element \"" + element.getName() + "\" without proper format");     }     }     /**      * Draw the element using the supplied Graphics2D context.      *      * @param g2d the Graphics2D context to be used for drawing      */     void draw(java.awt.Graphics2D g2d) {     if(shape instanceof CustomRenderer)         ((CustomRenderer)shape).draw(g2d);     else         g2d.draw(this);     }     /**      * Fill the element using the supplied Graphics2D context.      *      * @param g2d the Graphics2D context to be used for drawing      */     void fill(java.awt.Graphics2D g2d) {     if(shape instanceof CustomRenderer)         ((CustomRenderer)shape).fill(g2d);     else         g2d.fill(this);     }     /**      * Draw the image associated with the IMAGE_ATTR      * using the supplied Graphics2D context.      *      * @param g2d the Graphics2D context to be used for drawing      */     void drawImage(java.awt.Graphics2D g2d) {     if(Grappa.waitForImages && imageLoading) {         synchronized(this) {         try {             wait();         }         catch(InterruptedException e) {}         }     }     if(image != null) {         if(shape instanceof CustomRenderer) {         ((CustomRenderer)shape).drawImage(g2d);         } else {         Rectangle sbox = shape.getBounds();         Shape clip = g2d.getClip();         g2d.clip(shape);         g2d.drawImage(image, sbox.x, sbox.y, sbox.width, sbox.height, null);         g2d.setClip(clip);         }     }     } } att/grappa/GrappaPanel.java att/grappa/GrappaPanel.java/*  *  This software may only be used by you under license from AT&T Corp.  *  ("AT&T").  A copy of AT&T's Source Code Agreement is available at  *  AT&T's Internet website having the URL:  *  
 *  If you received this software without first entering into a license
 *  with AT&T, you have an infringing copy of this software and cannot use
 *  it without violating AT&T’s intellectual property rights.
 */

package att.grappa;

import java.awt.*;
import java.awt.geom.*;
import java.awt.print.*;
import java.util.Enumeration;
import java.util.Vector;
import javax.swing.event.AncestorListener;
import javax.swing.event.AncestorEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.event.PopupMenuEvent;
import javax.swing.Scrollable;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseEvent;
import java.awt.event.ComponentListener;
import java.awt.event.ComponentEvent;

/**
 * A class used for drawing the graph.
 *
 * @version 1.2, 26 May 2005; Copyright 1996 – 2005 by AT&T Corp.
 * @author  John Mocenigo, Research @ AT&T Labs
 */
public class GrappaPanel extends javax.swing.JPanel
    implements
    att.grappa.GrappaConstants,
    ComponentListener,
    AncestorListener, PopupMenuListener,
    MouseListener, MouseMotionListener,
    Printable,
        Runnable,
    Scrollable
{
    Graph graph;
    Subgraph subgraph;
    GrappaBacker backer;
    boolean nodeLabels, edgeLabels, subgLabels;
    AffineTransform transform = null;
    AffineTransform oldTransform = null;
    AffineTransform inverseTransform = null;
    Vector elementVector = null;
    int nextElement = -1;
    boolean scaleToFit = false;
    GrappaSize scaleToSize = null;
    GrappaListener grappaListener = null;

    private Element pressedElement = null;
    private GrappaPoint pressedPoint = null;
    private int pressedModifiers = 0;
    private GrappaStyle selectionStyle = null;
    private GrappaStyle deletionStyle = null;
    private double scaleFactor = 1;
    private double scaleInfo = 1;
    private GrappaBox outline = null;
    private GrappaBox savedOutline = null;
    private GrappaBox zoomBox = null;
    private boolean inMenu = false;
    private boolean scaleChanged = false;
    private boolean paintActive = false;

    private Point2D panelcpt = null;

    /**
     * Constructs a new canvas associated with a particular subgraph.
     * Keep in mind that Graph is a sub-class of Subgraph so that
     * usually a Graph object is passed to the constructor.
     *
     * @param subgraph the subgraph to be rendered on the canvas
     */
    public GrappaPanel(Subgraph subgraph) {
    this(subgraph, null);
    }

    /**
     * Constructs a new canvas associated with a particular subgraph.
     *
     * @param subgraph the subgraph to be rendered on the canvas.
     * @param backer used to draw a background for the graph.
     */
    public GrappaPanel(Subgraph subgraph, GrappaBacker backer) {
    super();
    this.subgraph = subgraph;
    this.backer = backer;
    this.graph = subgraph.getGraph();

    addAncestorListener(this);
    addComponentListener(this);

    selectionStyle = (GrappaStyle)(graph.getGrappaAttributeValue(GRAPPA_SELECTION_STYLE_ATTR));
    deletionStyle = (GrappaStyle)(graph.getGrappaAttributeValue(GRAPPA_DELETION_STYLE_ATTR));
    }

    /**
     * Adds the specified listener to receive mouse events from this graph.
     *
     * @param listener the event listener.
     * @return the previous event listener.
     *
     * @see GrappaAdapter
     */
    public GrappaListener addGrappaListener(GrappaListener listener) {
    GrappaListener oldGL = grappaListener;
    grappaListener = listener;
    if(grappaListener == null) {
        if (oldGL != null) {
        removeMouseListener(this);
        removeMouseMotionListener(this);
        }
        setToolTipText(null);
    } else {
        if (oldGL == null) {
        addMouseListener(this);
        addMouseMotionListener(this);
        }
        String tip = graph.getToolTipText();
        if(tip == null) {
        tip = Grappa.getToolTipText();
        }
        setToolTipText(tip);
    }
    return(oldGL);
    }

    /**
     * Removes the current listener from this graph.
     * Equivalent to addGrappaListener(null).
     *
     * @return the event listener just removed.
     */
    public GrappaListener removeGrappaListener() {
    return(addGrappaListener(null));
    }

    public int print(Graphics g, PageFormat pf, int pi)
    throws PrinterException
    {
    GrappaSize prevToSize = scaleToSize;
    boolean prevToFit = scaleToFit;

    if (pi >= 1) {
        return Printable.NO_SUCH_PAGE;
    }
    try {
        scaleToFit = false;
        scaleToSize = new GrappaSize(pf.getImageableWidth(), pf.getImageableHeight());
        ((Graphics2D)g).translate(pf.getImageableX(), pf.getImageableY());
        paintComponent(g);
    }
    finally {
        scaleToSize = prevToSize;
        scaleToFit = prevToFit;
    }
    return Printable.PAGE_EXISTS;
    }

    public void paintComponent(Graphics g) {
    Point2D cpt = null;

    if(Grappa.synchronizePaint || graph.getSynchronizePaint()) {
        if(graph.setPaint(true)) {
        cpt = componentPaint(g);
        graph.setPaint(false);
        }
    } else {
        cpt = componentPaint(g);
    }

    if (cpt != null) {
        setCPT(cpt);
        EventQueue.invokeLater(this);
    }
    }

    void setCPT(Point2D cpt) {
    panelcpt = cpt;
    }

    Point2D getCPT() {
    return(panelcpt);
    }

    private Point2D componentPaint(Graphics g) {
    if(subgraph == null || !subgraph.reserve()) return(null);
    Graphics2D g2d = (Graphics2D)g;
    int i;
    long thisPaint = System.currentTimeMillis();
    Container prnt;
    Container tprnt;

    Point2D cpt = null;

    //Color origBackground = g2d.getBackground();
    ////Composite origComposite = g2d.getComposite();
    //Paint origPaint = g2d.getPaint();
    //RenderingHints origRenderingHints = g2d.getRenderingHints();
    //Stroke origStroke = g2d.getStroke();
    //AffineTransform origAffineTransform = g2d.getTransform();
    //Font origFont = g2d.getFont();

    elementVector = null;

    GrappaBox bbox = new GrappaBox(subgraph.getBoundingBox());

    if(bbox == null) return(null);

    GrappaSize margins = (GrappaSize)(subgraph.getAttributeValue(MARGIN_ATTR));

    if(margins != null) {
        double x_margin = PointsPerInch * margins.width;
        double y_margin = PointsPerInch * margins.height;

        bbox.x -= x_margin;
        bbox.y -= y_margin;
        bbox.width += 2.0 * x_margin;
        bbox.height += 2.0 * y_margin;
    }

    subgLabels = subgraph.getShowSubgraphLabels();
    nodeLabels = subgraph.getShowNodeLabels();
    edgeLabels = subgraph.getShowEdgeLabels();
    if(Grappa.useAntiAliasing) {
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
    } else {
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_OFF);
    }
    if(Grappa.antiAliasText) {
        g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
    } else {
        g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
    }
    if(Grappa.useFractionalMetrics) {
        g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,RenderingHints.VALUE_FRACTIONALMETRICS_ON);
    }
    g2d.setStroke(GrappaStyle.defaultStroke);

    oldTransform = transform;
    transform = new AffineTransform();
    if(scaleToFit || scaleToSize != null) {
        scaleFactor = 1;
        zoomBox = null;
        double scaleToWidth = 0;
        double scaleToHeight = 0;
        if(scaleToFit) {
        tprnt = prnt = getParent();
        while (tprnt != null && !(tprnt instanceof javax.swing.JViewport))
            tprnt = tprnt.getParent();
        if (tprnt != null)
            prnt = tprnt;
        if(prnt instanceof javax.swing.JViewport) {
            Dimension sz = ((javax.swing.JViewport)prnt).getSize();
            scaleToWidth = sz.width;
            scaleToHeight = sz.height;
        } else {
            Rectangle scaleTo = getVisibleRect();
            scaleToWidth = scaleTo.width;
            scaleToHeight = scaleTo.height;
        }
        } else {
        scaleToWidth = scaleToSize.width;
        scaleToHeight = scaleToSize.height;
        }
        double widthRatio = scaleToWidth / bbox.getWidth();
        double heightRatio = scaleToHeight / bbox.getHeight();
        double xTranslate = 0;
        double yTranslate = 0;
        if(widthRatio < heightRatio) {         xTranslate = (scaleToWidth -  widthRatio * bbox.getWidth()) / ( 2.0 * widthRatio);         yTranslate = (scaleToHeight -  widthRatio * bbox.getHeight()) / (2.0 * widthRatio);         transform.scale(widthRatio, widthRatio);         scaleInfo = widthRatio;         } else {         xTranslate = (scaleToWidth -  heightRatio * bbox.getWidth()) / (2.0 * heightRatio);         yTranslate = (scaleToHeight -  heightRatio * bbox.getHeight()) / (2.0 * heightRatio);         transform.scale(heightRatio, heightRatio);         scaleInfo = heightRatio;         }         transform.translate(xTranslate, yTranslate);         setSize(new Dimension((int)Math.ceil(scaleToWidth),(int)Math.ceil(scaleToHeight)));         setPreferredSize(new Dimension((int)Math.ceil(scaleToWidth),(int)Math.ceil(scaleToHeight)));         transform.translate(-bbox.getMinX(),-bbox.getMinY());         scaleFactor = scaleInfo;     } else if(zoomBox != null) {         //System.err.println("zoombox");         Rectangle r;         tprnt = prnt = getParent();         while (tprnt != null && !(tprnt instanceof javax.swing.JViewport))         tprnt = tprnt.getParent();         if (tprnt != null)         prnt = tprnt;         if(prnt instanceof javax.swing.JViewport) {         Dimension sz = ((javax.swing.JViewport)prnt).getSize();         r = new Rectangle(0,0,sz.width,sz.height);         } else         r = getVisibleRect();         scaleFactor = 1;         if(zoomBox.width != 0 && zoomBox.height != 0 && oldTransform != null) {         GrappaBox zb = new GrappaBox(oldTransform.createTransformedShape(zoomBox).getBounds2D());         double scaleToWidth = r.width;         double scaleToHeight = r.height;         //System.err.println("zb=("+zb.x+","+zb.y+","+zb.width+","+zb.height+")");         //System.err.println("zB=("+zoomBox.x+","+zoomBox.y+","+zoomBox.width+","+zoomBox.height+")");         //System.err.println("vr=("+r.x+","+r.y+","+r.width+","+r.height+")");         double widthRatio = scaleToWidth / zoomBox.width;         double heightRatio = scaleToHeight / zoomBox.height;         double xTranslate = 0;         double yTranslate = 0;         if(widthRatio < heightRatio) {             scaleFactor = widthRatio;         } else {             scaleFactor = heightRatio;         }         transform.scale(scaleFactor, scaleFactor);         scaleInfo = scaleFactor;         //transform.translate(xTranslate, yTranslate);         scaleToWidth = bbox.getWidth() * scaleFactor;         scaleToHeight = bbox.getHeight() * scaleFactor;         setSize(new Dimension((int)Math.ceil(scaleToWidth),(int)Math.ceil(scaleToHeight)));         setPreferredSize(new Dimension((int)Math.ceil(scaleToWidth),(int)Math.ceil(scaleToHeight)));         transform.translate(-bbox.getMinX(),-bbox.getMinY());         cpt = new Point2D.Double(zoomBox.getCenterX(), zoomBox.getCenterY());         }         zoomBox = null;         //System.err.println("scaleFactor="+scaleFactor);         //transform.scale(scaleFactor, scaleFactor);         //int w = (int)Math.ceil(bbox.getWidth()*scaleFactor);         //int h = (int)Math.ceil(bbox.getHeight()*scaleFactor);         //w = w < r.width ? r.width : w;         //h = h < r.height ? r.height : h;         //setSize(new Dimension(w,h));         //setPreferredSize(new Dimension(w,h));         scaleFactor = scaleInfo;     } else if(scaleFactor != 1) {         Rectangle r = getVisibleRect();         cpt = null;         prnt = null;         if(scaleChanged) {         tprnt = prnt = getParent();         while (tprnt != null && !(tprnt instanceof javax.swing.JViewport))             tprnt = tprnt.getParent();         if (tprnt != null)             prnt = tprnt;         if(prnt instanceof javax.swing.JViewport && inverseTransform != null) {             Point2D pt = new Point2D.Double(r.x,r.y);             cpt = new Point2D.Double(r.x+r.width,r.y+r.height);             inverseTransform.transform(pt,pt);             inverseTransform.transform(cpt,cpt);             cpt.setLocation(                     pt.getX() + (cpt.getX() - pt.getX())/2.,                     pt.getY() + (cpt.getY() - pt.getY())/2.                     );         } else {             // to save checking again below             prnt = null;         }         }         transform.scale(scaleFactor, scaleFactor);         scaleInfo = scaleFactor;         int w = (int)Math.ceil(bbox.getWidth()*scaleFactor);         int h = (int)Math.ceil(bbox.getHeight()*scaleFactor);         w = w < r.width ? r.width : w;         h = h < r.height ? r.height : h;         setSize(new Dimension(w,h));         setPreferredSize(new Dimension(w,h));         transform.translate(-bbox.getMinX(),-bbox.getMinY());     } else {         cpt = null;         prnt = null;         if(scaleChanged) {         tprnt = prnt = getParent();         while (tprnt != null && !(tprnt instanceof javax.swing.JViewport))             tprnt = tprnt.getParent();         if (tprnt != null)             prnt = tprnt;         if(prnt instanceof javax.swing.JViewport && inverseTransform != null) {             Rectangle r = getVisibleRect();             Point2D pt = new Point2D.Double(r.x,r.y);             cpt = new Point2D.Double(r.x+r.width,r.y+r.height);             inverseTransform.transform(pt,pt);             inverseTransform.transform(cpt,cpt);             cpt.setLocation(                     pt.getX() + (cpt.getX() - pt.getX())/2.,                     pt.getY() + (cpt.getY() - pt.getY())/2.                     );         } else {             // to save checking again below             prnt = null;         }         }         scaleInfo = 1;         setSize(new Dimension((int)Math.ceil(bbox.getWidth()),(int)Math.ceil(bbox.getHeight())));         setPreferredSize(new Dimension((int)Math.ceil(bbox.getWidth()),(int)Math.ceil(bbox.getHeight())));         transform.translate(-bbox.getMinX(),-bbox.getMinY());     }     scaleChanged = false;     if(scaleInfo < Grappa.nodeLabelsScaleCutoff) {         nodeLabels = false;     }     if(scaleInfo < Grappa.edgeLabelsScaleCutoff) {         edgeLabels = false;     }     if(scaleInfo < Grappa.subgLabelsScaleCutoff) {         subgLabels = false;     }     try {         inverseTransform = transform.createInverse();     } catch(NoninvertibleTransformException nite) {         inverseTransform = null;     }     g2d.transform(transform);     Rectangle clip =  g2d.getClipBounds();     // grow bounds to account for Java's frugal definition of what     // constitutes the intersectable area of a shape     clip.x--;     clip.y--;     clip.width+=2;     clip.height+=2;     synchronized(graph) {         GrappaNexus grappaNexus = subgraph.grappaNexus;         if(grappaNexus != null) {             Color bkgdColor = null;         // do fill now in case there is a Backer supplied         g2d.setPaint(bkgdColor = (Color)(graph.getGrappaAttributeValue(GRAPPA_BACKGROUND_COLOR_ATTR)));         g2d.fill(clip);         if(grappaNexus.style.filled || grappaNexus.image != null) {             if(grappaNexus.style.filled) {             if (grappaNexus.fillcolor != null) {                 g2d.setPaint(bkgdColor = grappaNexus.fillcolor);                 grappaNexus.fill(g2d);                 if (grappaNexus.color != null)                 g2d.setPaint(grappaNexus.color);                 else                 g2d.setPaint(grappaNexus.style.line_color);             } else {                 g2d.setPaint(bkgdColor = grappaNexus.color);                 grappaNexus.fill(g2d);                 g2d.setPaint(grappaNexus.style.line_color);             }             }             grappaNexus.drawImage(g2d);             // for the main graph, only outline when filling/imaging             if(GrappaStyle.defaultStroke != grappaNexus.style.stroke) {             g2d.setStroke(grappaNexus.style.stroke);             grappaNexus.draw(g2d);             g2d.setStroke(GrappaStyle.defaultStroke);             } else {             grappaNexus.draw(g2d);             }         }         if(backer != null && Grappa.backgroundDrawing) {             backer.drawBackground(g2d, graph, bbox, clip);         }         paintSubgraph(g2d, subgraph, clip, bkgdColor);         }     }     //g2d.setBackground(origBackground);     ////g2d.setComposite(origComposite);     //g2d.setPaint(origPaint);     //g2d.setRenderingHints(origRenderingHints);     //g2d.setStroke(origStroke);     //g2d.setTransform(origAffineTransform);     //g2d.setFont(origFont);     subgraph.release();     return(cpt);     }     /**      * Centers the panel at the supplied point.      *      * @param cpt requested center point      *      */     public void centerPanelAtPoint(Point2D cpt) {     Container prnt, tprnt;     javax.swing.JViewport viewport;     //System.err.println("cpt="+cpt);     tprnt = prnt = getParent();     while (tprnt != null && !(tprnt instanceof javax.swing.JViewport))         tprnt = tprnt.getParent();     if (tprnt != null)         prnt = tprnt;     if (prnt instanceof javax.swing.JViewport) {         viewport = (javax.swing.JViewport)prnt;         transform.transform(cpt,cpt);         Dimension viewsize = viewport.getExtentSize();         viewport.setViewPosition(new Point((int)(cpt.getX() - ((double)viewsize.width)/2.), (int)(cpt.getY() - ((double)viewsize.height)/2.)));     } // else ...     }     /**      * Get the AffineTransform that applies to this drawing.      *      * @return the AffineTransform that applies to this drawing.      */     public AffineTransform getTransform() {     return (AffineTransform)(transform.clone());     }     /**      * Get the inverse AffineTransform that applies to this drawing.      *      * @return the inverse AffineTransform that applies to this drawing.      */     public AffineTransform getInverseTransform() {     return inverseTransform;     }     /**      * Registers the default text to display in a tool tip.      * Setting the default text to null turns off tool tips.      * The default text is displayed when the mouse is outside the      * graph boundaries, but within the panel.      *      * @see Graph#setToolTipText(String)      */     public void setToolTipText(String tip) {     //System.err.println("tip set to: " + tip);     super.setToolTipText(tip);     }     /**      * Generate an appropriate tooltip based on the mouse location      * provided by the given event.      * @return if a GrappaListener is available, the result of its      * grappaTip() method is returned, otherwise null.
     *
     * @see GrappaPanel#setToolTipText(String)
     */
    public String getToolTipText(MouseEvent mev) {
    if(inverseTransform == null || grappaListener == null) return(null);
    //System.err.println(“tip requested”);

    Point2D pt = inverseTransform.transform(mev.getPoint(),null);

    return(grappaListener.grappaTip(subgraph, findContainingElement(subgraph,pt), new GrappaPoint(pt.getX(), pt.getY()), mev.getModifiers(), this));
    }

    /**
     * Enable/disable scale-to-fit mode.
     *
     * @param setting if true, the graph drawing is scaled to fit the panel, otherwise the graph is drawn full-size.
     */
    public void setScaleToFit(boolean setting) {
    scaleToFit = setting;
    }

    /**
     * Scale the graph drawing to a specific size.
     */
    public void setScaleToSize(Dimension2D scaleSize) {

    if(scaleSize == null) {
        scaleToSize = null;
    } else {
        scaleToSize = new GrappaSize(scaleSize.getWidth(), scaleSize.getHeight());
    }
    }

    /**
     * Get the subgraph being drawn on this panel.
     *
     * @return the subgraph being drawn on this panel.
     */
    public Subgraph getSubgraph() {
    return(subgraph);
    }

    /**
     * Reset the scale factor to one.
     */
    public void resetZoom() {
    scaleChanged = scaleFactor != 1;
    scaleFactor = 1;
    zoomBox = null;
    }

    /**
     * Check if a swept outline is still available.
     *
     * @return true if there is an outline available.
     */
    public boolean hasOutline() {
    return(savedOutline != null);
    }

    /**
     * Clear swept outline, if any.
     */
    public void clearOutline() {
    savedOutline = null;
    }

    /**
     * Zoom the drawing to the outline just swept with the mouse, if any.
     *
     * @return the box corresponding to the swept outline, or null.
     */
    public GrappaBox zoomToOutline() {
    zoomBox = null;
    if(savedOutline != null) {
        scaleFactor = 1;
        zoomBox = new GrappaBox(savedOutline);
        savedOutline = null;
    }
    //System.err.println(“zoomBox=” + zoomBox);
    return(zoomBox);
    }

    /**
     * Zoom the drawing to the outline just swept with the mouse, if any.
     *
     * @param outline the zoom bounds
     * @return the box corresponding to the swept outline, or null.
     */
    public GrappaBox zoomToOutline(GrappaBox outline) {
    zoomBox = null;
    if(outline != null) {
        scaleFactor = 1;
        zoomBox = new GrappaBox(outline);
    }
    return(zoomBox);
    }

    /**
     * Adjust the scale factor by the supplied multiplier.
     *
     * @param multiplier multiply the scale factor by this amount.
     * @return the value of the previous scale factor.
     */
    public double multiplyScaleFactor(double multiplier) {
    double old = scaleFactor;
    zoomBox = null;
    scaleFactor *= multiplier;
    if(scaleFactor == 0) scaleFactor = old;
    scaleChanged = scaleFactor != old;
    return(old);
    }

    ////////////////////////////////////////////////////////////////////////
    //
    // Private methods
    //
    ////////////////////////////////////////////////////////////////////////

    private void paintSubgraph(Graphics2D g2d, Subgraph subg, Shape clipper, Color bkgdColor) {
    if(subg != subgraph && !subg.reserve()) return;

    Rectangle2D bbox = subg.getBoundingBox();
    GrappaNexus grappaNexus = subg.grappaNexus;

    if(bbox != null && grappaNexus != null && subg.visible && !grappaNexus.style.invis && clipper.intersects(bbox)) {

        Enumeration enm = null;

        int i;

        if(subg != subgraph) {
        g2d.setPaint(grappaNexus.color);
        if(grappaNexus.style.filled) {
            if (grappaNexus.fillcolor != null) {
            bkgdColor = grappaNexus.fillcolor;
            grappaNexus.fill(g2d);
            if (grappaNexus.color != null)
                g2d.setPaint(grappaNexus.color);
            else
                g2d.setPaint(grappaNexus.style.line_color);
            } else {
            bkgdColor = grappaNexus.color;
            grappaNexus.fill(g2d);
            g2d.setPaint(grappaNexus.style.line_color);
            }
        } else if(grappaNexus.color == bkgdColor) { // using == is OK (caching)
            g2d.setPaint(grappaNexus.style.line_color);
        }
        grappaNexus.drawImage(g2d);
        if(subg.isCluster() || Grappa.outlineSubgraphs) {
            if(GrappaStyle.defaultStroke != grappaNexus.style.stroke) {
                g2d.setStroke(grappaNexus.style.stroke);
                grappaNexus.draw(g2d);
                g2d.setStroke(GrappaStyle.defaultStroke);
            } else {
                grappaNexus.draw(g2d);
            }
        }
        }

        if((subg.highlight&DELETION_MASK) == DELETION_MASK) {
        g2d.setPaint(deletionStyle.line_color);
        if(GrappaStyle.defaultStroke != deletionStyle.stroke) {
            g2d.setStroke(deletionStyle.stroke);
            grappaNexus.draw(g2d);
            g2d.setStroke(GrappaStyle.defaultStroke);
        } else {
            grappaNexus.draw(g2d);
        }
        } else if((subg.highlight&SELECTION_MASK) == SELECTION_MASK) {
        g2d.setPaint(selectionStyle.line_color);
        if(GrappaStyle.defaultStroke != selectionStyle.stroke) {
            g2d.setStroke(selectionStyle.stroke);
            grappaNexus.draw(g2d);
            g2d.setStroke(GrappaStyle.defaultStroke);
        } else {
            grappaNexus.draw(g2d);
        }
        }

        if(grappaNexus.lstr != null && subgLabels) {
        g2d.setFont(grappaNexus.font);
        g2d.setPaint(grappaNexus.font_color);
        for(i = 0; i < grappaNexus.lstr.length; i++) {             g2d.drawString(grappaNexus.lstr[i],(int)grappaNexus.lpos[i].x,(int)grappaNexus.lpos[i].y);         }         }         enm = subg.subgraphElements();         Subgraph subsubg = null;         while(enm.hasMoreElements()) {         subsubg = (Subgraph)(enm.nextElement());         if(subsubg != null) paintSubgraph(g2d, subsubg, clipper, bkgdColor);         }         Node node;         enm = subg.nodeElements();         while(enm.hasMoreElements()) {         node = (Node)(enm.nextElement());         if(node == null || !node.reserve()) continue;         if((grappaNexus = node.grappaNexus) != null && node.visible && !grappaNexus.style.invis && clipper.intersects(grappaNexus.rawBounds2D())) {             if(grappaNexus.style.filled) {             if (grappaNexus.fillcolor != null) {                 g2d.setPaint(grappaNexus.fillcolor);                 grappaNexus.fill(g2d);                 if (grappaNexus.color != null)                 g2d.setPaint(grappaNexus.color);                 else                 g2d.setPaint(grappaNexus.style.line_color);             } else {                 g2d.setPaint(grappaNexus.color);                 grappaNexus.fill(g2d);                 g2d.setPaint(grappaNexus.style.line_color);             }             } else {             g2d.setPaint(grappaNexus.color);             }             grappaNexus.drawImage(g2d);             if((node.highlight&DELETION_MASK) == DELETION_MASK) {             g2d.setPaint(deletionStyle.line_color);             if(GrappaStyle.defaultStroke != deletionStyle.stroke) {                 g2d.setStroke(deletionStyle.stroke);                 grappaNexus.draw(g2d);                 g2d.setStroke(GrappaStyle.defaultStroke);             } else {                 grappaNexus.draw(g2d);             }             } else if((node.highlight&SELECTION_MASK) == SELECTION_MASK) {             g2d.setPaint(selectionStyle.line_color);             if(GrappaStyle.defaultStroke != selectionStyle.stroke) {                 g2d.setStroke(selectionStyle.stroke);                 grappaNexus.draw(g2d);                 g2d.setStroke(GrappaStyle.defaultStroke);             } else {                 grappaNexus.draw(g2d);             }             } else {             if(GrappaStyle.defaultStroke != grappaNexus.style.stroke) {                 g2d.setStroke(grappaNexus.style.stroke);                 grappaNexus.draw(g2d);                 g2d.setStroke(GrappaStyle.defaultStroke);             } else {                 grappaNexus.draw(g2d);             }             }             if(grappaNexus.lstr != null && nodeLabels) {             g2d.setFont(grappaNexus.font);             g2d.setPaint(grappaNexus.font_color);             for(i = 0; i < grappaNexus.lstr.length; i++) {                 g2d.drawString(grappaNexus.lstr[i],(int)grappaNexus.lpos[i].x,(int)grappaNexus.lpos[i].y);             }             }         }         node.release();         }         Edge edge;         enm = subg.edgeElements();         while(enm.hasMoreElements()) {         edge = (Edge)(enm.nextElement());         if(edge == null || !edge.reserve()) continue;         if((grappaNexus = edge.grappaNexus) != null && edge.visible && !grappaNexus.style.invis && clipper.intersects(grappaNexus.rawBounds2D())) {             grappaNexus.drawImage(g2d);             if((edge.highlight&DELETION_MASK) == DELETION_MASK) {             g2d.setPaint(deletionStyle.line_color);             grappaNexus.fill(g2d);             if(GrappaStyle.defaultStroke != deletionStyle.stroke) {                 g2d.setStroke(deletionStyle.stroke);                 grappaNexus.draw(g2d);                 g2d.setStroke(GrappaStyle.defaultStroke);             } else {                 grappaNexus.draw(g2d);             }             } else if((edge.highlight&SELECTION_MASK) == SELECTION_MASK) {             g2d.setPaint(selectionStyle.line_color);             grappaNexus.fill(g2d);             if(GrappaStyle.defaultStroke != selectionStyle.stroke) {                 g2d.setStroke(selectionStyle.stroke);                 grappaNexus.draw(g2d);                 g2d.setStroke(GrappaStyle.defaultStroke);             } else {                 grappaNexus.draw(g2d);             }             } else {             g2d.setPaint(grappaNexus.color);             grappaNexus.fill(g2d);             if(GrappaStyle.defaultStroke != grappaNexus.style.stroke) {                 g2d.setStroke(grappaNexus.style.stroke);                 grappaNexus.draw(g2d);                 g2d.setStroke(GrappaStyle.defaultStroke);             } else {                 grappaNexus.draw(g2d);             }             }             if(grappaNexus.lstr != null && edgeLabels) {             g2d.setFont(grappaNexus.font);             g2d.setPaint(grappaNexus.font_color);             for(i = 0; i < grappaNexus.lstr.length; i++) {                 g2d.drawString(grappaNexus.lstr[i],(int)grappaNexus.lpos[i].x,(int)grappaNexus.lpos[i].y);             }             }         }         edge.release();         }     }     subg.release();     }     private Element findContainingElement(Subgraph subg, Point2D pt) {     return(findContainingElement(subg, pt, null));     }     private Element findContainingElement(Subgraph subg, Point2D pt, Element crnt) {     Element elem;     Element[] stash = new Element[2];     stash[0] = crnt;     stash[1] = null;     if((elem = reallyFindContainingElement(subg, pt, stash)) == null)         elem = stash[1];     return(elem);     }     private Element reallyFindContainingElement(Subgraph subg, Point2D pt, Element[] stash) {     Enumeration enm;     Rectangle2D bb = subg.getBoundingBox();     GrappaNexus grappaNexus = null;     if(bb.contains(pt)) {         if((Grappa.elementSelection&EDGE) == EDGE) {         enm = subg.edgeElements();         Edge edge;         while(enm.hasMoreElements()) {             edge = (Edge)enm.nextElement();             if((grappaNexus = edge.grappaNexus) == null || !edge.selectable) continue;             if(grappaNexus.rawBounds2D().contains(pt)) {             if(grappaNexus.contains(pt.getX(),pt.getY())) {                 if(stash[0] == null)                 return((Element)edge);                 if(stash[1] == null)                 stash[1] = edge;                 if(stash[0] == edge)                 stash[0] = null;             }             }         }         }         if((Grappa.elementSelection&NODE) == NODE) {         enm = subg.nodeElements();         Node node;         while(enm.hasMoreElements()) {             node = (Node)enm.nextElement();             if((grappaNexus = node.grappaNexus) == null || !node.selectable) continue;             if(grappaNexus.rawBounds2D().contains(pt)) {             if(grappaNexus.contains(pt)) {                 if(stash[0] == null)                 return((Element)node);                 if(stash[1] == null)                 stash[1] = node;                 if(stash[0] == node)                 stash[0] = null;             }             }         }         }         Element subelem = null;         enm = subg.subgraphElements();         while(enm.hasMoreElements()) {         if((subelem = reallyFindContainingElement((Subgraph)(enm.nextElement()), pt, stash)) != null && subelem.selectable) {             if(stash[0] == null)             return(subelem);             if(stash[1] == null)             stash[1] = subelem;             if(stash[0] == subelem)             stash[0] = null;         }         }         if((Grappa.elementSelection&SUBGRAPH) == SUBGRAPH && subg.selectable) {         if(stash[0] == null)             return((Element)subg);         if(stash[1] == null)             stash[1] = subg;         if(stash[0] == subg)             stash[0] = null;         }     }     return(null);     }     ///////////////////////////////////////////////////////////////////     //     // AncestorListener Interface     //     ///////////////////////////////////////////////////////////////////     public void ancestorMoved(AncestorEvent aev) {     // don't care     }     public void ancestorAdded(AncestorEvent aev) {     graph.addPanel(this);     }     public void ancestorRemoved(AncestorEvent aev) {     graph.removePanel(this);     }     ///////////////////////////////////////////////////////////////////     //     // ComponentListener Interface     //     ///////////////////////////////////////////////////////////////////     public void componentHidden(ComponentEvent cev) {     // don't care     }     public void componentMoved(ComponentEvent cev) {     // don't care     }     public void componentResized(ComponentEvent cev) {     // Needed to reset JScrollPane scrollbars, for example     revalidate();     }     public void componentShown(ComponentEvent cev) {     // don't care     }     ///////////////////////////////////////////////////////////////////     //     // PopupMenuListener Interface     //     ///////////////////////////////////////////////////////////////////     public void popupMenuCanceled(PopupMenuEvent pmev) {     // don't care     }     public void popupMenuWillBecomeInvisible(PopupMenuEvent pmev) {     inMenu = false;     }     public void popupMenuWillBecomeVisible(PopupMenuEvent pmev) {     inMenu = true;     }     ///////////////////////////////////////////////////////////////////     //     // MouseListener Interface     //     ///////////////////////////////////////////////////////////////////     public void mouseClicked(MouseEvent mev) {     if(inverseTransform == null || grappaListener == null || inMenu) return;     Point2D pt = inverseTransform.transform(mev.getPoint(),null);     grappaListener.grappaClicked(subgraph, findContainingElement(subgraph,pt, (subgraph.currentSelection != null && subgraph.currentSelection instanceof Element ? ((Element)subgraph.currentSelection) : null)), new GrappaPoint(pt.getX(), pt.getY()), mev.getModifiers(), mev.getClickCount(), this);     }     public void mousePressed(MouseEvent mev) {     if(inverseTransform == null || grappaListener == null || inMenu) return;     Point2D pt = inverseTransform.transform(mev.getPoint(),null);     outline = null;     grappaListener.grappaPressed(subgraph, (pressedElement = findContainingElement(subgraph,pt)), (pressedPoint = new GrappaPoint(pt.getX(), pt.getY())), (pressedModifiers = mev.getModifiers()), this);     }     public void mouseReleased(MouseEvent mev) {     if(inverseTransform == null || grappaListener == null || inMenu) return;     int modifiers = mev.getModifiers();     Point2D pt = inverseTransform.transform(mev.getPoint(),null);     GrappaPoint gpt = new GrappaPoint(pt.getX(), pt.getY());     grappaListener.grappaReleased(subgraph, findContainingElement(subgraph,pt), gpt, modifiers, pressedElement, pressedPoint, pressedModifiers, outline, this);     if((modifiers&java.awt.event.InputEvent.BUTTON1_MASK) != 0 && (modifiers&java.awt.event.InputEvent.BUTTON1_MASK) == modifiers) {         if(outline != null) {         //System.err.println("saving outline");         savedOutline = GrappaSupport.boxFromCorners(outline, pressedPoint.x, pressedPoint.y, gpt.x, gpt.y);         outline = null;         } else {         //System.err.println("clearing outline");         savedOutline = null;         }     }          }     public void mouseEntered(MouseEvent mev) {     // don't care     }     public void mouseExited(MouseEvent mev) {     // don't care     }     ///////////////////////////////////////////////////////////////////     //     // MouseMotionListener Interface     //     ///////////////////////////////////////////////////////////////////     public void mouseDragged(MouseEvent mev) {     if(inverseTransform == null || grappaListener == null || inMenu) return;     int modifiers = mev.getModifiers();     Point2D pt = inverseTransform.transform(mev.getPoint(),null);     GrappaPoint gpt = new GrappaPoint(pt.getX(), pt.getY());     grappaListener.grappaDragged(subgraph, gpt, modifiers, pressedElement, pressedPoint, pressedModifiers, outline, this);     if((modifiers&java.awt.event.InputEvent.BUTTON1_MASK) != 0 && (modifiers&java.awt.event.InputEvent.BUTTON1_MASK) == modifiers) {         outline = GrappaSupport.boxFromCorners(outline, pressedPoint.x, pressedPoint.y, gpt.x, gpt.y);     }     }     public void mouseMoved(MouseEvent mev) {     // don't care     }     // --- Scrollable interface ----------------------------------------     /**      * Returns the size of the bounding box of the graph augmented by      * the margin attribute and any scaling.      *       * @return The preferredSize of a JViewport whose view is this Scrollable.      * @see JViewport#getPreferredSize      */     public Dimension getPreferredScrollableViewportSize() {     // preferred size is set above as needed, so just return it         return getPreferredSize();     }     /**      * Always returns 1 since a GrappaPanel has not logical rows or      * columns.      * @param visibleRect The view area visible within the viewport      * @param orientation Either SwingConstants.VERTICAL or SwingConstants.HORIZONTAL.      * @param direction Less than zero to scroll up/left, greater than zero for down/right.      * @return The "unit" increment for scrolling in the specified direction, which in the case of a GrappaPanel is always 1.      * @see JScrollBar#setUnitIncrement      */     public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {     return(1);     }     /**      * Returns 90% of the view area dimension that is in the orientation      * of the requested scroll.      * @param visibleRect The view area visible within the viewport      * @param orientation Either SwingConstants.VERTICAL or SwingConstants.HORIZONTAL.      * @param direction Less than zero to scroll up/left, greater than zero for down/right.      * @return The "unit" increment for scrolling in the specified direction, which in the case of a GrappaPanel is 90% of the visible width for a horizontal increment or 90% of the visible height for a vertical increment.      * @see JScrollBar#setBlockIncrement      */     public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {     int block;     if(orientation == javax.swing.SwingConstants.VERTICAL) {         block = (int)(visibleRect.height * 0.9);     } else {         block = (int)(visibleRect.width * 0.9);     }     if(block < 1)         block = 1;     return(block);     }     /**      * Always returns false as the viewport should not force the width of this       * GrappaPanel to match the width of the viewport.       *       * @return false      */     public boolean getScrollableTracksViewportWidth() {         return( false );     }     /**      * Always returns false as the viewport should not force the height of      * this GrappaPanel to match the width of the viewport.       *       * @return false      */     public boolean getScrollableTracksViewportHeight() {         return( false );     }     public void run() {     Point2D cpt = getCPT();     if (cpt != null) {         centerPanelAtPoint(cpt);     }     } } att/grappa/GrappaPathIterator.java att/grappa/GrappaPathIterator.java/*  *  This software may only be used by you under license from AT&T Corp.  *  ("AT&T").  A copy of AT&T's Source Code Agreement is available at  *  AT&T's Internet website having the URL:  *  
 *  If you received this software without first entering into a license
 *  with AT&T, you have an infringing copy of this software and cannot use
 *  it without violating AT&T’s intellectual property rights.
 */

package att.grappa;

import java.awt.*;
import java.awt.geom.*;

/**
 * This class provides a PathIterator for GrappaNexus shapes.
 *
 * @version 1.2, 26 May 2005; Copyright 1996 – 2005 by AT&T Corp.
 * @author  John Mocenigo, Research @ AT&T Labs
 */
public class GrappaPathIterator implements PathIterator
{
    GrappaNexus grappaNexus;
    AffineTransform affine;
    PathIterator shapeIterator = null;
    PathIterator areaIterator = null;
    double[] pts = new double[6];
    int type;

    ////////////////////////////////////////////////////////////////////////
    //
    // Constructors
    //
    ////////////////////////////////////////////////////////////////////////

    /**
     * Constructs a new GrappaPathIterator given a GrappaNexus.
     */
    public GrappaPathIterator(GrappaNexus shape) {
    this(shape, null);
    }

    /**
     * Constructs a new GrappaPathIterator given a GrappaNexus
     * and an optional AffineTransform.
     */
    public GrappaPathIterator(GrappaNexus shape, AffineTransform at) {
    if(shape == null) {
        throw new IllegalArgumentException(“shape cannot be null”);
    }
    this.grappaNexus = shape;
    this.affine = at;
    if(shape.shape != null) {
        shapeIterator = shape.shape.getPathIterator(this.affine);
        if(shapeIterator.isDone()) {
        shapeIterator = null;
        }
    }
    if(shape.textArea != null && (Grappa.shapeClearText || shape.clearText)) {
        areaIterator = shape.textArea.getPathIterator(this.affine);
        if(areaIterator.isDone()) {
        areaIterator = null;
        }
    }
    if(shapeIterator != null) {
        type = shapeIterator.currentSegment(pts);
    } else if(areaIterator != null) {
        type = areaIterator.currentSegment(pts);
    } else {
        throw new RuntimeException(“cannot initialize; nothing to iterate over”);
    }
    }

    ////////////////////////////////////////////////////////////////////////
    //
    // PathIterator interface
    //
    ////////////////////////////////////////////////////////////////////////

    public int currentSegment(double[] coords) {
    System.arraycopy(pts, 0, coords, 0, 6);
    return(type);
    }

    public int currentSegment(float[] coords) {
    coords[0] = (float)pts[0];
    coords[1] = (float)pts[1];
    coords[2] = (float)pts[2];
    coords[3] = (float)pts[3];
    coords[4] = (float)pts[4];
    coords[5] = (float)pts[5];
    return(type);
    }

    /**
     * Return the winding rule for determining the interior of the path.
     */
    public int getWindingRule() {
    return(grappaNexus.getWindingRule());
    }

    public boolean isDone() {
    return(
           (shapeIterator == null || shapeIterator.isDone())
           &&
           (areaIterator == null || areaIterator.isDone())
           );
    }

    public void next() {
    if(shapeIterator != null) {
        if(shapeIterator.isDone()) {
        shapeIterator = null;
        } else {
        shapeIterator.next();
        if(shapeIterator.isDone()) {
            shapeIterator = null;
        } else {
            type = shapeIterator.currentSegment(pts);
        }
        return;
        }
    }
    if(areaIterator != null) {
        if(areaIterator.isDone()) {
        areaIterator = null;
        } else {
        areaIterator.next();
        if(areaIterator.isDone()) {
            areaIterator = null;
        } else {
            type = areaIterator.currentSegment(pts);
        }
        return;
        }
    }
    }

}

att/grappa/GrappaPoint.java
att/grappa/GrappaPoint.java/*
 *  This software may only be used by you under license from AT&T Corp.
 *  (“AT&T”).  A copy of AT&T’s Source Code Agreement is available at
 *  AT&T’s Internet website having the URL:
 *  
 *  If you received this software without first entering into a license
 *  with AT&T, you have an infringing copy of this software and cannot use
 *  it without violating AT&T’s intellectual property rights.
 */

package att.grappa;

/**
 * This class extends java.awt.geom.Point2D.Double and provides built-in
 * string-to-Point2D and Point2D-to-string conversions suitable for Grappa.
 *
 * @version 1.2, 26 May 2005; Copyright 1996 – 2005 by AT&T Corp.
 * @author  John Mocenigo, Research @ AT&T Labs
 */
public class GrappaPoint extends java.awt.geom.Point2D.Double
{
    /**
     * Constructs and initializes a GrappaPoint with
     * coordinates (0, 0).
     */
    public GrappaPoint() {
    }

    /**
     * Constructs and initializes a GrappaPoint with the
     * specified coordinates.
     * @param x, y the coordinates to which to set the newly
     * constructed GrappaPoint
     */
    public GrappaPoint(double x, double y) {
    this.x = x;
    this.y = y;
    }

    /**
     * Constructs and initializes a GrappaPoint with the
     * coordinates derived from the specified String representation.
     * The String format should be: “x-coord,y-coord
     * @param coordString String representing the coordinates to which to
     * set the newly constructed GrappaPoint
     */
    public GrappaPoint(String coordString) {
    double[] coords = null;
    try {
        coords = GrappaSupport.arrayForTuple(coordString);
    }
    catch(NumberFormatException nfe) {
        throw new IllegalArgumentException(“coordinate string (” + coordString + “) has a bad number format (” + nfe.getMessage() + “)”);
    }
    if(coords == null || coords.length != 2) {
        throw new IllegalArgumentException(“coordinate string (” + coordString + “) does not contain 2 valid coordinates”);
    }
    this.x = coords[0];
    this.y = (Grappa.negateStringYCoord?-coords[1]:coords[1]);
    }

    /**
     * Provides a string representation of this object consistent 
     * with Grappa attributes.
     *
     * @return attribute-suitable string representation of this GrappaPoint.
     */
    public String toAttributeString() {
    return(toFormattedString(“%p”));
    }

    /**
     * Provides a formatted string representation of this object.
     * 
     * @param format the format used to build the string (%p is the base directive for a GrappaPoint).
     * @return a string representation of this GrappaPoint. 
     */
    public String toFormattedString(String format) {
    return(GrappaSupportPrintf.sprintf(new Object[] { format, this }));
    }

    /**
     * Provides a generic string representation of this object.
     * 
     * @return a generic string representation of this GrappaPoint. 
     */
    public String toString() {
    return(x+”,”+y);
    }
}

att/grappa/GrappaShape.java
att/grappa/GrappaShape.java/*
 *  This software may only be used by you under license from AT&T Corp.
 *  (“AT&T”).  A copy of AT&T’s Source Code Agreement is available at
 *  AT&T’s Internet website having the URL:
 *  
 *  If you received this software without first entering into a license
 *  with AT&T, you have an infringing copy of this software and cannot use
 *  it without violating AT&T’s intellectual property rights.
 */

package att.grappa;

import java.awt.*;
import java.awt.geom.*;

/**
 * This class provides a flexible, parameterized polygonal shape builder.
 * The guts of a GrappaShape is a GeneralPath object.
 *
 * @version 1.2, 26 May 2005; Copyright 1996 – 2005 by AT&T Corp.
 * @author  John Mocenigo, Research @ AT&T Labs
 */
public class GrappaShape
    implements
    GrappaConstants,
    Cloneable, Shape
{

    /**
     * path that defines shape
     */
    protected GeneralPath path = null;

    private final static double RBCONST = 12.0;
    private final static double RBCURVE = 0.5;

    private final static double CIRCLE_XDIAG = Math.sqrt(7.0/16.0);
    private final static double CIRCLE_YDIAG = 0.75;

    ////////////////////////////////////////////////////////////////////////
    //
    // Constructors
    //
    ////////////////////////////////////////////////////////////////////////

    /**
     * Constructs a new GrappaShape object.
     * The winding rule for this path is defaulted (from Grappa.windingRule).
     *
     * @param type the shape specifier (.e.g., EGG_SHAPE).
     * @param x the x-coordinate of the polygon center point.
     * @param y the y-coordinate of the polygon center point.
     * @param width the overall width of the polygon bounding box.
     * @param height the overall height of the polygon bounding box.
     * @param sidesArg the number of sides of the polygon.
     * @param peripheriesArg the number of peripheries (outlines) of the polygon.
     * @param distortionArg a distortion factor applied to the polygon shape, when non-zero
     * @param skewArg a skewing factor applied to the polygon shape, when non-zero
     * @param orientationArg an orientation angle in radians.
     * @param roundedArg a directive to round the corners.
     * @param diagonalsArg a directive to draw catercorners at the vertices.
     * @param extra used only with the record shape at present, it provides a string of tuples giving interior partition information.
     */
    public GrappaShape(int type, double x, double y, double width, double height, int sidesArg, int peripheriesArg, double distortionArg, double skewArg, double orientationArg, boolean roundedArg, boolean diagonalsArg, Object extra) {

    path = new GeneralPath(Grappa.windingRule);

    // defaults
    int sides = 120;
    int peripheries = 1;
    double distortion = 0;
    double skew = 0;
    double orientation = 0;
    boolean rounded = false;
    boolean diagonals = false;
    float[] rects = null;
    Point2D.Float rectsMin = null;
    Point2D.Float rectsMax = null;
    int i;

    type &= SHAPE_MASK;

    switch(type) {
    case MSQUARE_SHAPE:
        diagonals = true;
        type = BOX_SHAPE;
        // fall through to BOX_SHAPE case
    case BOX_SHAPE:
        sides = 4;
        break;
    case MDIAMOND_SHAPE:
        diagonals = true;
        type = DIAMOND_SHAPE;
        // fall through to DIAMOND_SHAPE case
    case DIAMOND_SHAPE:
        sides = 4;
        orientation = Math.PI / 4.0;
        break;
    case DOUBLECIRCLE_SHAPE:
        peripheries = 2;
        break;
    case DOUBLEOCTAGON_SHAPE:
        sides = 8;
        peripheries = 2;
        break;
    case EGG_SHAPE:
        distortion = -0.3;
        break;
    case HEXAGON_SHAPE:
        sides = 6;
        break;
    case HOUSE_SHAPE:
        sides = 5;
        distortion = -0.64;
        break;
    case INVERTEDHOUSE_SHAPE:
        sides = 5;
        distortion = -0.64;
        orientation = Math.PI;
        break;
    case INVERTEDTRAPEZIUM_SHAPE:
        sides = 4;
        distortion = -0.4;
        orientation = Math.PI;
        break;
    case INVERTEDTRIANGLE_SHAPE:
        sides = 3;
        orientation = Math.PI;
        break;
    case MRECORD_SHAPE:
        rounded = true;
        type = RECORD_SHAPE;
        // fall through to RECORD_SHAPE case
    case RECORD_SHAPE:
        sides = 4;
        break;
    case PARALLELOGRAM_SHAPE:
        sides = 4;
        skew = 0.6;
        break;
    case PENTAGON_SHAPE:
        sides = 5;
        break;
    case OCTAGON_SHAPE:
        sides = 8;
        break;
    case PLAINTEXT_SHAPE:
        sides = 0;
        break;
    case ROUNDEDBOX_SHAPE:
        sides = 4;
        rounded = true;
        break;
    case TRAPEZIUM_SHAPE:
        sides = 4;
        distortion = -0.4;
        break;
    case TRIANGLE_SHAPE:
        sides = 3;
        break;
    case TRIPLEOCTAGON_SHAPE:
        sides = 8;
        peripheries = 3;
        break;
    case MCIRCLE_SHAPE:
        diagonals = true;
        type = OVAL_SHAPE;
        // fall through to OVAL_SHAPE case
    case OVAL_SHAPE:
    case POINT_SHAPE:
    case POLYGON_SHAPE:
    default:
        break;
    case CUSTOM_SHAPE:
        sidesArg = 4;
        peripheriesArg = 0;
        distortionArg = 0;
        skewArg = 0;
        orientationArg = 0;
        roundedArg = false;
        diagonalsArg = false;
        extra = null;
        break;
    }

    sides = (sidesArg >= 0) ? sidesArg : sides;
    peripheries = (peripheriesArg >= 0) ? peripheriesArg : peripheries;
    distortion = (distortionArg != 0) ? distortionArg : distortion;
    skew = (skewArg != 0) ? skewArg : skew;
    orientation = (orientationArg != 0) ? orientationArg : orientation;
    rounded = (roundedArg) ? roundedArg : rounded;
    diagonals = (diagonalsArg) ? diagonalsArg : diagonals;

    if(sides < 3) {         sides = 0;     }     if(type == OVAL_SHAPE || type == POINT_SHAPE) {         sides = 120;     } else if(type == RECORD_SHAPE) {         // basically, only rounded is an option         sides = 4;         peripheries = 1;         distortion = 0;         skew = 0;         orientation = 0;         diagonals = false;         if(extra != null && extra instanceof String) {         rects = GrappaSupport.floatArrayForTuple((String)extra);         if((rects.length % 4) == 0) {             rectsMin = new Point2D.Float();             rectsMax = new Point2D.Float();             rectsMax.x = rectsMin.x = rects[0];             if(Grappa.negateStringYCoord) {             rectsMax.y = rectsMin.y = -rects[1];             } else {             rectsMax.y = rectsMin.y = rects[1];             }             for(i = 0; i < rects.length; i += 2) {             if(Grappa.negateStringYCoord)                 rects[i+1] = -rects[i+1];             if(rects[i] < rectsMin.x)                 rectsMin.x = rects[i];             if(rects[i] > rectsMax.x)
                rectsMax.x = rects[i];
            if(rects[i+1] < rectsMin.y)                 rectsMin.y = rects[i+1];             if(rects[i+1] > rectsMax.y)
                rectsMax.y = rects[i+1];
            }
            width = rectsMax.x – rectsMin.x;
            height = rectsMax.y – rectsMin.y;
            x = rectsMin.x + (width / 2.0);
            y = rectsMin.y + (height / 2.0);
        } else {
            rects = null;
        }
        }
    }

    if(peripheries < 1 || sides == 0) {         path.moveTo((float)x,(float)y);         return;     }     double dtmp, alpha, beta;     double sectorAngle = 2.0 * Math.PI / (double)sides;     double sideLength  = Math.sin(sectorAngle/2.0);     double skewDist, gDist, gSkew, angle;     if(skew == 0 && distortion == 0) {         skewDist = 1;         gDist = 0;         gSkew = 0;     } else {         skewDist = Math.abs(distortion) + Math.abs(skew);         skewDist = Math.sqrt(skewDist * skewDist + 1.0);         gDist = distortion * Math.sqrt(2.0) / Math.cos(sectorAngle/2.0);         gSkew = skew / 2.0;     }     GrappaPoint Rpt = new GrappaPoint();     GrappaPoint Ppt = new GrappaPoint();     GrappaPoint Bpt = new GrappaPoint();     int verts = 0;     double sinX, cosX;     GrappaPoint[] rawVertices = new GrappaPoint[sides];     GrappaPoint[] tmpVertices = null;     if(diagonals) {         tmpVertices = new GrappaPoint[2 * sides];     } else if(rounded) {         tmpVertices = new GrappaPoint[4 * sides];     }     GrappaPoint Pt0;     GrappaPoint Pt1;     double tmp1 = 0;     double tmp2 = 0;     int tverts = 0;     double delta_w = 0, delta_h = 0;     double minX = 0, minY = 0, maxX = 0, maxY = 0;     boolean tooSmall = false;     tmp1 = (peripheries - 1) * (2 * PERIPHERY_GAP);     if((width - tmp1) <= 0 || (height - tmp2) <= 0) {         peripheries = 1;     }     for(int j = 0; j < peripheries; j++) {         if(j > 0) {
        if(j == 1) {

            Ppt.x = rawVertices[sides-1].x;
            Ppt.y = rawVertices[sides-1].y;

            Rpt.x = rawVertices[0].x;
            Rpt.y = rawVertices[0].y;

            beta = Math.atan2(Rpt.y-Ppt.y,Rpt.x-Ppt.x);

            for(i = 0; i < sides; i++) {             Ppt.x = Rpt.x;             Ppt.y = Rpt.y;             Rpt.x = rawVertices[(i+1)%sides].x;             Rpt.y = rawVertices[(i+1)%sides].y;             alpha = beta;             beta = Math.atan2(Rpt.y-Ppt.y,Rpt.x-Ppt.x);             tmp1 = (alpha + Math.PI - beta) / 2.0;             /*              * find the distance along the bisector to the              * intersection of the next periphery              */             dtmp = PERIPHERY_GAP / Math.sin(tmp1);             // convert this distance to x and y             tmp2 = alpha - tmp1;             sinX = Math.sin(tmp2);             cosX = Math.cos(tmp2);             sinX *= dtmp;             cosX *= dtmp;             tmp1 = Ppt.x - cosX;             tmp2 = Ppt.y - sinX;             if(i == 0) {                 maxX = minX = tmp1;                 maxY = minY = tmp2;             } else {                 if(minX > tmp1) minX = tmp1;
                if(maxX < tmp1) maxX = tmp1;                 if(minY > tmp2) minY = tmp2;
                if(maxY < tmp2) maxY = tmp2;             }             }             delta_w = width - (maxX - minX);             delta_h = height - (maxY - minY);         }         width -= delta_w;         height -= delta_h;         }         angle = (sectorAngle - Math.PI)/2.0;         //angle = sectorAngle/2.0;         sinX = Math.sin(angle);         cosX = Math.cos(angle);         Rpt.x = 0.5 * cosX;         Rpt.y = 0.5 * sinX;         angle += (Math.PI - sectorAngle)/2.0;         //angle = Math.PI/2.0;         Bpt.x = 0;         Bpt.y = 0;         verts = 0;         for(i = 0; i < sides; i++) {         // next regular vertex         angle += sectorAngle;         sinX = Math.sin(angle);         cosX = Math.cos(angle);         Rpt.x += sideLength*cosX;         Rpt.y += sideLength*sinX;         // distort and skew         Ppt.x = Rpt.x * (skewDist + Rpt.y * gDist) + Rpt.y * gSkew;         Ppt.y = Rpt.y;         // orient Ppt         if(orientation != 0) {             alpha = orientation + Math.atan2(Ppt.y,Ppt.x);              sinX = Math.sin(alpha);             cosX = Math.cos(alpha);             dtmp = Ppt.distance(0,0);             Ppt.x = dtmp * cosX;             Ppt.y = dtmp * sinX;         }         // scale         Ppt.x *= width;         Ppt.y *= height;         // store result         rawVertices[verts++] = (GrappaPoint)Ppt.clone();         if(Bpt.x < Math.abs(Ppt.x)) Bpt.x = Math.abs(Ppt.x);         if(Bpt.y < Math.abs(Ppt.y)) Bpt.y = Math.abs(Ppt.y);         }         Bpt.x = width  / (2.0 * Bpt.x);         Bpt.y = height / (2.0 * Bpt.y);         for(i = 0; i < sides; i++) {         rawVertices[i].x *= Bpt.x;         rawVertices[i].y *= Bpt.y;         }         if((rounded || diagonals) && type != OVAL_SHAPE && type != POINT_SHAPE && j == (peripheries - 1)) {         tooSmall = false;         tverts = 0;         Pt0 = rawVertices[0];         for(i = 0; i < sides; i++) {             // already scaled             Pt0 = rawVertices[i];             if(i < sides - 1) {             Pt1 = rawVertices[i+1];             } else {             Pt1 = rawVertices[0];             }             tmp2 = Pt0.distance(Pt1);             if(tmp2 < RBCONST) {             tooSmall = true;             break;             }             tmp1 = RBCONST / tmp2;             if(!diagonals) {             tmp2 = RBCURVE * tmp1;             tmpVertices[tverts++] = new GrappaPoint(                                 Pt0.x + tmp2 * (Pt1.x - Pt0.x),                                 Pt0.y + tmp2 * (Pt1.y - Pt0.y)                                 );             }             tmpVertices[tverts++] = new GrappaPoint(                                 Pt0.x + tmp1 * (Pt1.x - Pt0.x),                                 Pt0.y + tmp1 * (Pt1.y - Pt0.y)                                 );             tmp1 = 1 - tmp1;             tmpVertices[tverts++] = new GrappaPoint(                                 Pt0.x + tmp1 * (Pt1.x - Pt0.x),                                 Pt0.y + tmp1 * (Pt1.y - Pt0.y)                                 );             if(!diagonals) {             tmp2 = 1 - tmp2;             tmpVertices[tverts++] = new GrappaPoint(                                 Pt0.x + tmp2 * (Pt1.x - Pt0.x),                                 Pt0.y + tmp2 * (Pt1.y - Pt0.y)                                 );             }         }         if(tooSmall) {             for(i = 0; i < sides; i++) {             if(i == 0) {                 path.moveTo((float)(x + rawVertices[i].x), (float)(y - rawVertices[i].y));             } else {                 path.lineTo((float)(x + rawVertices[i].x), (float)(y - rawVertices[i].y));             }             }         } else {             if(diagonals) {             path.moveTo((float)(x + tmpVertices[0].x), (float)(y - tmpVertices[0].y));             for(i = (2*sides)-1; i > 0; i-=2) {
                path.lineTo((float)(x + tmpVertices[i].x), (float)(y – tmpVertices[i].y));
                path.moveTo((float)(x + tmpVertices[i-1].x), (float)(y – tmpVertices[i-1].y));
            }
            for(i = 0; i < sides; i++) {                 if(i == 0) {                 path.moveTo((float)(x + rawVertices[i].x), (float)(y - rawVertices[i].y));                 } else {                 path.lineTo((float)(x + rawVertices[i].x), (float)(y - rawVertices[i].y));                 }             }             } else {             path.moveTo((float)(x + tmpVertices[2].x), (float)(y - tmpVertices[2].y));             for(i = 3; i < (4*sides)-2; i+=4) {                 path.curveTo(                      (float)(x + tmpVertices[i].x), (float)(y - tmpVertices[i].y),                      (float)(x + tmpVertices[i+1].x), (float)(y - tmpVertices[i+1].y),                      (float)(x + tmpVertices[i+2].x), (float)(y - tmpVertices[i+2].y)                      );                 path.lineTo((float)(x + tmpVertices[i+3].x), (float)(y - tmpVertices[i+3].y));             }             i = (4 * sides) - 1;             path.curveTo(                      (float)(x + tmpVertices[i].x), (float)(y - tmpVertices[i].y),                      (float)(x + tmpVertices[0].x), (float)(y - tmpVertices[0].y),                      (float)(x + tmpVertices[1].x), (float)(y - tmpVertices[1].y)                      );             }         }         } else {         for(i = 0; i < sides; i++) {             if(i == 0) {             path.moveTo((float)(x + rawVertices[i].x), (float)(y - rawVertices[i].y));             } else {             path.lineTo((float)(x + rawVertices[i].x), (float)(y - rawVertices[i].y));             }         }         }         path.closePath();     }     // special cases     if(type == OVAL_SHAPE && diagonals) {         Pt0 = new GrappaPoint(                   width * CIRCLE_XDIAG / 2.0,                   height * CIRCLE_YDIAG / 2.0                   );         Ppt.x = x + Pt0.x;         Ppt.y = y - Pt0.y;         Rpt.x = Ppt.x - 2.0 * Pt0.x;         Rpt.y = Ppt.y;         path.moveTo((float)Ppt.x, (float)Ppt.y);         path.lineTo((float)Rpt.x, (float)Rpt.y);                  Ppt.y += (2.0 * Pt0.y) - 1.0;         Rpt.y = Ppt.y;         path.moveTo((float)Ppt.x, (float)Ppt.y);         path.lineTo((float)Rpt.x, (float)Rpt.y);              } else if(type == RECORD_SHAPE) {         if(rects != null) {         float tmp;         for(i = 0; i < rects.length; i += 4) {             if(rects[i] > rects[i+2]) {
            tmp = rects[i];
            rects[i] = rects[i+2];
            rects[i+2] = tmp;
            }
            if(rects[i+1] > rects[i+3]) {
            tmp = rects[i+1];
            rects[i+1] = rects[i+3];
            rects[i+3] = tmp;
            }
            if(!(rects[i] == rectsMin.x && rects[i+2] == rectsMax.x) || rects[i+1] != rectsMin.y) {
            path.moveTo(rects[i],rects[i+1]);
            path.lineTo(rects[i+2],rects[i+1]);
            }
            if(rects[i+2] != rectsMax.x || !(rects[i+1] == rectsMin.y && rects[i+3] == rectsMax.y)) {
            path.moveTo(rects[i+2],rects[i+1]);
            path.lineTo(rects[i+2],rects[i+3]);
            }
            if(!(rects[i] == rectsMin.x && rects[i+2] == rectsMax.x) || rects[i+3] != rectsMax.y) {
            path.moveTo(rects[i+2],rects[i+3]);
            path.lineTo(rects[i],rects[i+3]);
            }
            if(rects[i] != rectsMin.x || !(rects[i+1] == rectsMin.y && rects[i+3] == rectsMax.y)) {
            path.moveTo(rects[i],rects[i+3]);
            path.lineTo(rects[i],rects[i+1]);
            }
        }
        }
    }
    }

    ////////////////////////////////////////////////////////////////////////
    //
    // Cloneable interface
    //
    ////////////////////////////////////////////////////////////////////////
 
    /**
     * Creates a new object of the same class as this object.
     *
     * @return     a clone of this instance.
     * @exception  OutOfMemoryError            if there is not enough memory.
     * @see        java.lang.Cloneable
     */
    public Object clone() {
    try {
        GrappaShape copy = (GrappaShape) super.clone();
        copy.path = (GeneralPath) path.clone();
        return copy;
    } catch (CloneNotSupportedException e) {
        // this shouldn’t happen, since we are Cloneable
        throw new InternalError();
    }
    }

    ////////////////////////////////////////////////////////////////////////
    //
    // Shape interface
    //
    ////////////////////////////////////////////////////////////////////////

    public final boolean contains(double x, double y) {
    return(path.contains(x, y));
    }

    public final boolean contains(double x, double y, double width, double height) {
    return(path.contains(x, y, width, height));
    }

    public final boolean contains(Point2D p) {
    return(path.contains(p));
    }

    public final boolean contains(Rectangle2D r) {
    return(path.contains(r));
    }

    public final Rectangle getBounds() {
    return(path.getBounds2D().getBounds());
    }

    public final Rectangle2D getBounds2D() {
    return(path.getBounds2D());
    }

    /**
     * Equivalent to getPathIterator(null).
     *
     * @see getPathIterator(AffineTransform)
     */
    public final PathIterator getPathIterator() {
    return path.getPathIterator(null);
    }

    public final PathIterator getPathIterator(AffineTransform at) {
    return path.getPathIterator(at);
    }

    public final PathIterator getPathIterator(AffineTransform at, double flatness) {
    return new FlatteningPathIterator(path.getPathIterator(at), flatness);
    }

    public final boolean intersects(double x, double y, double width, double height) {
    return(path.intersects(x, y, width, height));
    }

    public final boolean intersects(Rectangle2D r) {
    return(path.intersects(r));
    }
}

att/grappa/GrappaSize.java
att/grappa/GrappaSize.java/*
 *  This software may only be used by you under license from AT&T Corp.
 *  (“AT&T”).  A copy of AT&T’s Source Code Agreement is available at
 *  AT&T’s Internet website having the URL:
 *  
 *  If you received this software without first entering into a license
 *  with AT&T, you have an infringing copy of this software and cannot use
 *  it without violating AT&T’s intellectual property rights.
 */

package att.grappa;

/**
 * This class extends java.awt.geom.Dimension2D and provides built-in
 * string-to-Dimension2D and Dimension2D-to-string conversions suitable for Grappa.
 *
 * @version 1.2, 26 May 2005; Copyright 1996 – 2005 by AT&T Corp.
 * @author  John Mocenigo, Research @ AT&T Labs
 */
public class GrappaSize extends java.awt.geom.Dimension2D
{
    /**
     * The width of the Dimension.
     */
    public double width;

    /**
     * The height of the Dimension.
     */
    public double height;

    /**
     * Constructs and initializes a GrappaSize with
     * coordinates (0, 0).
     */
    public GrappaSize() {
    }

    /**
     * Constructs and initializes a GrappaSize with the
     * specified coordinates.
     * @param width, height the coordinates to which to set the newly
     * constructed GrappaSize
     */
    public GrappaSize(double width, double height) {
    this.width = width;
    this.height = height;
    }

    /**
     * Constructs and initializes a GrappaSize with the
     * coordinates derived from the specified String representation.
     * The String format should be: “width,height
     * @param dimenString String representing the dimensions to which to
     * set the newly constructed GrappaSize
     */
    public GrappaSize(String dimenString) {
    double[] coords = null;
    try {
        coords = GrappaSupport.arrayForTuple(dimenString);
    }
    catch(NumberFormatException nfe) {
        throw new IllegalArgumentException(“coordinate string (” + dimenString + “) has a bad number format (” + nfe.getMessage() + “)”);
    }
    if(coords == null || coords.length != 2) {
        throw new IllegalArgumentException(“coordinate string (” + dimenString + “) does not contain 2 valid coordinates”);
    }
    this.width = coords[0];
    this.height = coords[1];
    }

    /**
     * Returns the width.
     */
    public double getWidth() {
    return width;
    }

    /**
     * Returns the height.
     */
    public double getHeight() {
    return height;
    }

    /**
     * Sets the width and height.
     */
    public void setSize(java.awt.geom.Dimension2D d) {
    setSize(d.getWidth(), d.getHeight());
    }

    /**
     * Sets the width and height.
     */
    public void setSize(double width, double height) {
    this.width = width;
    this.height = height;
    }

    /**
     * Provides a string representation of this object consistent 
     * with Grappa attributes.
     *
     * @return attribute-suitable string representation of this GrappaSize.
     */
    public String toAttributeString() {
    return(toFormattedString(“%p”));
    }

    /**
     * Provides a formatted string representation of this object.
     * 
     * @param format the format used to build the string (%p is the base directive suitable for a GrappaSize).
     * @return a string representation of this GrappaSize. 
     */
    public String toFormattedString(String format) {
    return(GrappaSupportPrintf.sprintf(new Object[] { format, this }));
    }

    /**
     * Provides a generic string representation of this object.
     * 
     * @return a generic string representation of this GrappaSize. 
     */
    public String toString() {
    return(width+”,”+height);
    }
}

att/grappa/GrappaStyle.java
att/grappa/GrappaStyle.java/*
 *  This software may only be used by you under license from AT&T Corp.
 *  (“AT&T”).  A copy of AT&T’s Source Code Agreement is available at
 *  AT&T’s Internet website having the URL:
 *  
 *  If you received this software without first entering into a license
 *  with AT&T, you have an infringing copy of this software and cannot use
 *  it without violating AT&T’s intellectual property rights.
 */

package att.grappa;

import java.awt.*;
import java.awt.geom.*;
import java.util.Hashtable;

/**
 * This class translates and encapsulates information provided by the style attribute.
 *
 * @version 1.2, 26 May 2005; Copyright 1996 – 2005 by AT&T Corp.
 * @author  John Mocenigo, Research @ AT&T Labs
 */
public class GrappaStyle
    implements
    GrappaConstants,
    Cloneable
{
    // element type associated with this style
    private int elementType;

    /**
     * A style attribute with this string value gets set to the default
     * style for the associated element type.
     */
    public final static String DEFAULT_SET_STRING = “__default__”;

    /**
     * Integer value for indicating solid line info.
     */
    public final static int STYLE_SOLID         = 0;
    /**
     * Integer value for indicating dashed line info.
     */
    public final static int STYLE_DASHED        = 1;
    /**
     * Integer value for indicating dotted line info.
     */
    public final static int STYLE_DOTTED        = 2;
    /**
     * Integer value for indicating specific dashed line info.
     */
    public final static int STYLE_DASH          = 3;
    /**
     * Integer value for indicating dash phase info for a dashed line.
     */
    public final static int STYLE_DASH_PHASE    = 4;
    /**
     * Integer value for indicating line width info.
     */
    public final static int STYLE_LINE_WIDTH    = 5;
    /**
     * Integer value for indicating line color info.
     */
    public final static int STYLE_LINE_COLOR    = 6;
    /**
     * Integer value for indicating fill info.
     */
    public final static int STYLE_FILLED        = 7;
    /**
     * Integer value for indicating diagonal corner info.
     */
    public final static int STYLE_DIAGONALS     = 8;
    /**
     * Integer value for indicating rounded corner info.
     */
    public final static int STYLE_ROUNDED       = 9;
    /**
     * Integer value for indicating butt cap info.
     */
    public final static int STYLE_CAP_BUTT      = 10;
    /**
     * Integer value for indicating round cap info.
     */
    public final static int STYLE_CAP_ROUND     = 11;
    /**
     * Integer value for indicating square cap info.
     */
    public final static int STYLE_CAP_SQUARE    = 12;
    /**
     * Integer value for indicating bevel join info.
     */
    public final static int STYLE_JOIN_BEVEL    = 13;
    /**
     * Integer value for indicating miter join info.
     */
    public final static int STYLE_JOIN_MITER    = 14;
    /**
     * Integer value for indicating round miter info.
     */
    public final static int STYLE_JOIN_ROUND    = 15;
    /**
     * Integer value for indicating miter limit info.
     */
    public final static int STYLE_MITER_LIMIT   = 16;
    /**
     * Integer value for indicating fixed size info.
     */
    public final static int STYLE_FIXED_SIZE    = 17;
    /**
     * Integer value for indicating fill info.
     */
    public final static int STYLE_INVIS         = 18;

    // for compatibility with dot (Grappa uses fontstyle)
    /**
     * Integer value for indicating bold font info (should use fontstyle)
     */
    public final static int STYLE_OLD_BOLD    = 100;
    /**
     * Integer value for indicating italic font info (should use fontstyle)
     */
    public final static int STYLE_OLD_ITALIC    = 101;
    /**
     * Integer value for indicating plain font info (should use fontstyle)
     */
    public final static int STYLE_OLD_PLAIN    = 102;

    /**
     * Line color default.
     */
    public final static Color STYLE_LINE_COLOR_DEFAULT = GrappaColor.getColor(“black”,null);
    /**
     * Line style default.
     */
    public final static int STYLE_LINE_STYLE_DEFAULT = STYLE_SOLID;
    /**
     * Line width default.
     */
    public final static float STYLE_LINE_WIDTH_DEFAULT = 1;
    /**
     * Line cap default.
     */
    public final static int STYLE_CAP_DEFAULT = BasicStroke.CAP_BUTT;
    /**
     * Line join default.
     */
    public final static int STYLE_JOIN_DEFAULT = BasicStroke.JOIN_BEVEL;
    /**
     * Line miter default.
     */
    public final static float STYLE_MITER_LIMIT_DEFAULT = 0;
    /**
     * Line dash default.
     */
    public final static float[] STYLE_DASH_DEFAULT = null;
    /**
     * Line dash phase default.
     */
    public final static float STYLE_DASH_PHASE_DEFAULT = 0;
    /**
     * Rounded corner default.
     */
    public final static boolean STYLE_ROUNDED_DEFAULT = false;
    /**
     * Diagonal corner default.
     */
    public final static boolean STYLE_DIAGONALS_DEFAULT = false;
    /**
     * Fill default.
     */
    public final static boolean STYLE_FILLED_DEFAULT = false;
    /**
     * Invisibility default.
     */
    public final static boolean STYLE_INVIS_DEFAULT = false;
    /**
     * Fixed size default.
     */
    public final static boolean STYLE_FIXED_SIZE_DEFAULT = false;

    static BasicStroke defaultStroke = new BasicStroke(STYLE_LINE_WIDTH_DEFAULT,STYLE_CAP_DEFAULT,STYLE_JOIN_DEFAULT,STYLE_MITER_LIMIT_DEFAULT,STYLE_DASH_DEFAULT,STYLE_DASH_PHASE_DEFAULT);

    static String defaultStrokeString = generateStrokeString(STYLE_LINE_WIDTH_DEFAULT,STYLE_CAP_DEFAULT,STYLE_JOIN_DEFAULT,STYLE_MITER_LIMIT_DEFAULT,STYLE_DASH_DEFAULT,STYLE_DASH_PHASE_DEFAULT);

    private static Hashtable styleTypes = new Hashtable(20);

    private static Hashtable strokeCache = new Hashtable(4);

    static {
    styleTypes.put(“solid”,new Integer(STYLE_SOLID));
    styleTypes.put(“dashed”,new Integer(STYLE_DASHED));
    styleTypes.put(“dotted”,new Integer(STYLE_DOTTED));
    styleTypes.put(“dash”,new Integer(STYLE_DASH));
    styleTypes.put(“dashphase”,new Integer(STYLE_DASH_PHASE));
    styleTypes.put(“dash_phase”,new Integer(STYLE_DASH_PHASE));
    styleTypes.put(“width”,new Integer(STYLE_LINE_WIDTH));
    styleTypes.put(“linewidth”,new Integer(STYLE_LINE_WIDTH));
    styleTypes.put(“line_width”,new Integer(STYLE_LINE_WIDTH));
    styleTypes.put(“setlinewidth”,new Integer(STYLE_LINE_WIDTH));
    styleTypes.put(“color”,new Integer(STYLE_LINE_COLOR));
    styleTypes.put(“linecolor”,new Integer(STYLE_LINE_COLOR));
    styleTypes.put(“line_color”,new Integer(STYLE_LINE_COLOR));
    styleTypes.put(“filled”,new Integer(STYLE_FILLED));
    styleTypes.put(“invis”,new Integer(STYLE_INVIS));
    styleTypes.put(“diagonals”,new Integer(STYLE_DIAGONALS));
    styleTypes.put(“rounded”,new Integer(STYLE_ROUNDED));
    styleTypes.put(“capbutt”,new Integer(STYLE_CAP_BUTT));
    styleTypes.put(“cap_butt”,new Integer(STYLE_CAP_BUTT));
    styleTypes.put(“capround”,new Integer(STYLE_CAP_ROUND));
    styleTypes.put(“cap_round”,new Integer(STYLE_CAP_ROUND));
    styleTypes.put(“capsquare”,new Integer(STYLE_CAP_SQUARE));
    styleTypes.put(“cap_square”,new Integer(STYLE_CAP_SQUARE));
    styleTypes.put(“joinbevel”,new Integer(STYLE_JOIN_BEVEL));
    styleTypes.put(“join_bevel”,new Integer(STYLE_JOIN_BEVEL));
    styleTypes.put(“joinmiter”,new Integer(STYLE_JOIN_MITER));
    styleTypes.put(“join_miter”,new Integer(STYLE_JOIN_MITER));
    styleTypes.put(“joinround”,new Integer(STYLE_JOIN_ROUND));
    styleTypes.put(“join_round”,new Integer(STYLE_JOIN_ROUND));
    styleTypes.put(“miterlimit”,new Integer(STYLE_MITER_LIMIT));
    styleTypes.put(“miter_limit”,new Integer(STYLE_MITER_LIMIT));
    styleTypes.put(“fixedsize”,new Integer(STYLE_FIXED_SIZE));
    styleTypes.put(“fixed_size”,new Integer(STYLE_FIXED_SIZE));

    // for compatibility with dot (these should be fontstyle for Grappa)
    styleTypes.put(“bold”,new Integer(STYLE_OLD_BOLD));
    styleTypes.put(“italic”,new Integer(STYLE_OLD_ITALIC));
    styleTypes.put(“normal”,new Integer(STYLE_OLD_PLAIN));
    styleTypes.put(“plain”,new Integer(STYLE_OLD_PLAIN));

    strokeCache.put(defaultStrokeString, defaultStroke);
    }

    Color line_color = STYLE_LINE_COLOR_DEFAULT;
    int line_style = STYLE_LINE_STYLE_DEFAULT;
    float line_width = STYLE_LINE_WIDTH_DEFAULT;
    int cap = STYLE_CAP_DEFAULT;
    int join = STYLE_JOIN_DEFAULT;
    float miter_limit = STYLE_MITER_LIMIT_DEFAULT;
    float[] dash = STYLE_DASH_DEFAULT;
    float dash_phase = STYLE_DASH_PHASE_DEFAULT;
    boolean rounded = STYLE_ROUNDED_DEFAULT;
    boolean diagonals = STYLE_DIAGONALS_DEFAULT;
    boolean filled = STYLE_FILLED_DEFAULT;
    boolean invis = STYLE_INVIS_DEFAULT;
    boolean fixed_size = STYLE_FIXED_SIZE_DEFAULT;
    Integer font_style = null;

    BasicStroke stroke = defaultStroke;

    ////////////////////////////////////////////////////////////////////////
    //
    // Constructors
    //
    ////////////////////////////////////////////////////////////////////////

    /**
     * Constructs a new GrappaStyle object from a style
     * description string.
     *
     * @param type element type to associate with this style.
     * @param style the String that specifies the style info.
     *              format is: style1,style2(extra2),…,styleN.
     */
    public GrappaStyle(int type, String style) {
    if((type&(Grappa.NODE|Grappa.EDGE|Grappa.SUBGRAPH|Grappa.SYSTEM)) != type) {
        throw new RuntimeException(“type must specify node, edge or subgraph”);
    }
    this.elementType = type;
    updateStyle(style);
    }

    ////////////////////////////////////////////////////////////////////////
    //
    // Public methods
    //
    ////////////////////////////////////////////////////////////////////////

    /**
     * Update this GrappaStyle based on the supplied style string.
     *
     * @param style a style specification
     */
    public void updateStyle(String style) {
    stroke = defaultStroke;

    line_color = STYLE_LINE_COLOR_DEFAULT;
    line_style = STYLE_LINE_STYLE_DEFAULT;
    line_width = STYLE_LINE_WIDTH_DEFAULT;
    cap = STYLE_CAP_DEFAULT;
    join = STYLE_JOIN_DEFAULT;
    miter_limit = STYLE_MITER_LIMIT_DEFAULT;
    dash = STYLE_DASH_DEFAULT;;
    dash_phase = STYLE_DASH_PHASE_DEFAULT;
    rounded = STYLE_ROUNDED_DEFAULT;
    diagonals = STYLE_DIAGONALS_DEFAULT;
    filled = STYLE_FILLED_DEFAULT;
    invis = STYLE_INVIS_DEFAULT;
    fixed_size = STYLE_FIXED_SIZE_DEFAULT;
    font_style = null;

    if(style == null) return;

    int len = style.length();
    if(len == 0 || style.equals(GrappaStyle.DEFAULT_SET_STRING)) return;

    String option_string = null;
    Object option_obj = null;
    int option = -1;
    int last_option = -1;
    boolean in_parens = false;
    boolean keyword_ok = true;
    boolean parens_ok = false;
    int i = 0, offset = 0;
    char c;
    while(i 0)) {
            if(c == ‘(‘) pcnt++;
            else if(c == ‘)’) pcnt–;
            i++;
            }
        } else {
            while(i < len && (c = style.charAt(i)) != ',' && c != ' ' && c != '(' && c != ')') i++;         }         option_string = style.substring(offset,i);         if(in_parens) {             if(last_option == -1) {             throw new RuntimeException("style attribute modifier (" + option_string + ") is unexpected");             }             switch(last_option) {             case STYLE_DASH:             line_style = last_option;             try {                 dash = GrappaSupport.floatArrayForTuple(option_string);             } catch(NumberFormatException nfe) {                 throw new RuntimeException("dash style attribute modifier (" + option_string + ") is not a comma-delimited floating point tuple");             }             break;             case STYLE_DASH_PHASE:             try {                 dash_phase = Float.valueOf(option_string).floatValue();             } catch(NumberFormatException nfe) {                 throw new RuntimeException("dash phase style attribute modifier (" + option_string + ") is not a floating point number");             }             break;             case STYLE_LINE_WIDTH:             try {                 line_width = Float.valueOf(option_string).floatValue();             } catch(NumberFormatException nfe) {                 throw new RuntimeException("line width style attribute modifier (" + option_string + ") is not a floating point number");             }             break;             case STYLE_MITER_LIMIT:             try {                 miter_limit = Float.valueOf(option_string).floatValue();             } catch(NumberFormatException nfe) {                 throw new RuntimeException("miter limit style attribute modifier (" + option_string + ") is not a floating point number");             }             break;             case STYLE_LINE_COLOR:             line_color = GrappaColor.getColor(option_string,null);             break;             case STYLE_FILLED:             filled = Boolean.valueOf(option_string).booleanValue();             break;             case STYLE_INVIS:             invis = Boolean.valueOf(option_string).booleanValue();             break;             case STYLE_DIAGONALS:             diagonals = Boolean.valueOf(option_string).booleanValue();             break;             case STYLE_ROUNDED:             rounded = Boolean.valueOf(option_string).booleanValue();             break;             case STYLE_FIXED_SIZE:             fixed_size = Boolean.valueOf(option_string).booleanValue();             break;             case STYLE_OLD_BOLD:             case STYLE_OLD_ITALIC:             case STYLE_OLD_PLAIN:             case STYLE_SOLID:             case STYLE_DASHED:             case STYLE_DOTTED:             case STYLE_CAP_BUTT:             case STYLE_CAP_ROUND:             case STYLE_CAP_SQUARE:             case STYLE_JOIN_BEVEL:             case STYLE_JOIN_MITER:             case STYLE_JOIN_ROUND:             default:             throw new RuntimeException("style attribute (" + option_string + ") has bad format");             }             last_option = -1;         } else {             option_obj = styleTypes.get(option_string.toLowerCase());             if(option_obj == null || !(option_obj instanceof Integer) ) {             throw new RuntimeException("style attribute (" + option_string + ") is unrecognized or badly implemented");             }             option = ((Integer)option_obj).intValue();             last_option = -1;             switch(option) {             case STYLE_SOLID:             line_style = option;             dash = null;             dash_phase = 0;             break;             case STYLE_DASHED:             line_style = option;             dash = new float[] { 12, 12 };             dash_phase = 0;             break;             case STYLE_DOTTED:             line_style = option;             dash = new float[] { 2, 2 };             dash_phase = 0;             break;             case STYLE_FILLED:             last_option = option;             filled = true;             break;             case STYLE_INVIS:             last_option = option;             invis = true;             break;             case STYLE_DIAGONALS:             last_option = option;             diagonals = true;             break;             case STYLE_ROUNDED:             last_option = option;             rounded = true;             break;             case STYLE_CAP_BUTT:             cap = BasicStroke.CAP_BUTT;             break;             case STYLE_CAP_ROUND:             cap = BasicStroke.CAP_ROUND;             break;             case STYLE_CAP_SQUARE:             cap = BasicStroke.CAP_SQUARE;             break;             case STYLE_JOIN_BEVEL:             cap = BasicStroke.JOIN_BEVEL;             break;             case STYLE_JOIN_MITER:             cap = BasicStroke.JOIN_MITER;             break;             case STYLE_JOIN_ROUND:             cap = BasicStroke.JOIN_ROUND;             break;             case STYLE_FIXED_SIZE:             last_option = option;             fixed_size = true;             break;             case STYLE_OLD_BOLD:             font_style = new Integer(Font.BOLD);             break;             case STYLE_OLD_ITALIC:             font_style = new Integer(Font.ITALIC);             break;             case STYLE_OLD_PLAIN:             font_style = new Integer(Font.PLAIN);             break;             case STYLE_DASH:             case STYLE_DASH_PHASE:             case STYLE_LINE_WIDTH:             case STYLE_LINE_COLOR:             case STYLE_MITER_LIMIT:             last_option = option;             break;             default:             throw new RuntimeException("style attribute (" + option_string + ") has bad format");             }             if(last_option != -1) parens_ok = true;         }         }     }     if(in_parens) {         throw new RuntimeException("style attribute has unmatched left parenthesis");     }     String strokeString = generateStrokeString(line_width,cap,join,miter_limit,dash,dash_phase);     if((stroke = (BasicStroke)strokeCache.get(strokeString)) == null) {         stroke = new BasicStroke(line_width,cap,join,miter_limit,dash,dash_phase);         strokeCache.put(strokeString,stroke);     }     }     private static String generateStrokeString(                            float lineWidth,                            int capType,                            int joinType,                            float miterLimit,                            float[] dashSpec,                            float dashPhase                            ) {     StringBuffer strokeStringBuffer = new StringBuffer();     strokeStringBuffer.append(lineWidth);     strokeStringBuffer.append(",");     strokeStringBuffer.append(capType);     strokeStringBuffer.append(",");     strokeStringBuffer.append(joinType);     strokeStringBuffer.append(",");     strokeStringBuffer.append(miterLimit);     strokeStringBuffer.append(",");     if(dashSpec == null) {         strokeStringBuffer.append("null");     } else {         strokeStringBuffer.append("{");         strokeStringBuffer.append(dashSpec[0]);         for(int i = 1; i < dashSpec.length; i++) {         strokeStringBuffer.append(",");         strokeStringBuffer.append(dashSpec[i]);         }         strokeStringBuffer.append("}");     }     strokeStringBuffer.append(",");     strokeStringBuffer.append(dashPhase);     return(strokeStringBuffer.toString());     }     /**      * Provides a string representation of this object consistent       * with Grappa attributes.      *      * @return attribute-suitable string representation of this GrappaStyle.      */     public String toAttributeString() {     return(generateStyleString(line_color,line_style,line_width,cap,join,miter_limit,dash,dash_phase,rounded,diagonals,filled,invis,fixed_size,font_style,false,elementType));     }     /**      * Provides a generic string representation of this object.      *       * @return a generic string representation of this GrappaStyle.       */     public String toString() {     return(generateStyleString(line_color,line_style,line_width,cap,join,miter_limit,dash,dash_phase,rounded,diagonals,filled,invis,fixed_size,font_style,true,elementType));     }     private static String generateStyleString(                           Color color,                           int lineStyle,                           float lineWidth,                           int capType,                           int joinType,                           float miterLimit,                           float[] dashSpec,                           float dashPhase,                           boolean roundedFlag,                           boolean diagonalsFlag,                           boolean filledFlag,                           boolean invisFlag,                           boolean fixedSizeFlag,                           Integer fontStyle,                           boolean showAll,                           int type                           ) {     StringBuffer styleStringBuffer = null;     String tmpstr = null;     Object[] args = { "%g", null };     if(        showAll        ||        (         color != STYLE_LINE_COLOR_DEFAULT         &&         STYLE_LINE_COLOR_DEFAULT != null         &&         !STYLE_LINE_COLOR_DEFAULT.equals(color)         )        ) {         if(styleStringBuffer == null) {         styleStringBuffer = new StringBuffer();         } else {         styleStringBuffer.append(',');         }         styleStringBuffer.append("lineColor(");         if((tmpstr = GrappaColor.getColorName(color)) == null) {         float[] hsb = Color.RGBtoHSB(color.getRed(),color.getGreen(),color.getBlue(),null);         //styleStringBuffer.append(hsb[0]);         //styleStringBuffer.append(',');         //styleStringBuffer.append(hsb[1]);         //styleStringBuffer.append(',');         //styleStringBuffer.append(hsb[2]);         styleStringBuffer.append(GrappaSupportPrintf.sprintf(new Object[] { "%g,%g,%g", new Float(hsb[0]), new Float(hsb[1]), new Float(hsb[2]) }));         } else {         styleStringBuffer.append(tmpstr);         }         styleStringBuffer.append(')');     }     if(        showAll        ||        lineStyle != STYLE_LINE_STYLE_DEFAULT        ) {         if(styleStringBuffer == null) {         styleStringBuffer = new StringBuffer();         } else {         styleStringBuffer.append(',');         }         switch(lineStyle) {         case STYLE_SOLID:         styleStringBuffer.append("solid");         break;         case STYLE_DASHED:         styleStringBuffer.append("dashed");         break;         case STYLE_DOTTED:         styleStringBuffer.append("dotted");         break;         case STYLE_DASH:         if(dashSpec == null) {             styleStringBuffer.append("solid");         } else {             styleStringBuffer.append("dash(");             //styleStringBuffer.append(dashSpec[0]);             args[1] = new Float(dashSpec[0]);             styleStringBuffer.append(GrappaSupportPrintf.sprintf(args));             for(int i = 1; i < dashSpec.length; i++) {             styleStringBuffer.append(',');             //styleStringBuffer.append(dashSpec[i]);             args[1] = new Float(dashSpec[i]);             styleStringBuffer.append(GrappaSupportPrintf.sprintf(args));             }             styleStringBuffer.append(')');         }         break;         default:         throw new InternalError("unexpected lineStyle (" + lineStyle + ")");         }     }     if(        showAll        ||        lineWidth != STYLE_LINE_WIDTH_DEFAULT        ) {         if(styleStringBuffer == null) {         styleStringBuffer = new StringBuffer();         } else {         styleStringBuffer.append(',');         }         styleStringBuffer.append("lineWidth(");         //styleStringBuffer.append(lineWidth);         args[1] = new Float(lineWidth);         styleStringBuffer.append(GrappaSupportPrintf.sprintf(args));         styleStringBuffer.append(')');     }     if(        showAll        ||        capType != STYLE_CAP_DEFAULT        ) {         if(styleStringBuffer == null) {         styleStringBuffer = new StringBuffer();         } else {         styleStringBuffer.append(',');         }         switch(capType) {         case BasicStroke.CAP_BUTT:         styleStringBuffer.append("capButt");         break;         case BasicStroke.CAP_ROUND:         styleStringBuffer.append("capRound");         break;         case BasicStroke.CAP_SQUARE:         styleStringBuffer.append("capSquare");         break;         default:         throw new InternalError("unexpected cap type (" + capType + ")");         }     }     if(        showAll        ||        joinType != STYLE_JOIN_DEFAULT        ) {         if(styleStringBuffer == null) {         styleStringBuffer = new StringBuffer();         } else {         styleStringBuffer.append(',');         }         switch(joinType) {         case BasicStroke.JOIN_BEVEL:         styleStringBuffer.append("joinBevel");         break;         case BasicStroke.JOIN_MITER:         styleStringBuffer.append("joinMiter");         break;         case BasicStroke.JOIN_ROUND:         styleStringBuffer.append("joinRound");         break;         default:         throw new InternalError("unexpected join type (" + joinType + ")");         }     }     if(        showAll        ||        miterLimit != STYLE_MITER_LIMIT_DEFAULT        ) {         if(styleStringBuffer == null) {         styleStringBuffer = new StringBuffer();         } else {         styleStringBuffer.append(',');         }         styleStringBuffer.append("miterLimit(");         //styleStringBuffer.append(miterLimit);         args[1] = new Float(miterLimit);         styleStringBuffer.append(GrappaSupportPrintf.sprintf(args));         styleStringBuffer.append(')');     }     if(        showAll        ||        dashPhase != STYLE_DASH_PHASE_DEFAULT        ) {         if(styleStringBuffer == null) {         styleStringBuffer = new StringBuffer();         } else {         styleStringBuffer.append(',');         }         styleStringBuffer.append("dashPhase(");         //styleStringBuffer.append(dashPhase);         args[1] = new Float(dashPhase);         styleStringBuffer.append(GrappaSupportPrintf.sprintf(args));         styleStringBuffer.append(')');     }     if(        (         type > 0
        &&
        (type&Grappa.NODE) == Grappa.NODE
        )
       &&
       (
        showAll
        ||
        roundedFlag != STYLE_ROUNDED_DEFAULT
        )
       ) {
        if(styleStringBuffer == null) {
        styleStringBuffer = new StringBuffer();
        } else {
        styleStringBuffer.append(‘,’);
        }
        if(roundedFlag) {
        styleStringBuffer.append(“rounded”);
        } else {
        styleStringBuffer.append(“rounded(false)”);
        }
    }

    if(
       (
        type > 0
        &&
        (type&Grappa.NODE) == Grappa.NODE
        )
       &&
       (
        showAll
        ||
        diagonalsFlag != STYLE_DIAGONALS_DEFAULT
        )
       ) {
        if(styleStringBuffer == null) {
        styleStringBuffer = new StringBuffer();
        } else {
        styleStringBuffer.append(‘,’);
        }
        if(diagonalsFlag) {
        styleStringBuffer.append(“diagonals”);
        } else {
        styleStringBuffer.append(“diagonals(false)”);
        }
    }

    if(
       (
        type > 0
        &&
        (type&(Grappa.NODE|Grappa.SUBGRAPH)) != 0
        )
       &&
       (
        showAll
        ||
        filledFlag != STYLE_FILLED_DEFAULT
        )
       ) {
        if(styleStringBuffer == null) {
        styleStringBuffer = new StringBuffer();
        } else {
        styleStringBuffer.append(‘,’);
        }
        if(filledFlag) {
        styleStringBuffer.append(“filled”);
        } else {
        styleStringBuffer.append(“filled(false)”);
        }
    }

    if(
       (
        type > 0
        &&
        (type&(Grappa.NODE|Grappa.EDGE|Grappa.SUBGRAPH)) != 0
        )
       &&
       (
        showAll
        ||
        invisFlag != STYLE_INVIS_DEFAULT
        )
       ) {
        if(styleStringBuffer == null) {
        styleStringBuffer = new StringBuffer();
        } else {
        styleStringBuffer.append(‘,’);
        }
        if(invisFlag) {
        styleStringBuffer.append(“invis”);
        } else {
        styleStringBuffer.append(“invis(false)”);
        }
    }

    if(
       showAll
       ||
       fixedSizeFlag != STYLE_FIXED_SIZE_DEFAULT
       ) {
        if(styleStringBuffer == null) {
        styleStringBuffer = new StringBuffer();
        } else {
        styleStringBuffer.append(‘,’);
        }
        if(fixedSizeFlag) {
        styleStringBuffer.append(“fixedSize”);
        } else {
        styleStringBuffer.append(“fixedSize(false)”);
        }
    }

    if(
       fontStyle != null
       ) {
        if(styleStringBuffer == null) {
        styleStringBuffer = new StringBuffer();
        } else {
        styleStringBuffer.append(‘,’);
        }
        if(fontStyle.intValue() == Font.BOLD) {
        styleStringBuffer.append(“bold”);
        } else if(fontStyle.intValue() == Font.ITALIC) {
        styleStringBuffer.append(“italic”);
        } else {
        styleStringBuffer.append(“plain”);
        }
    }

    if(styleStringBuffer == null) {
        tmpstr = “”; // or set it null?
    } else {
        tmpstr = styleStringBuffer.toString();
    }

    return(tmpstr);
    }

    /**
     * Get the line color.
     *
     * @return the line color.
     */
    public Color getLineColor() {
    return line_color;
    }

    /**
     * Get the line style.
     *
     * @return the line style.
     */
    public int getLineStyle() {
    return line_style;
    }

    /**
     * Get the line width.
     *
     * @return the line width.
     */
    public float getLineWidth() {
    return line_width;
    }

    /**
     * Get the cap style.
     *
     * @return the cap style.
     */
    public int getCapStyle() {
    return cap;
    }

    /**
     * Get the join style.
     *
     * @return the join style.
     */
    public int getJoinStyle() {
    return join;
    }

    /**
     * Get the miter limit.
     *
     * @return the miter limit.
     */
    public float getMiterLimit() {
    return miter_limit;
    }

    /**
     * Get the dash specification.
     *
     * @return the dash specification.
     */
    public float[] getDash() {
    if(dash == null) {
        return(null);
    }
    return((float[])(dash.clone()));
    }

    /**
     * Get the dash phase.
     *
     * @return the dash phase.
     */
    public float getDashPhase() {
    return dash_phase;
    }

    /**
     * Get the rounded corner specification.
     *
     * @return the rounded corner specification color (true indicates rounded corners).
     */
    public boolean getRounded() {
    return rounded;
    }

    /**
     * Get the diagonal corner specification.
     *
     * @return the diagonal corner specification color (true indicates diagonal corners).
     */
    public boolean getDiagonals() {
    return diagonals;
    }

    /**
     * Get the fill specification.
     *
     * @return the fill specification (true indicates filling should occur).
     */
    public boolean getFilled() {
    return filled;
    }

    /**
     * Get the invisibility specification.
     *
     * @return the invisibility specification (true indicates element is invisible).
     */
    public boolean getInvis() {
    return invis;
    }

    /**
     * Get the fixed size specification.
     *
     * @return the fixed size specification (true indicates that fixed size drawing is requested).
     */
    public boolean getFixedSize() {
    return fixed_size;
    }

    /**
     * Get the font style.
     * Actually, the fontstyle attribute should be used for
     * font style dealings rather than the style attribute, but
     * the latter is permitted for backward compatibility with dot.
     *
     * @return the font style.
     */
    public int getFontStyle() {
    if(font_style == null) return(Font.PLAIN);
    return font_style.intValue();
    }

    ////////////////////////////////////////////////////////////////////////
    //
    // Cloneable interface
    //
    ////////////////////////////////////////////////////////////////////////
 
    /**
     * Creates a new object of the same class as this object.
     *
     * @return     a clone of this instance.
     * @exception  OutOfMemoryError            if there is not enough memory.
     * @see        java.lang.Cloneable
     */
    public Object clone() {
    try {
        GrappaStyle copy = (GrappaStyle) super.clone();
        copy.dash = getDash();
        return copy;
    } catch (CloneNotSupportedException e) {
        // this shouldn’t happen, since we are Cloneable
        throw new InternalError();
    }
    }
}

att/grappa/GrappaSupport.java
att/grappa/GrappaSupport.java/*
 *  This software may only be used by you under license from AT&T Corp.
 *  (“AT&T”).  A copy of AT&T’s Source Code Agreement is available at
 *  AT&T’s Internet website having the URL:
 *  
 *  If you received this software without first entering into a license
 *  with AT&T, you have an infringing copy of this software and cannot use
 *  it without violating AT&T’s intellectual property rights.
 */

package att.grappa;

import java.io.*;
import java.util.*;
import java.awt.geom.*;

/**
 * A class providing some supports function for Grappa.
 *
 * @version 1.2, 26 May 2005; Copyright 1996 – 2005 by AT&T Corp.
 * @author  John Mocenigo and Rich Drechsler, Research @ AT&T Labs
 */
public abstract
class GrappaSupport

    implements GrappaConstants

{

    ///////////////////////////////////////////////////////////////////////////
    //
    // The ctypes type stuff is courtesy of Rich Drechsler of AT&T Labs.
    //
    ///////////////////////////////////////////////////////////////////////////

    private static final short  CN = 0x01;  // control
    private static final short  WS = 0x02;  // white space
    private static final short  SP = 0x04;  // space
    private static final short  PU = 0x08;  // punctuation
    private static final short  DG = 0x10;  // digit
    private static final short  OD = 0x20;  // octal digit
    private static final short  UC = 0x40;  // upper case
    private static final short  HD = 0x80;  // hex digit
    private static final short  LC = 0x100; // lower case

    private static final short  ctype[] = {
    CN, CN, CN, CN, CN, CN, CN, CN,
    CN, CN|WS, CN|WS, CN|WS, CN|WS, CN|WS, CN, CN,
    CN, CN, CN, CN, CN, CN, CN, CN,
    CN, CN, CN, CN, CN, CN, CN, CN,
    WS|SP, PU, PU, PU, PU, PU, PU, PU,
    PU, PU, PU, PU, PU, PU, PU, PU,
    DG|OD, DG|OD, DG|OD, DG|OD, DG|OD, DG|OD, DG|OD, DG|OD,
    DG, DG, PU, PU, PU, PU, PU, PU,
    PU, UC|HD, UC|HD, UC|HD, UC|HD, UC|HD, UC|HD, UC,
    UC, UC, UC, UC, UC, UC, UC, UC,
    UC, UC, UC, UC, UC, UC, UC, UC,
    UC, UC, UC, PU, PU, PU, PU, PU,
    PU, LC|HD, LC|HD, LC|HD, LC|HD, LC|HD, LC|HD, LC,
    LC, LC, LC, LC, LC, LC, LC, LC,
    LC, LC, LC, LC, LC, LC, LC, LC,
    LC, LC, LC, PU, PU, PU, PU, CN,
    };

    private static final short  ALPHA = (UC|LC);
    private static final short  ALNUM = (UC|LC|DG);
    private static final short  GRAPH = (ALNUM|PU);
    private static final short  PRINT = (GRAPH|SP);
    private static final short  ODIGIT = (OD);
    private static final short  XDIGIT = (DG|HD);

    private static final short  LOWERTOUPPER = ‘A’ – ‘a’;
    private static final short  UPPERTOLOWER = ‘a’ – ‘A’;

    ///////////////////////////////////////////////////////////////////////////
    //
    // GrappaSupport
    //
    ///////////////////////////////////////////////////////////////////////////

    /**
     * for ASCII only
     */
    static boolean
    isalnum(int ch) {

    return(isascii(ch) && (ctype[ch]&ALNUM) != 0);
    }

    ///////////////////////////////////////////////////////////////////////////

    /**
     * for ASCII only
     */
    static boolean
    isalpha(int ch) {

    return(isascii(ch) && (ctype[ch]&ALPHA) != 0);
    }

    ///////////////////////////////////////////////////////////////////////////

    /**
     * for ASCII only
     */
    static boolean
    isascii(int ch) {

    return(ch >= 0 && ch < 128);     }     ///////////////////////////////////////////////////////////////////////////     /**      * for ASCII only      */     static boolean     iscntrl(int ch) {     return(isascii(ch) && (ctype[ch]&CN) != 0);     }     ///////////////////////////////////////////////////////////////////////////     /**      * for ASCII only      */     static boolean     isdigit(int ch) {     return(isascii(ch) && (ctype[ch]&DG) != 0);     }     ///////////////////////////////////////////////////////////////////////////     /**      * for ASCII only      */     static boolean     isgraph(int ch) {     return(isascii(ch) && (ctype[ch]&DG) != 0);     }     ///////////////////////////////////////////////////////////////////////////     /**      * for ASCII only      */     static boolean     islower(int ch) {     return(isascii(ch) && (ctype[ch]&LC) != 0);     }     ///////////////////////////////////////////////////////////////////////////     /**      * for ASCII only      */     static boolean     isoctal(int ch) {     return(isascii(ch) && (ctype[ch]&ODIGIT) != 0);     }     ///////////////////////////////////////////////////////////////////////////     /**      * for ASCII only      */     static boolean     isprint(int ch) {     return(isascii(ch) && (ctype[ch]&PRINT) != 0);     }     ///////////////////////////////////////////////////////////////////////////     /**      * for ASCII only      */     static boolean     ispunct(int ch) {     return(isascii(ch) && (ctype[ch]&PU) != 0);     }     ///////////////////////////////////////////////////////////////////////////     /**      * for ASCII only      */     static boolean     isspace(int ch) {     return(isascii(ch) && (ctype[ch]&WS) != 0);     }     ///////////////////////////////////////////////////////////////////////////     /**      * for ASCII only      */     static boolean     isupper(int ch) {     return(isascii(ch) && (ctype[ch]&UC) != 0);     }     ///////////////////////////////////////////////////////////////////////////     /**      * for ASCII only      */     static boolean     isxdigit(int ch) {     return(isascii(ch) && (ctype[ch]&XDIGIT) != 0);     }     ///////////////////////////////////////////////////////////////////////////     /**      * for ASCII only      */     static int     tolower(int ch) {     return(isupper(ch) ? ch + UPPERTOLOWER : ch);     }     ///////////////////////////////////////////////////////////////////////////     /**      * for ASCII only      */     static int     toupper(int ch) {     return(islower(ch) ? ch + LOWERTOUPPER : ch);     }     ///////////////////////////////////////////////////////////////////////////     static String[]     strsplit(String tuple) throws IllegalArgumentException {     if(tuple == null) throw new IllegalArgumentException("supplied split string is null");     StringTokenizer st = new StringTokenizer(tuple,",",false);     String[] array = new String[st.countTokens()];     int i = 0;     while(st.hasMoreTokens()) {         array[i++] = st.nextToken();     }     return array;     }     ///////////////////////////////////////////////////////////////////////////     static float[]     floatArrayForTuple(String tuple) throws IllegalArgumentException, NumberFormatException {     if(tuple == null) throw new IllegalArgumentException("supplied tuple string is null");     StringTokenizer st = new StringTokenizer(tuple,", \t",false);     float[] array = new float[st.countTokens()];     int i = 0;     while(st.hasMoreTokens()) {         array[i++] = Float.valueOf(st.nextToken()).floatValue();     }     return array;     }     ///////////////////////////////////////////////////////////////////////////     static double[]     arrayForTuple(String tuple) throws IllegalArgumentException, NumberFormatException {     if(tuple == null) throw new IllegalArgumentException("supplied tuple string is null");     StringTokenizer st = new StringTokenizer(tuple,", \t",false);     double[] array = new double[st.countTokens()];     int i = 0;     while(st.hasMoreTokens()) {         array[i++] = Double.valueOf(st.nextToken()).doubleValue();     }     return array;     }     ///////////////////////////////////////////////////////////////////////////     /**      * Converts a string to an integer edge direction.      * The string is first canonicalized (converted to lower case and      * non-alphanumerics are removed) then compared to none, back, forward      * or both.  A match returns GrappaLine.NONE_ARROW_EDGE,      * GrappaLine.HEAD_ARROW_EDGE, GrappaLine.TAIL_ARROW_EDGE or       * GrappaLine.BOTH_ARROW_EDGE, respectively. When there is no match,      * GrappaLine.NONE_ARROW_EDGE is returned.      *      * @param direction a string representing an edge direction      *      * @return an integer representation of the supplied edge direction      */     public static int xlateDirString(String direction) {     if(direction != null) {         String dir = canonize(direction);         if(dir.equals("forward")) {         return GrappaLine.TAIL_ARROW_EDGE;         } else if(dir.equals("back")) {         return GrappaLine.HEAD_ARROW_EDGE;         } else if(dir.equals("both")) {         return GrappaLine.BOTH_ARROW_EDGE;         }     }     return GrappaLine.NONE_ARROW_EDGE;     }     /**      * Converts an integer edge direction value to a string representation.      * Only GrappaLine.NONE_ARROW_EDGE, GrappaLine.HEAD_ARROW_EDGE,      * GrappaLine.TAIL_ARROW_EDGE and GrappaLine.BOTH_ARROW_EDGE are      * understood, all others are taken to mean GrappaLine.NONE_ARROW_EDGE.      *      * @param direction an integer representing an edge direction      *      * @return a string representation of the supplied edge direction      */     public static String xlateDir(int direction) {     String retstr = null;     if(direction == GrappaLine.TAIL_ARROW_EDGE) {         retstr = "forward";     } else if(direction == GrappaLine.HEAD_ARROW_EDGE) {         retstr = "back";     } else if(direction == GrappaLine.BOTH_ARROW_EDGE) {         retstr = "both";     } else {         retstr = "none";     }     return retstr;     }     /**      * Converts a string to an integer font style.      * The string is first canonicalized (converted to lower case and      * non-alphanumerics are removed) then compared to italic, bold or      * bolditalic.  A match returns Font.ITALIC, Font.BOLD, or a bitwise      * OR-ing of the two, respectively. When there is no match, Font.PLAIN      * is returned.      *      * @param fontstyle a string representing a font style      *      * @return an integer representation of the supplied font style string      */     public static int xlateFontStyleString(String fontstyle) {     if(fontstyle != null) {         String style = canonize(fontstyle);         if(style.equals("italic")) {         return java.awt.Font.ITALIC;         } else if(style.equals("bold")) {         return java.awt.Font.BOLD;         } else if(style.equals("bolditalic")) {         return (java.awt.Font.BOLD|java.awt.Font.ITALIC);         }     }     return java.awt.Font.PLAIN;     }     /**      * Converts an integer font style value to a string representation.      * Only Font.ITALIC, Font.BOLD, and (Font.BOLD|Font.ITALIC) are      * understood, all others are taken to mean Font.PLAIN.      *      * @param fontstyle an integer representing a font style      *      * @return a string representation of the supplied font style value      */     public static String xlateFontStyle(int fontstyle) {     String retstr = null;     if(fontstyle == java.awt.Font.ITALIC) {         retstr = "italic";     } else if(fontstyle == java.awt.Font.BOLD) {         retstr = "bold";     } else if(fontstyle == (java.awt.Font.BOLD|java.awt.Font.ITALIC)) {         retstr = "bolditalic";     } else {         retstr = "normal";     }     return retstr;     }     /**      * Canonize string by converting to lower-case and removing all      * non-letter, non-digit characters.      *      * @param input the string to be canonized      *      * @return the canonized string      */     public static String canonize(String input) {     if(input == null) return null;     char[] array = input.toCharArray();     int pos = 0;     for(int i = 0; i < array.length; i++) {         if(Character.isLetterOrDigit(array[i])) {         array[pos++] = Character.toLowerCase(array[i]);         }     }     if(pos == 0) return("");     return new String(array,0,pos);     }     /**      * Creates a GrappaBox from the coordinates of any two opposing corners.      *      * @param x1 x-coordinate of corner number 1.      * @param y1 x-coordinate of corner number 1.      * @param x2 x-coordinate of corner number 2, which is opposite corner 1.      * @param y2 x-coordinate of corner number 2, which is opposite corner 1.      * @return a GrappaBox generated the possibly-reordered coordinates.      */     public static GrappaBox boxFromCorners(double x1, double y1, double x2, double y2) {     return(boxFromCorners(null, x1, y1, x2, y2));     }     /**      * Creates a GrappaBox from the coordinates of any two opposing corners.      *      * @param box if non-null, the coordinates of this box are changed and this box is returned, otherwise a new box is created.      * @param x1 x-coordinate of corner number 1.      * @param y1 x-coordinate of corner number 1.      * @param x2 x-coordinate of corner number 2, which is opposite corner 1.      * @param y2 x-coordinate of corner number 2, which is opposite corner 1.      * @return a GrappaBox generated the possibly-reordered coordinates.      */     public static GrappaBox boxFromCorners(GrappaBox box, double x1, double y1, double x2, double y2) {     if(box == null) {         box = new GrappaBox();     }     box.x = x1 < x2 ? x1 : x2;     box.y = y1 < y2 ? y1 : y2;     box.width = x1 < x2 ? x2 - x1 : x1 - x2;     box.height = y1 < y2 ? y2 - y1 : y1 - y2;     return(box);     }     /**      * Find an element in the supplied subgraph that contains the given point.      * The last element encoutered is returned from a search of first      * subgraphs, then edges, then nodes. The ordering within a set of elements      * (e.g., nodes) is indeterminate.      *      * @param subg the subgraph to be searched.      * @param pt the point of the search.      * @return an element containing the point, or null.      */     public static Element findContainingElement(Subgraph subg, Point2D pt) {     Element elem = null;     Rectangle2D bb = subg.getBoundingBox();     GrappaNexus grappaNexus = null;     if(bb.contains(pt)) {         elem = subg;         Enumeration enm;         Element subelem = null;         enm = subg.subgraphElements();         while(enm.hasMoreElements()) {         if((subelem = findContainingElement((Subgraph)(enm.nextElement()), pt)) != null) {             elem = subelem;         }         }         enm = subg.edgeElements();         Edge edge;         while(enm.hasMoreElements()) {         edge = (Edge)enm.nextElement();         if((grappaNexus = edge.grappaNexus) == null) continue;         if(grappaNexus.rawBounds2D().contains(pt)) {             if(grappaNexus.contains(pt)) {             elem = edge;             }         }         }         enm = subg.nodeElements();         Node node;         while(enm.hasMoreElements()) {         node = (Node)enm.nextElement();         if((grappaNexus = node.grappaNexus) == null) continue;         if(grappaNexus.rawBounds2D().contains(pt)) {             if(grappaNexus.contains(pt)) {             elem = node;             }         }         }     }     return(elem);     }     /**      * Find the elements in the supplied subgraph that are contained in      * the given box.      *      * @param subg the subgraph to be searched.      * @param pt the container box.      * @return a vector whose components may be single elements or another      *         vector of this same type with the property that all the elements      *         in the vector (when eventually unravelled) are contained in      *         the supplied box.      */     public static Vector findContainedElements(Subgraph subg, GrappaBox box) {     Vector elems = null;     Rectangle2D bb = subg.getBoundingBox();     GrappaNexus grappaNexus = null;     if(box.contains(bb)) {         return subg.vectorOfElements(Grappa.SUBGRAPH|Grappa.NODE|Grappa.EDGE);     } else if(box.intersects(bb)) {         Enumeration enm;         Vector subelems = null;         enm = subg.subgraphElements();         while(enm.hasMoreElements()) {         if((subelems = findContainedElements((Subgraph)(enm.nextElement()), box)) != null) {             if(elems == null) {             elems = new Vector();             }             elems.add(subelems);         }         }         enm = subg.edgeElements();         Edge edge;         while(enm.hasMoreElements()) {         edge = (Edge)enm.nextElement();         if((grappaNexus = edge.grappaNexus) == null) continue;         if(box.contains(grappaNexus.rawBounds2D())) {             if(elems == null) {             elems = new Vector();             }             elems.add(edge);         }         }         enm = subg.nodeElements();         Node node;         while(enm.hasMoreElements()) {         node = (Node)enm.nextElement();         if((grappaNexus = node.grappaNexus) == null) continue;         if(box.contains(grappaNexus.rawBounds2D())) {             if(elems == null) {             elems = new Vector();             }             elems.add(node);         }         }     }     return(elems);     }     /**      * Set the highlight on an element and, possibly, related elements.      * Since deletion can affect related elements (i.e., the edges connected      * to a node or the sub-elements of a subgraph), those elements are      * affected as well when highlighting.      *      * @param elem the element whose highlighting is to be adjusted.      * @param mode the highlight mode to apply or remove; a mode of      *             zero indicates all highlighting is turned off regardless      *             of the setting.      * @param setting one of HIGHLIGHT_ON, HIGHLIGHT_OFF or HIGHLIGHT_TOGGLE.      */     public static void setHighlight(Element elem, int mode, int setting) {     if(elem == null) return;     if(mode == 0) {         // treat delete specially         boolean wasDelete = ((elem.highlight&DELETION_MASK) == DELETION_MASK);         elem.highlight = 0;         if(wasDelete) {         if(elem.isNode()) {             Enumeration enm = ((Node)elem).edgeElements();             while(enm.hasMoreElements()) {             ((Element)(enm.nextElement())).highlight = 0;             }         } else if(elem.isSubgraph()) {             Enumeration enm = ((Subgraph)elem).elements();             while(enm.hasMoreElements()) {             ((Element)(enm.nextElement())).highlight = 0;             }         }         }     } else {         mode &= HIGHLIGHT_MASK;         if(setting == HIGHLIGHT_TOGGLE) {         elem.highlight ^= mode;         } else if(setting == HIGHLIGHT_ON) {         elem.highlight |= mode;         } else {         elem.highlight &= ~mode;         }         if((mode&DELETION_MASK) == DELETION_MASK) {         if(elem.isNode()) {             if((elem.highlight&DELETION_MASK) == DELETION_MASK) {             Enumeration enm = ((Node)elem).edgeElements();             while(enm.hasMoreElements()) {                 ((Element)(enm.nextElement())).highlight |= DELETION_MASK;             }             } else {             Enumeration enm = ((Node)elem).edgeElements();             while(enm.hasMoreElements()) {                 ((Element)(enm.nextElement())).highlight &= ~DELETION_MASK;             }             }         } else if(elem.isSubgraph()) {             if((elem.highlight&DELETION_MASK) == DELETION_MASK) {             Enumeration enm = ((Subgraph)elem).elements();             while(enm.hasMoreElements()) {                 ((Element)(enm.nextElement())).highlight |= DELETION_MASK;             }             } else {             Enumeration enm = ((Subgraph)elem).elements();             while(enm.hasMoreElements()) {                 ((Element)(enm.nextElement())).highlight &= ~DELETION_MASK;             }             }         }         }     }     }     /**      * Filter the supplied graph using the given connector.      * The connector is either a java.lang.Process or a      * java.net.URLConnection.      * As such, it provides an output stream to which the graph can be      * written and an input stream from which the processed graph can be      * read back in (to replace the original graph).      * Such filtering is useful for processing the graph through a layout      * engine such as the dot program.
     *
     * 

Unlike previous versions of Grappa, this version does not try
     * to explicitly redraw the graph after filtering is completed.
     *
     * @param graph the graph to be processed and reset
     * @param connector a Process or URLConnector that provides an input and
     *                  output stream
     * @return true if the filtering completed successfully, false otherwise.
     */
    public static boolean filterGraph(Graph graph, Object connector) {
    return filterGraph(graph,connector,null);
    }

    /**
     * Filter the supplied graph using the given connector.
     * The connector is either a java.lang.Process or a
     * java.net.URLConnection.
     * As such, it provides an output stream to which the graph can be
     * written and an input stream from which the processed graph can be
     * read back in (to replace the original graph).
     * Such filtering is useful for processing the graph through a layout
     * engine such as the dot program. The existing graph is reset
     * and its contents are replaced with the graph that is read in.
     *
     * 

Unlike previous versions of Grappa, this version does not try
     * to explicitly redraw the graph after filtering is completed.
     *
     * @param graph the graph to be processed and reset
     * @param connector a Process or URLConnector that provides an input and
     *                  output stream
     * @param preamble if not null, a string sent to filter prior to graph
     * @return true if the filtering completed successfully, false otherwise.
     */
    public static boolean filterGraph(Graph graph, Object connector, String preamble) {
    if(connector == null) return false;
    OutputStream toFilterRaw = null;
    try {
        if(connector instanceof java.lang.Process) {
        toFilterRaw = ((java.lang.Process)connector).getOutputStream();
        } else if(connector instanceof java.net.URLConnection) {
        toFilterRaw = ((java.net.URLConnection)connector).getOutputStream();
        } else {
        return false;
        }
    } catch(IOException ioex) {
        Grappa.displayException(ioex);
        return false;
    }
    BufferedWriter toFilter = new BufferedWriter(new OutputStreamWriter(toFilterRaw));
    String content = null;
    boolean status = true;
    graph.filterMode = true;
    try {
        StringWriter theGraph = new StringWriter();
        graph.printGraph(theGraph);
        theGraph.flush();
        content = theGraph.toString();
        theGraph.close();
    } catch(Exception ex) {
        Grappa.displayException(ex);
        return false;
    }
    finally {
        graph.filterMode = false;
    }
    try {
        if(preamble != null) {
        toFilter.write(preamble,0,preamble.length());
        toFilter.flush();
        }
        toFilter.write(content,0,content.length());
        toFilter.flush();
        toFilter.close();
    } catch(Exception ex) {
        Grappa.displayException(ex);
        return false;
    }
    InputStream fromFilterRaw = null;
    try {
        if(connector instanceof java.lang.Process) {
        fromFilterRaw = ((java.lang.Process)connector).getInputStream();
        } else if(connector instanceof java.net.URLConnection) {
        fromFilterRaw = ((java.net.URLConnection)connector).getInputStream();
        } else {
        return false;
        }
    } catch(IOException ioex) {
        Grappa.displayException(ioex);
        return false;
    }
    BufferedReader fromFilter = new BufferedReader(new InputStreamReader(fromFilterRaw));
    StringBuffer newGraph = new StringBuffer(content.length() + 128);
    try {
        String line = null;
        while((line = fromFilter.readLine()) !=  null) {
        newGraph.append(line);
        // assume a lone right-brace on a line is the end-of-graph

        if(line.equals(“}”) || line.equals(“}\r”)) {
            break;
        }
        /*
         * Need to append new-line on the chance that there was a
         * backslash-newline (otherwise need to test for a lone
         * backslash at the end of the string and remove it…
         * cheaper to just append a newline.
         */
        newGraph.append(Grappa.NEW_LINE);
        }
    } catch(Exception ex) {
        Grappa.displayException(ex);
        status = false;
        if(newGraph.length() == 0) {
        newGraph.append(content);
        content = null;
        }
    }
    try {
        fromFilter.close();
    } catch(IOException io) {}
    Reader fromReader = null;
    try {
        fromReader = new StringReader(newGraph.toString());
    } catch(Exception ex) {
        Grappa.displayException(ex);
        return false;
    }
    graph.reset();
    Parser program = new Parser(fromReader,graph.getErrorWriter(),graph);
    try {
        program.parse();
    } catch(Exception ex) {
        Grappa.displayException(ex);
        status = false;
        try {
        fromReader.close();
        fromReader = new StringReader(content);
        } catch(Exception ex2) {
        Grappa.displayException(ex2);
        return false;
        }
        program = new Parser(fromReader,graph.getErrorWriter(),graph);
        try {
        program.parse();
        } catch(Exception ex2) {
        Grappa.displayException(ex2);
        return false;
        }
    }
    return status;
    }

    /**
     * Scroll to the viewport containing the specified GrappaPanel so that
     * it is centered on the given point. If the point is not contained in
     * the subgraph being displayed in the GrappaPanel, no action is taken.
     * If the getParent() method applied to the GrappaPanel argument does not
     * return a JViewport, an error message is displayed.
     *
     * @param cpt the point to place at the center of the GrappaPanel viewport
     * @param gpanel the GrappaPanel displaying the graph
     *
     * @return true for a valid request, false otherwise
     *
     */
    public static boolean centerPanel(Point2D cpt, GrappaPanel gpanel) {
    if(cpt == null || gpanel == null)
        return false;
    if(!gpanel.getSubgraph().getBoundingBox().contains(cpt.getX(), cpt.getY())) {
        return false;
    }
    Object prnt = gpanel.getParent();
    if(!(prnt instanceof javax.swing.JViewport)) {
        Grappa.displayException(new RuntimeException(“the parent of the supplied GrappaPanel is not a JViewport”));
        return false;
    }
    javax.swing.JViewport vport = (javax.swing.JViewport)prnt;
    java.awt.Rectangle b = gpanel.getBounds();
    java.awt.Dimension d = vport.getExtentSize();
    AffineTransform transform = gpanel.getTransform();
    if(transform == null)
        return false;
    Point2D p = transform.transform(cpt, null);

    Rectangle2D r = new Rectangle2D.Double(p.getX() + (double)b.x – ((double)d.width)/2., p.getY() + (double)b.y – ((double)d.height)/2., (double)d.width, (double)d.height);
    r = r.createIntersection(b);
    vport.scrollRectToVisible(r.getBounds());

    return true;
    }

}

att/grappa/GrappaSupportPrintf.java
att/grappa/GrappaSupportPrintf.java/*
 *  This software may only be used by you under license from AT&T Corp.
 *  (“AT&T”).  A copy of AT&T’s Source Code Agreement is available at
 *  AT&T’s Internet website having the URL:
 *  
 *  If you received this software without first entering into a license
 *  with AT&T, you have an infringing copy of this software and cannot use
 *  it without violating AT&T’s intellectual property rights.
 */

package att.grappa;
import java.io.*;
import java.util.*;

/**
 * A class providing sprintf support.
 *
 * @version 1.2, 26 May 2005; Copyright 1996 – 2005 by AT&T Corp.
 * @author  John Mocenigo and Rich Drechsler, Research @ AT&T Labs
 */
public class GrappaSupportPrintf

    implements GrappaConstants

{

    ///////////////////////////////////////////////////////////////////////////
    //
    // GrappaSupportPrintf
    //
    ///////////////////////////////////////////////////////////////////////////

    /**
     * The familiar C-language sprintf rendered in Java and extended for
     * some Grappa types.
     *
     * @param args the first element of this array is a string giving the
     *             format of the returned string, the remaining elements
     *             are object to be formatted according to the format.
     * @return a string giving a formatted representation of the arguments.
     */
    public final static String
    sprintf(Object args[]) {

    PrintfParser    cvt;
    StringBuffer    prtbuf;
    char        format[];
    int     flen;
    int     argn;
    int     n;
    char        ch;
    boolean         flag;

    if(!(args[0] instanceof String)) {
        throw new RuntimeException(“initial argument must be format String”);
    }

    argn = 0;
    format = ((String)args[argn++]).toCharArray();

    flen = format.length;
    prtbuf = new StringBuffer(2 * flen);
    cvt = new PrintfParser();

    for (n = 0; n < flen; ) {         if ((ch = format[n++]) == '%') {         if ((n = cvt.parse(format, n)) < flen) {             switch (ch = format[n++]) {             case 'b':             if (args.length <= argn) throw new RuntimeException("too few arguments for format");             if (args[argn] instanceof GrappaBox)                 flag = ((GrappaBox)args[argn]).isDimensioned();             else                 flag = true;             if (args[argn] instanceof java.awt.geom.Rectangle2D)                 cvt.buildBox(prtbuf, ((java.awt.geom.Rectangle2D)args[argn++]), false, flag);             else throw new RuntimeException("argument " + argn + " should be a Rectangle2D");             break;             case 'B':             if (args.length <= argn) throw new RuntimeException("too few arguments for format");             if (args[argn] instanceof GrappaBox)                 flag = ((GrappaBox)args[argn]).isDimensioned();             else                 flag = true;             if (args[argn] instanceof java.awt.geom.Rectangle2D)                 cvt.buildBox(prtbuf, ((java.awt.geom.Rectangle2D)args[argn++]), true, flag);             else throw new RuntimeException("argument " + argn + " should be a Rectangle2D");             break;             case 'c':             if (args.length <= argn) throw new RuntimeException("too few arguments for format");             if (args[argn] instanceof Character)                 cvt.buildChar(prtbuf, ((Character)args[argn++]).charValue());             else throw new RuntimeException("argument " + argn + " should be a Character");             break;             case 'd':             if (args.length <= argn) throw new RuntimeException("too few arguments for format");             if (args[argn] instanceof Number)                 cvt.buildInteger(prtbuf, ((Number)args[argn++]).intValue());             else throw new RuntimeException("argument " + argn + " should be a Number");             break;             case 'o':             if (args.length <= argn) throw new RuntimeException("too few arguments for format");             if (args[argn] instanceof Character)                 cvt.buildOctal(prtbuf, ((Character)args[argn++]).charValue());             else if (args[argn] instanceof Number)                 cvt.buildOctal(prtbuf, ((Number)args[argn++]).intValue());             else throw new RuntimeException("argument " + argn + " should be a Character or Number");             break;             case 'p':             if (args.length <= argn) throw new RuntimeException("too few arguments for format");             if (args[argn] instanceof java.awt.geom.Point2D)                 cvt.buildPoint(prtbuf, ((java.awt.geom.Point2D)args[argn++]), false);             else if (args[argn] instanceof java.awt.geom.Dimension2D)                 cvt.buildSize(prtbuf, ((java.awt.geom.Dimension2D)args[argn++]), false);             else throw new RuntimeException("argument " + argn + " should be a Point2D");             break;             case 'P':             if (args.length <= argn) throw new RuntimeException("too few arguments for format");             if (args[argn] instanceof java.awt.geom.Point2D)                 cvt.buildPoint(prtbuf, ((java.awt.geom.Point2D)args[argn++]), true);             else if (args[argn] instanceof java.awt.geom.Dimension2D)                 cvt.buildSize(prtbuf, ((java.awt.geom.Dimension2D)args[argn++]), true);             else throw new RuntimeException("argument " + argn + " should be a Point2D");             break;             case 'x':             if (args.length <= argn) throw new RuntimeException("too few arguments for format");             if (args[argn] instanceof Character)                 cvt.buildHex(prtbuf, ((Character)args[argn++]).charValue(), false);             else if (args[argn] instanceof Number)                 cvt.buildHex(prtbuf, ((Number)args[argn++]).intValue(), false);             else throw new RuntimeException("argument " + argn + " should be a Character or Number");             break;             case 'X':             if (args.length <= argn) throw new RuntimeException("too few arguments for format");             if (args[argn] instanceof Character)                 cvt.buildHex(prtbuf, ((Character)args[argn++]).charValue(), true);             else if (args[argn] instanceof Number)                 cvt.buildHex(prtbuf, ((Number)args[argn++]).intValue(), true);             else throw new RuntimeException("argument " + argn + " should be a Character or Number");             break;             case 'e':             if (args.length <= argn) throw new RuntimeException("too few arguments for format");             if (args[argn] instanceof Character)                 cvt.buildExp(prtbuf, ((Character)args[argn++]).charValue(), false);             else if (args[argn] instanceof Number)                 cvt.buildExp(prtbuf, ((Number)args[argn++]).doubleValue(), false);             else throw new RuntimeException("argument " + argn + " should be a Character or Number");             break;             case 'E':             if (args.length <= argn) throw new RuntimeException("too few arguments for format");             if (args[argn] instanceof Character)                 cvt.buildExp(prtbuf, ((Character)args[argn++]).charValue(), true);             else if (args[argn] instanceof Number)                 cvt.buildExp(prtbuf, ((Number)args[argn++]).doubleValue(), true);             else throw new RuntimeException("argument " + argn + " should be a Character or Number");             break;             case 'f':             if (args.length <= argn) throw new RuntimeException("too few arguments for format");             if (args[argn] instanceof Character)                 cvt.buildFloat(prtbuf, ((Character)args[argn++]).charValue());             else if (args[argn] instanceof Number)                 cvt.buildFloat(prtbuf, ((Number)args[argn++]).doubleValue());             else throw new RuntimeException("argument " + argn + " should be a Character or Number");             break;             case 'g':             if (args.length <= argn) throw new RuntimeException("too few arguments for format");             if (args[argn] instanceof Character)                 cvt.buildFlex(prtbuf, ((Character)args[argn++]).charValue(), false);             else if (args[argn] instanceof Number)                 cvt.buildFlex(prtbuf, ((Number)args[argn++]).doubleValue(), false);             else throw new RuntimeException("argument " + argn + " should be a Character or Number");             break;             case 'G':             if (args.length <= argn) throw new RuntimeException("too few arguments for format");             if (args[argn] instanceof Character)                 cvt.buildFlex(prtbuf, ((Character)args[argn++]).charValue(), true);             else if (args[argn] instanceof Number)                 cvt.buildFlex(prtbuf, ((Number)args[argn++]).doubleValue(), true);             else throw new RuntimeException("argument " + argn + " should be a Character or Number");             break;             case 's':             if (args.length <= argn) throw new RuntimeException("too few arguments for format");             cvt.buildString(prtbuf, args[argn++].toString());             break;             case '%':             prtbuf.append('%');             break;             default:             // different compilers handle this different ways,             // some just do the equivalent of prtbuf.append(ch),             // but we will just ignore the unrecognized format             break;             }         } else prtbuf.append(ch);         } else if (ch == '\\') {         if (n < flen) {             switch (ch = format[n++]) {             case 'b':             prtbuf.append('\b');             break;             case 'f':             prtbuf.append('\f');             break;             case 'n':             prtbuf.append('\n');             break;             case 'r':             prtbuf.append('\r');             break;             case 't':             prtbuf.append('\t');             break;             case 'u':             if ((n+3) < flen) {                 if (                 GrappaSupport.isdigit(format[n])                 &&                 GrappaSupport.isdigit(format[n+1])                 &&                 GrappaSupport.isdigit(format[n+2])                 &&                 GrappaSupport.isdigit(format[n+3])                 ) {                 int uni = (int)format[n+3]+16*(int)format[n+2]+256*(int)format[n+1]+4096*(int)format[n];                 prtbuf.append((char)uni);                 n += 4;                 } else prtbuf.append('u');             } else prtbuf.append('u');             break;             case '"':             prtbuf.append('\"');             break;             case '\'':             prtbuf.append('\'');             break;             case '\\':             prtbuf.append('\\');             break;             case '0':             case '1':             case '2':             case '3':             case '4':             case '5':             case '6':             case '7':             case '8':             case '9':             // need to fix this, assumes 3 digit octals             if ((n+1) < flen) {                 if (                 GrappaSupport.isdigit(format[n])                 &&                 GrappaSupport.isdigit(format[n+1])                 ) {                 int oct = (int)format[n+1]+8*(int)format[n]+64*(int)ch;                 prtbuf.append((char)oct);                 n += 2;                 } else prtbuf.append(ch);             } else prtbuf.append(ch);             break;             }         } else prtbuf.append(ch);         } else prtbuf.append(ch);     }     return(prtbuf.toString());     }     /////////////////////////////////////////////////////////////////////////// } class PrintfParser     implements GrappaConstants {     private boolean     alternate;     private boolean     rightpad;     private boolean     sign;     private boolean     space;     private boolean     zeropad;     private boolean     trim;     private int         precision;     private int         width;     private String      plus;     private char        padding;     private StringBuffer    scratch;     ///////////////////////////////////////////////////////////////////////////     //     // Constructor     //     ///////////////////////////////////////////////////////////////////////////     PrintfParser() {     scratch = new StringBuffer();     }     ///////////////////////////////////////////////////////////////////////////     //     // printfParser     //     ///////////////////////////////////////////////////////////////////////////     final int     parse(char cfmt[]) {     return(parse(cfmt, 0));     }     ///////////////////////////////////////////////////////////////////////////     final int     parse(char cfmt[], int n) {     boolean done;     int ch;     //     // Parse the conversion specification that starts at index n     // in fmt.  Results are stored in the class variables and the     // position of the character that stopped the parse is     // returned to the caller.     //     alternate = false;     rightpad = false;     sign = false;     space = false;     zeropad = false;     trim = false;     for (done = false; n < cfmt.length && !done; n++) {         switch (cfmt[n]) {         case '-': rightpad = true; break;         case '+': sign = true; break;         case ' ': space = true; break;         case '0': zeropad = true; break;         case '#': alternate = true; break;         default: done = true; n--; break;         }     }     plus = (sign ? "+" : (space ? " " : ""));     for (width = 0; n < cfmt.length && GrappaSupport.isdigit(ch = cfmt[n]); n++)         width = width*10 + (ch - '0');     if (n < cfmt.length && cfmt[n] == '.') {         n++;         for (precision = 0; n < cfmt.length && GrappaSupport.isdigit(ch = cfmt[n]); n++)         precision = precision*10 + (ch - '0');     } else precision = -1;     padding = (zeropad && !rightpad) ? '0' : ' ';     return(n);     }     ///////////////////////////////////////////////////////////////////////////     final StringBuffer     buildChar(StringBuffer buf, int arg) {     scratch.setLength(0);     scratch.append((char)arg);     return(strpad(buf, scratch.toString(), ' ', width, rightpad));     }     ///////////////////////////////////////////////////////////////////////////     final StringBuffer     buildExp(StringBuffer buf, double arg, boolean upper) {     double      exp;     double      base;     double      val;     int     sign;     precision = (precision >= 0) ? precision : 6;

    val = arg;
    sign = (val >= 0) ? 1 : -1;
    val = (val < 0) ? -val : val;     if (val >= 1) {
        exp = Math.log(val)/LOG10;
        base = Math.pow(10, exp – (int)exp);
    } else {
        exp = Math.log(val/10)/LOG10;
        base = Math.pow(10, exp – (int)exp + 1);
    }

    scratch.setLength(0);
    scratch.append(upper ? “E” : “e”);
    scratch.append(exp > 0 ? ‘+’ : ‘-‘);

    strpad(scratch, (“” + (int)(exp > 0 ? exp : -exp)), ‘0’, 2, false);
    if (padding == ‘0’ && precision >= 0)
        padding = ‘ ‘;

    return(strpad(buf, doubleToString(sign*base, scratch.toString()), padding, width, rightpad));
    }

    ///////////////////////////////////////////////////////////////////////////

    final StringBuffer
    buildFlex(StringBuffer buf, double arg, boolean upper) {

    double      exp;
    double      val;
    double      ival;
    StringBuffer    retbuf;
    int     iexp;
    int     pr;

    trim = true;

    val = arg;
    ival = (int)arg;
    val = (val < 0) ? -val : val;     if (val >= 1) {
        exp = Math.log(val)/LOG10;
    } else {
        exp = Math.log(val/10)/LOG10;
    }

    iexp = (int)exp;
    precision = (precision >= 0) ? –precision : 5;

    if (val == ival) {
        if (alternate) {
        if (precision < 0 || iexp <= precision) {             precision -= iexp;             retbuf = buildFloat(buf, arg);         } else retbuf = buildExp(buf, arg, upper);         } else {         if (precision < 0 || iexp <= precision) {             precision = -1;             retbuf = buildInteger(buf, (int)arg);         } else retbuf = buildExp(buf, arg, upper);         }     } else if (iexp < -4 || iexp > precision)
        retbuf = buildExp(buf, arg, upper);
    else retbuf = buildFloat(buf, arg);

    return(retbuf);
    }

    ///////////////////////////////////////////////////////////////////////////

    final StringBuffer
    buildPoint(StringBuffer buf, java.awt.geom.Point2D parg, boolean upper) {

    double[]        arg = { 0, 0 };
    double[]        exp = { 0, 0 };
    double[]        val = { 0, 0 };
    double[]        ival = { 0, 0 };
    int[]           iexp = { 0, 0 };
    StringBuffer        retbuf = null;
    int         orig_precision;
    int         pr;

    trim = true;

    arg[0] = parg.getX();
    arg[1] = (Grappa.negateStringYCoord?-parg.getY():parg.getY());
    val[0] = arg[0];
    val[1] = arg[1];
    orig_precision = precision;

    for(int i=0; i<2; i++) {         precision = orig_precision;         ival[i] = (int)val[i];         val[i] = (val[i] < 0) ? -val[i] : val[i];         if (val[i] >= 1) {
        exp[i] = Math.log(val[i])/LOG10;
        } else {
        exp[i] = Math.log(val[i]/10)/LOG10;
        }

        iexp[i] = (int)exp[i];
        precision = (precision >= 0) ? –precision : 5;

        if (val[i] == ival[i]) {
        if (alternate) {
            if (precision < 0 || iexp[i] <= precision) {             precision -= iexp[i];             retbuf = buildFloat(buf, arg[i]);             } else retbuf = buildExp(buf, arg[i], upper);         } else {             if (precision < 0 || iexp[i] <= precision) {             precision = -1;             retbuf = buildInteger(buf, (long)arg[i]);             } else retbuf = buildExp(buf, arg[i], upper);         }         } else if (iexp[i] < -4 || iexp[i] > precision)
        retbuf = buildExp(buf, arg[i], upper);
        else retbuf = buildFloat(buf, arg[i]);

        if(i == 0) {
        retbuf = retbuf.append(‘,’);
        buf = retbuf;
        }
    }

    return(retbuf);
    }

    ///////////////////////////////////////////////////////////////////////////

    final StringBuffer
    buildSize(StringBuffer buf, java.awt.geom.Dimension2D parg, boolean upper) {

    double[]        arg = { 0, 0 };
    double[]        exp = { 0, 0 };
    double[]        val = { 0, 0 };
    double[]        ival = { 0, 0 };
    int[]           iexp = { 0, 0 };
    StringBuffer        retbuf = null;
    int         orig_precision;
    int         pr;

    trim = true;

    arg[0] = parg.getWidth();
    arg[1] = parg.getHeight();
    val[0] = arg[0];
    val[1] = arg[1];
    orig_precision = precision;

    for(int i=0; i<2; i++) {         precision = orig_precision;         ival[i] = (int)val[i];         val[i] = (val[i] < 0) ? -val[i] : val[i];         if (val[i] >= 1) {
        exp[i] = Math.log(val[i])/LOG10;
        } else {
        exp[i] = Math.log(val[i]/10)/LOG10;
        }

        iexp[i] = (int)exp[i];
        precision = (precision >= 0) ? –precision : 5;

        if (val[i] == ival[i]) {
        if (alternate) {
            if (precision < 0 || iexp[i] <= precision) {             precision -= iexp[i];             retbuf = buildFloat(buf, arg[i]);             } else retbuf = buildExp(buf, arg[i], upper);         } else {             if (precision < 0 || iexp[i] <= precision) {             precision = -1;             retbuf = buildInteger(buf, (long)arg[i]);             } else retbuf = buildExp(buf, arg[i], upper);         }         } else if (iexp[i] < -4 || iexp[i] > precision)
        retbuf = buildExp(buf, arg[i], upper);
        else retbuf = buildFloat(buf, arg[i]);

        if(i == 0) {
        retbuf = retbuf.append(‘,’);
        buf = retbuf;
        }
    }

    return(retbuf);
    }

    ///////////////////////////////////////////////////////////////////////////

    final StringBuffer
    buildBox(StringBuffer buf, java.awt.geom.Rectangle2D parg, boolean upper, boolean dimensioned) {

    double[]        arg = { 0, 0, 0, 0 };
    double[]        exp = { 0, 0, 0, 0 };
    double[]        val = { 0, 0, 0, 0 };
    double[]        ival = { 0, 0, 0, 0 };
    int[]           iexp = { 0, 0, 0, 0 };
    StringBuffer        retbuf = null;
    int         orig_precision;
    int         pr;

    trim = true;

    if(!dimensioned) {
        arg[0] = parg.getX();
        arg[1] = parg.getY();
        arg[2] = arg[0] + arg[2];
        arg[3] = arg[1] + arg[3];
        arg[1] = (Grappa.negateStringYCoord?-arg[1]:arg[1]);
        arg[3] = (Grappa.negateStringYCoord?-arg[3]:arg[3]);
    } else {
        arg[0] = parg.getX();
        arg[1] = (Grappa.negateStringYCoord?-parg.getY():parg.getY());
        arg[2] = parg.getWidth();
        arg[3] = parg.getHeight();
    }
    val[0] = arg[0];
    val[1] = arg[1];
    val[2] = arg[2];
    val[3] = arg[3];
    orig_precision = precision;

    for(int i=0; i<4; i++) {         precision = orig_precision;         ival[i] = (int)val[i];         val[i] = (val[i] < 0) ? -val[i] : val[i];         if (val[i] >= 1) {
        exp[i] = Math.log(val[i])/LOG10;
        } else {
        exp[i] = Math.log(val[i]/10)/LOG10;
        }

        iexp[i] = (int)exp[i];
        precision = (precision >= 0) ? –precision : 5;

        if (val[i] == ival[i]) {
        if (alternate) {
            if (precision < 0 || iexp[i] <= precision) {             precision -= iexp[i];             retbuf = buildFloat(buf, arg[i]);             } else retbuf = buildExp(buf, arg[i], upper);         } else {             if (precision < 0 || iexp[i] <= precision) {             precision = -1;             retbuf = buildInteger(buf, (long)arg[i]);             } else retbuf = buildExp(buf, arg[i], upper);         }         } else if (iexp[i] < -4 || iexp[i] > precision)
        retbuf = buildExp(buf, arg[i], upper);
        else retbuf = buildFloat(buf, arg[i]);

        if(i < 3) {         retbuf = retbuf.append(',');         buf = retbuf;         }     }     return(retbuf);     }     ///////////////////////////////////////////////////////////////////////////     final StringBuffer     buildFloat(StringBuffer buf, double arg) {     double  val;     int sign;     precision = (precision >= 0) ? precision : 6;
    val = arg;

    if (padding == ‘0’ && precision >= 0)
        padding = ‘ ‘;
    return(strpad(buf, doubleToString(val, “”), padding, width, rightpad));
    }

    ///////////////////////////////////////////////////////////////////////////

    final StringBuffer
    buildHex(StringBuffer buf, int arg, boolean upper) {

    String  str;

    scratch.setLength(0);

    str = (upper) ? Integer.toHexString(arg).toUpperCase() : Integer.toHexString(arg);

    if (precision > str.length()) {
        if (alternate)
        scratch.append(upper ? “0X” : “0x”);
        strpad(scratch, str, ‘0’, precision, false);
        strpad(buf, scratch.toString(), ‘ ‘, width, rightpad);
    } else {
        if (zeropad && !rightpad && precision < 0) {         if (alternate) {             if (width > 2) {
            strpad(scratch, str, ‘0’, width-2, rightpad);
            buf.append(upper ? “0X” : “0x”);
            buf.append(scratch.toString());
            } else {
            buf.append(upper ? “0X” : “0x”);
            buf.append(str);
            }
        } else strpad(buf, str, ‘0’, width, rightpad);
        } else {
        if (alternate) {
            scratch.append(upper ? “0X” : “0x”);
            scratch.append(str);
            str = scratch.toString();
        }
        strpad(buf, str, ‘ ‘, width, rightpad);
        }
    }

    return(buf);
    }

    ///////////////////////////////////////////////////////////////////////////

    final StringBuffer
    buildInteger(StringBuffer buf, long arg) {

    String  str;
    String  sign;
    long    val;

    scratch.setLength(0);

    val = arg;
    sign = (val >= 0) ? plus : “-“;
    str = “” + ((val < 0) ? -val : val);     if (precision > str.length()) {
        strpad(scratch, str, ‘0’, precision, false);
        scratch.insert(0, sign);
    } else {
        scratch.append(sign);
        scratch.append(str);
    }

    if (padding == ‘0’ && precision >= 0)
        padding = ‘ ‘;

    return(strpad(buf, scratch.toString(), padding, width, rightpad));
    }

    ///////////////////////////////////////////////////////////////////////////

    final StringBuffer
    buildOctal(StringBuffer buf, int arg) {

    String  str;

    scratch.setLength(0);

    if (alternate)
        scratch.append(‘0’);

    scratch.append(Integer.toOctalString(arg));
    if (precision > scratch.length()) {
        str = scratch.toString();
        scratch.setLength(0);
        strpad(scratch, str, ‘0’, precision, false);
    }

    if (padding == ‘0’ && precision >= 0)
        padding = ‘ ‘;

    return(strpad(buf, scratch.toString(), padding, width, rightpad));
    }

    ///////////////////////////////////////////////////////////////////////////

    final StringBuffer
    buildString(StringBuffer buf, String arg) {

    String  str;

    if (precision > 0) {
        if (precision < arg.length())         str = arg.substring(0, precision);         else str = arg;     } else str = arg;     return(strpad(buf, str, padding, width, rightpad));     }     ///////////////////////////////////////////////////////////////////////////     //     // Private methods     //     ///////////////////////////////////////////////////////////////////////////     private String     doubleToString(double val, String exp) {     String  sign;     double  whole;     double  power;     double  frac;     //     // Building the resulting String up by casting to an int or long     // doesn't always work, so we use algorithm that may look harder     // and slower than necessary.     //     scratch.setLength(0);     sign = (val >= 0) ? plus : “-“;
    val = (val < 0) ? -val : val;     whole = Math.floor(val);     if (precision != 0) {         power = Math.pow(10, precision);         frac = (val - whole)*power;         scratch.append((long)whole);         String tail = (""+((long)Math.round(frac)));         if(trim) {         int len = tail.length();         int extra = 0;         while(extra < len && tail.charAt(len-extra-1) == '0') extra++;         if(extra == len) {             if(exp.length() > 0) {
            tail = “.0”;
            } else {
            tail = “”;
            }
            precision = 0;
        } else if(extra > 0) {
            scratch.append(‘.’);
            tail = tail.substring(0,len-extra);
            precision -= extra;
        } else {
            scratch.append(‘.’);
        }
        } else {
        scratch.append(‘.’);
        }
        if (precision > 0 && (power/10) > frac) {
        strpad(scratch, tail, ‘0’, precision, false);
        } else scratch.append(tail);
        scratch.append(exp);
    } else {
        scratch.append((long)whole);
        if (alternate && exp.length() == 0)
        scratch.append(‘.’);
        scratch.append(exp);
    }

    if (zeropad && !rightpad) {
        String str = scratch.toString();
        scratch.setLength(0);
        strpad(scratch, str, ‘0’, width – sign.length(), false);
    }

    scratch.insert(0, sign);
    return(scratch.toString());
    }

    ///////////////////////////////////////////////////////////////////////////

    private StringBuffer
    strpad(StringBuffer buf, String str, int ch, int width, boolean right) {

    int len;
    int n;

    if (width > 0) {
        if ((len = width – str.length()) > 0) {
        if (right)
            buf.append(str);
        for (n = 0; n < len; n++)             buf.append((char)ch);         if (!right)             buf.append(str);         } else buf.append(str);     } else buf.append(str);     return(buf);     }     /////////////////////////////////////////////////////////////////////////// } att/grappa/GrappaSupportRects.java att/grappa/GrappaSupportRects.java /*  *  This software may only be used by you under license from AT&T Corp.  *  ("AT&T").  A copy of AT&T's Source Code Agreement is available at  *  AT&T's Internet website having the URL:  *  
 *  If you received this software without first entering into a license
 *  with AT&T, you have an infringing copy of this software and cannot use
 *  it without violating AT&T’s intellectual property rights.
 */

package att.grappa;

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

/**
 * This class provides a method for parsing RECORD_SHAPE node
 * labels and deriving the RECT_ATTR information from it. It
 * is called by the GrappaNexus class.
 *
 * @version 1.2, 26 May 2005; Copyright 1996 – 2005 by AT&T Corp.
 * @author  John Mocenigo, Research @ AT&T Labs
 * @see Graph
 */
public class GrappaSupportRects
    implements GrappaConstants
{
    /**
     * Rough font sizing information for the roman (or serif) font.
     */
    final static double[] romanFontwidth = {                  // +——+
    0.2500,  0.3330,  0.4080,  0.5000,  0.5000,  0.8330,  // | !”#$%|
    0.7780,  0.3330,  0.3330,  0.3330,  0.5000,  0.5640,  // |&'()*+|
    0.2500,  0.3330,  0.2500,  0.2780,  0.5000,  0.5000,  // |,-./01|
    0.5000,  0.5000,  0.5000,  0.5000,  0.5000,  0.5000,  // |234567|
    0.5000,  0.5000,  0.2780,  0.2780,  0.5640,  0.5640,  // |89:;<=|     0.5640,  0.4440,  0.9210,  0.7220,  0.6670,  0.6670,  // |>?@ABC|
    0.7220,  0.6110,  0.5560,  0.7220,  0.5560,  0.3330,  // |DEFGHI|
    0.3890,  0.7220,  0.6110,  0.8890,  0.7220,  0.7220,  // |JKLMNO|
    0.5560,  0.7220,  0.6670,  0.5560,  0.6110,  0.7220,  // |PQRSTU|
    0.7220,  0.9440,  0.7220,  0.7220,  0.6110,  0.3330,  // |VWXYZ[|
    0.2780,  0.3330,  0.4690,  0.5000,  0.3330,  0.4440,  // |\]^_`a|
    0.5000,  0.4440,  0.5000,  0.4440,  0.3330,  0.5000,  // |bcdefg|
    0.3330,  0.2780,  0.2780,  0.5000,  0.2780,  0.7780,  // |hijklm|
    0.5000,  0.5000,  0.5000,  0.5000,  0.3330,  0.3890,  // |nopqrs|
    0.2780,  0.5000,  0.5000,  0.7220,  0.5000,  0.5000,  // |tuvwxy|
    0.4440,  0.4800,  0.2000,  0.4800,  0.5410,  0.0      // |z{|}~/
    };                                                        // +—–+

    /**
     * Rough font sizing information for the helvetica (or sansserif) font.
     */
    final static double[] helveticaFontwidth = {              // +——+
    0.2780,  0.2780,  0.3550,  0.5560,  0.5560,  0.8890,  // | !”#$%|
    0.6670,  0.2210,  0.3330,  0.3330,  0.3890,  0.5840,  // |&'()*+|
    0.2780,  0.3330,  0.2780,  0.2780,  0.5560,  0.5560,  // |,-./01|
    0.5560,  0.5560,  0.5560,  0.5560,  0.5560,  0.5560,  // |234567|
    0.5560,  0.5560,  0.2780,  0.2780,  0.5840,  0.5840,  // |89:;<=|     0.5840,  0.5560,  01.015,  0.6670,  0.6670,  0.7220,  // |>?@ABC|
    0.7220,  0.6670,  0.6110,  0.7780,  0.6110,  0.2780,  // |DEFGHI|
    0.5000,  0.6670,  0.5560,  0.8330,  0.7220,  0.7780,  // |JKLMNO|
    0.6670,  0.7780,  0.7220,  0.6670,  0.6110,  0.7220,  // |PQRSTU|
    0.6670,  0.9440,  0.6670,  0.6670,  0.6110,  0.2780,  // |VWXYZ[|
    0.2780,  0.2780,  0.4690,  0.5560,  0.2220,  0.5560,  // |\]^_`a|
    0.5560,  0.5000,  0.5560,  0.5560,  0.2780,  0.5560,  // |bcdefg|
    0.2780,  0.2220,  0.2220,  0.5000,  0.2220,  0.8330,  // |hijklm|
    0.5560,  0.5560,  0.5560,  0.5560,  0.3330,  0.5000,  // |nopqrs|
    0.2780,  0.5560,  0.5000,  0.7220,  0.5000,  0.5000,  // |tuvwxy|
    0.5000,  0.3340,  0.2600,  0.3340,  0.5840,  0.0   ,  // |z{|}~/
    };                                                        // +—–+

    /**
     * Rough font sizing information for the courier (or constant) font.
     */
    final static double constantFontwidth = 0.6206;

    final static int HASTEXT  = 1;
    final static int HASPORT  = 2;
    final static int HASTABLE = 4;
    final static int INTEXT   = 8;
    final static int INPORT   = 16;

    final static char NBSP = ‘\u00a0’; // Unicode no-break space

    private static char[] parseArray = null;
    private static int arrayOffset = 0;
    private static int fields = 0;
    private static StringBuffer rbuf = null;

    // assumes shape type is RECORD_SHAPE or MRECORD_SHAPE
    protected static synchronized Object[] parseRecordInfo(Node node) {
    Object[] objs = { null, null, null };

    if(node == null) {
        return objs;
    }

    String label = (String)node.getAttributeValue(LABEL_ATTR);

    if(label != null && label.equals(“\\N”)) {
        label = node.getName();
    }

    if(label == null || label.length() == 0 || label.indexOf(‘|’) < 0) {         node.setAttribute(RECTS_ATTR, null);         return objs;     }     parseArray = label.toCharArray();     arrayOffset = 0;     TableField tableField = doParse(node, !node.getSubgraph().isLR(), true);     if(tableField == null) {         node.setAttribute(RECTS_ATTR, null);         return objs;     }     tableField.sizeFields();     double width = ((Double)node.getAttributeValue(WIDTH_ATTR)).doubleValue() * PointsPerInch;     double height = ((Double)node.getAttributeValue(HEIGHT_ATTR)).doubleValue() * PointsPerInch;     Dimension sz = new Dimension((int)Math.round(width),(int)Math.round(height));     tableField.resizeFields(sz);     GrappaPoint pos = (GrappaPoint)node.getAttributeValue(POS_ATTR);     tableField.positionFields(new Point((int)Math.round(pos.getX()-width/2.0),(int)Math.round(pos.getY()-height/2.0)));     objs[0] = new String[fields];     objs[1] = new GrappaPoint[fields];     fields = 0;     if(emitFields(tableField,objs)) {         objs[2] = rbuf.toString();         node.setAttribute(RECTS_ATTR, ((String)objs[2]));     } else {         objs = null;     }     rbuf = null;     //for(int i = 0; i < fields; i++) {         //pos = ((GrappaPoint[])objs[1])[i];         //pos.setLocation(pos.getX() - width, pos.getY() - height);     //}     return objs;     }     private static boolean emitFields(TableField tf, Object[] objs) {     boolean retval = false;     if(tf == null) return false;     int fc = tf.fieldCount();     if(fc == 0) {         Rectangle rect = tf.getBounds();         if(rbuf == null) {         rbuf = new StringBuffer();         } else {         rbuf.append(' ');         }         rbuf.append(rect.x);         rbuf.append(',');         rbuf.append(Grappa.negateStringYCoord?-rect.y:rect.y);         rbuf.append(',');         rbuf.append(rect.x+rect.width);         rbuf.append(',');         rbuf.append(Grappa.negateStringYCoord?(-rect.y-rect.height):(rect.y+rect.height));;         ((String[])objs[0])[fields] = tf.getText();         ((GrappaPoint[])objs[1])[fields] = new GrappaPoint(rect.getCenterX(), rect.getCenterY());         fields++;         return true;     }     for(int cnt = 0; cnt < fc; cnt++) {         if(emitFields(tf.fieldAt(cnt),objs))         retval = true;     }     return(retval);     }        private static TableField doParse(Node node, boolean LR, boolean topLevel) {     int maxf = 1;     int cnt = 0;     for(int pos = arrayOffset; pos < parseArray.length; pos++) {         if(parseArray[pos] == '\\') {         pos++;         if(pos < parseArray.length && (parseArray[pos] == '{' || parseArray[pos] == '}' || parseArray[pos] == '|')) {             continue;         }         }         if(parseArray[pos] == '{') {         cnt++;         } else if(parseArray[pos] == '}') {         cnt--;         } else if(cnt == 0 && parseArray[pos] == '|') {         maxf++;         }         if(cnt < 0) {         break;         }     }     TableField rv = new TableField();     rv.setLR(LR);     rv.subfields(maxf);     if(topLevel) {         rv.setParent(null);     }     StringBuffer textBuf, portBuf;     textBuf = new StringBuffer();     portBuf = new StringBuffer();          int mode = 0;     int fi = 0;     boolean wflag = true;     TableField tf = null;     char curCh = '\000';     while(wflag) {         if(arrayOffset >= parseArray.length) {
        curCh = ‘\000’;
        wflag = false;
        } else {
        curCh = parseArray[arrayOffset];
        }
        switch((int)curCh) {
        case ‘<':         if((mode & (HASTABLE|HASPORT)) != 0) {             return null;         }         mode |= (HASPORT|INPORT);         arrayOffset++;         break;         case '>‘:
        if((mode & INPORT) == 0) {
            return null;
        }
        mode &= ~INPORT;
        arrayOffset++;
        break;
        case ‘{‘:
        arrayOffset++;
        if(mode != 0 || arrayOffset >= parseArray.length) {
            return null;
        }
        mode = HASTABLE;
        if((tf = doParse(node,!LR,false)) == null) {
            return null;
        } else {
            rv.addField(tf);
            tf.setParent(rv);
        }
        break;
        case ‘}’:
        case ‘|’:
        case ‘\000’:
        if((arrayOffset >= parseArray.length && !topLevel) || (mode&INPORT) != 0) {
            return null;
        }
        if((mode&HASTABLE) == 0) {
            tf = new TableField();
            rv.addField(tf);
            tf.setLR(!LR);
            tf.setParent(rv);
            if((mode&HASPORT) != 0) {
            tf.setId(portBuf.toString().trim());
            portBuf.setLength(0);
            }
        }
        if((mode&(HASTEXT|HASTABLE)) == 0) {
            mode |= HASTEXT;
            textBuf.append(‘ ‘);
        }
        if((mode&HASTEXT) != 0) {
            tf.setTextBounds(textBuf.toString().trim(),node);
            fields++;
            //tf.setLR(true);
            textBuf.setLength(0);
        }
        if(arrayOffset < parseArray.length) {             if(curCh == '}') {             arrayOffset++;             return rv;             }             mode = 0;             arrayOffset++;         }         break;         case '\\':         if(arrayOffset+1 < parseArray.length) {             if(isSpec(parseArray[arrayOffset+1])) {             arrayOffset++;             curCh = parseArray[arrayOffset];             } else if(parseArray[arrayOffset+1] == ' ') {             arrayOffset++;             curCh = NBSP;             }         }         // fall through...         default:         if((mode&HASTABLE) != 0 && curCh != ' ' && curCh != NBSP) {             return null;         }         if((mode&(INTEXT|INPORT)) == 0 && curCh != ' ' && curCh != NBSP) {             mode |= (INTEXT|HASTEXT);         }         if((mode&INTEXT) != 0) {             textBuf.append(curCh);         } else if((mode&INPORT) != 0) {             portBuf.append(curCh);         }         arrayOffset++;         break;         }     }     return rv;     }     private static boolean isSpec(char c) {     return ((c) == '{' || (c) == '}' || (c) == '|' || (c) == '<' || (c) == '>‘);
    }
}

class TableField
    implements GrappaConstants
{
    private Dimension size = new Dimension();
    private Rectangle bounds = new Rectangle();
    private Rectangle textBounds = null;
    private TableField[] subFields = null;
    private int subFieldsUsed = 0;
    private boolean orientLR = false;
    private String idTag = null;
    private String text = null;
    private TableField parent = null;

    /**
     * Creates an empty TableField instance.
     */
    TableField() {
    //super();
    }

    void setParent(TableField prnt) {
    parent = prnt;
    }

    TableField getTopMost() {
    TableField topper = this;
    while(topper.getParent() != null)
        topper = topper.getParent();
    return topper;
    }

    TableField getParent() {
    return parent;
    }
     
    String getText() {
    return text;
    }
    
    String getIdentifier() {
    StringBuffer buf = new StringBuffer();
    if(isLR()) {
        buf.append(“LR:”);
    } else {
        buf.append(“TB:”);
    }
    buf.append(fieldCount());
    buf.append(‘(‘);
    buf.append(text);
    buf.append(‘)’);
    TableField prnt = getParent();
    while(prnt != null) {
        buf.append(‘,’);
        buf.append(prnt.fieldCount());
        buf.append(‘(‘);
        buf.append(prnt.getText());
        buf.append(‘)’);
        prnt = prnt.getParent();
    }
    return buf.toString();
    }

    /**
     * Get the bounding box of this element
     *
     * @return the bounding box of this element
     */
    Rectangle getBounds() {
    return bounds;
    }

    void setBounds(int x, int y, int width, int height) {
    bounds.setBounds(x,y,width,height);
    }

    void setBounds(Rectangle r) {
    bounds.setBounds(r);
    }

    /**
     * Get the size of this object.
     *
     * @return the size of this object.
     */
    Dimension getSize() {
    return size;
    }

    void setSize(int width, int height) {
    size.setSize(width,height);
    }

    void setSize(Dimension d) {
    size.setSize(d.width,d.height);
    }

    boolean hasFields() {
    if(subFields == null || subFields.length == 0 || subFieldsUsed == 0) {
        return false;
    }
    return true;
    }

    synchronized int subfields(int len) {
    if(len < 1) return 0;     subFields = new TableField[len];     return subFields.length;     }     int fieldCount() {     if(subFields == null) {         return 0;     }     return subFieldsUsed;     }     synchronized void addField(TableField tf) {     // can cause exception     subFields[subFieldsUsed++] = tf;     }     TableField fieldAt(int nbr) {     if(nbr < 0 || nbr >= subFieldsUsed) return null;
    return subFields[nbr];
    }

    boolean isLR() {
    return orientLR;
    }

    void setLR(boolean lr) {
    orientLR = lr;
    }

    String getId() {
    return idTag;
    }

    void setId(String id) {
    idTag = null;
    if(id == null) return;
    char[] array = id.toCharArray();
    boolean hadNBSP = false;
    for(int i = 0; i < array.length; i++) {         if(array[i] == GrappaSupportRects.NBSP) {         array[i] = ' ';         hadNBSP = true;         }     }     if(hadNBSP) idTag = new String(array,0,array.length);     else idTag = id;     }     Dimension sizeFields() {     return sizeUpFields(this);     }        private Dimension sizeUpFields(TableField tf) {     //System.err.println(tf.getIdentifier());     int fc = tf.fieldCount();     if(fc == 0) {         if(tf.getTextBounds() != null) {         tf.setSize(tf.getTextBounds().getSize());         } else {         tf.setSize(0,0);         }     } else {         Dimension dtmp = null;         Dimension dim = new Dimension();         for(int cnt = 0; cnt < fc; cnt++) {         dtmp = sizeUpFields(tf.fieldAt(cnt));         if(tf.isLR()) {             dim.width += dtmp.width;             dim.height = (dim.height > dtmp.height) ? dim.height : dtmp.height;
        } else {
            dim.width = (dim.width > dtmp.width) ? dim.width : dtmp.width;
            dim.height += dtmp.height;
        }
        }
        tf.setSize(dim);
    }
    //System.err.println(“Size:”+tf.getSize()+”;”+tf.getIdentifier());
    return tf.getSize();
    }

    Dimension resizeFields(Dimension sz) {
    resizeUpFields(this,sz);
    return this.getSize();
    }
  
    void resizeUpFields(TableField tf, Dimension sz) {
    //System.err.println(tf.getIdentifier());

    Dimension delta = new Dimension(sz.width – tf.getSize().width, sz.height – tf.getSize().height);
    tf.setSize(sz);

    int fc = tf.fieldCount();
    
    if(fc == 0) {
        //System.err.println(“Size:”+tf.getSize()+”;”+tf.getIdentifier());
        return;
    }

    // adjust children, if any
    double incr = 0;
    if(tf.isLR()) {
        incr = (double)delta.width / (double)fc;
    } else {
        incr = (double)delta.height / (double)fc;
    }
    TableField tfield = null;
    int amt = 0;
    // reuse old space under new name for readability
    Dimension newSz = delta;
    for(int cnt = 0; cnt < fc; cnt++) {         tfield = tf.fieldAt(cnt);         amt = (int)Math.floor(((double)(cnt+1))*incr) - (int)Math.floor(((double)cnt)*incr);         if(tf.isLR()) {         newSz.setSize(tfield.getSize().width+amt,sz.height);         } else {         newSz.setSize(sz.width,tfield.getSize().height+amt);         }         resizeUpFields(tfield,newSz);     }     }     void positionFields(Point pos) {     posFields(this,pos);     }     private void posFields(TableField tf, Point pos) {     tf.setBounds(pos.x,pos.y,tf.getSize().width,tf.getSize().height);     // clip spillage outside outer-most bounds     Rectangle b1 = tf.getBounds();     Rectangle b2 = tf.getTopMost().getBounds();     int tmpi;     tmpi = Math.max(b1.x,b2.x);     b1.width = Math.min(b1.x+b1.width,b2.x+b2.width) - tmpi;     b1.x = tmpi;     tmpi = Math.max(b1.y,b2.y);     b1.height = Math.min(b1.y+b1.height,b2.y+b2.height) - tmpi;     b1.y = tmpi;     int fc = tf.fieldCount();     if(fc == 0) {         return;     }     TableField tfield = null;     for(int cnt = 0; cnt < fc; cnt++) {         tfield = tf.fieldAt(cnt);         posFields(tfield,new Point(pos));         if(tf.isLR()) {         pos.x += tfield.getSize().width;         } else {         pos.y += tfield.getSize().height;         }     }     }     void setTextBounds(String str, Node node) {     int lines = 1;     boolean cwFont = false;     double[] fontwidth = { GrappaSupportRects.constantFontwidth };              String fontname = node.getAttribute(FONTNAME_ATTR).getStringValue().toLowerCase();     if(fontname.startsWith("courier") || fontname.startsWith("monospaced")) {         cwFont = true;     } else if(fontname.startsWith("helvetica") || fontname.startsWith("sansserif")) {         fontwidth = GrappaSupportRects.helveticaFontwidth;     } else {         fontwidth = GrappaSupportRects.romanFontwidth;     }     char[] array = str.toCharArray();     double fwidth = 0;     double xwidth = 0;     int value = 0;     for(int i = 0; i < array.length; i++) {         if(array[i] == GrappaSupportRects.NBSP) {         array[i] = ' ';         }         if(array[i] == '\\' && (i+1) < array.length) {         if(array[i+1] == 'n' || array[i+1] == 'l' || array[i+1] == 'r') {             lines++;             i++;             if(fwidth > xwidth)
            xwidth = fwidth;
            fwidth = 0;
            continue;
        }
        }
        value = array[i] – 32;
        fwidth += (cwFont) ? fontwidth[0] : ((value >= 0 && value < fontwidth.length) ? fontwidth[value] : 0 );     }     if(fwidth > xwidth)
        xwidth = fwidth;
    int height = ((Integer)node.getAttributeValue(FONTSIZE_ATTR)).intValue();
    int width = (int)Math.round((double)height * xwidth);
    textBounds = new Rectangle(0,0,width,height*lines);
    text = str;
    }

    Rectangle getTextBounds() {
    return(textBounds);
    }

    void debugID() {
    int fc = fieldCount();

    if(fc == 0) {
        return;
    }

    TableField tfield = null;
    for(int cnt = 0; cnt < fc; cnt++) {         tfield = fieldAt(cnt);         tfield.debugID();     }     } } att/grappa/Lexer.java att/grappa/Lexer.java/*  *  This software may only be used by you under license from AT&T Corp.  *  ("AT&T").  A copy of AT&T's Source Code Agreement is available at  *  AT&T's Internet website having the URL:  *  
 *  If you received this software without first entering into a license
 *  with AT&T, you have an infringing copy of this software and cannot use
 *  it without violating AT&T’s intellectual property rights.
 */

package att.grappa;

import java_cup.runtime.Symbol;
import java.util.Hashtable;
import java.io.*;

/**
 * A class for doing lexical analysis of dot formatted input.
 *
 * @version 1.2, 26 May 2005; Copyright 1996 – 2005 by AT&T Corp.
 * @author  John Mocenigo, Research @ AT&T Labs
 */
public class Lexer
{
    /**
     * First character of lookahead.
     * Set to ‘\n’ initially (needed for initial 2 calls to advance())
     */
    private int next_char = ‘\n’;

    /**
     * Second character of lookahead.
     * Set to ‘\n’ initially (needed for initial 2 calls to advance())
     */
    private int next_char2 = ‘\n’;

    /**
     * Current line number for use in error messages.
     * Set to -1 to account for next_char/next_char2 initialization
     */
    private int current_line = -1;

    /**
     * Character position in current line.
     */
    private int current_position = 1;

    /**
     * EOF constant.
     */
    private static final int EOF_CHAR = -1;

    /**
     * needed to handle anonymous subgraphs since parser has no precedence
     */
    private boolean haveId = false;

    /**
     * needed for retreating
     */
    private int old_char;
    private int old_position;
    boolean retreated = false;

    /**
     * Count of total errors detected so far.
     */
    private int error_count = 0;

    /**
     *  Count of warnings issued so far
     */
    private int warning_count = 0;

    /**
     *  hash tables to hold symbols
     */
    private Hashtable keywords = new Hashtable(32);
    private Hashtable char_symbols = new Hashtable(32);

    private Reader inReader;
    private PrintWriter errWriter = null;

    /**
     * common StringBuffer (suggested by Ginny Travers (bbn.com))
     */
    private StringBuffer cmnstrbuf = new StringBuffer();

    /**
     * Create an instance of Lexer that reads from input and
     * sends error messages to error.
     *
     * @param input input Reader object
     * @param error error output Writer object
     *
     * @exception IllegalArgumentException whenever input is null
     */
    public Lexer(Reader input, PrintWriter error) throws IllegalArgumentException {
    super();
    if (input == null) {
        throw new IllegalArgumentException(“Reader cannot be null”);
    }
    inReader = input;
    errWriter = error;
    }

    /**
     * Initialize internal tables and read two characters of input for
     * look-ahead purposes.
     *
     * @exception IOException if advance() does
     * @see Lexer#advance()
     */
    public void init() throws IOException {
    // set up the keyword table
    keywords.put(“strict”, new Integer(Symbols.STRICT));
    keywords.put(“strictdigraph”, new Integer(Symbols.STRICTDIGRAPH));
    keywords.put(“strictgraph”, new Integer(Symbols.STRICTGRAPH));
    keywords.put(“digraph”, new Integer(Symbols.DIGRAPH));
    keywords.put(“graph”, new Integer(Symbols.GRAPH));
    keywords.put(“subgraph”, new Integer(Symbols.SUBGRAPH));
    keywords.put(“node”, new Integer(Symbols.NODE));
    keywords.put(“edge”, new Integer(Symbols.EDGE));
    keywords.put(“–“, new Integer(Symbols.ND_EDGE_OP));
    keywords.put(“->”, new Integer(Symbols.D_EDGE_OP));

    // set up the table of single character symbols
    char_symbols.put(new Integer(‘;’), new Integer(Symbols.SEMI));
    char_symbols.put(new Integer(‘,’), new Integer(Symbols.COMMA));
    char_symbols.put(new Integer(‘{‘), new Integer(Symbols.LCUR));
    char_symbols.put(new Integer(‘}’), new Integer(Symbols.RCUR));
    char_symbols.put(new Integer(‘[‘), new Integer(Symbols.LBR));
    char_symbols.put(new Integer(‘]’), new Integer(Symbols.RBR));
    char_symbols.put(new Integer(‘=’), new Integer(Symbols.EQUAL));
    char_symbols.put(new Integer(‘:’), new Integer(Symbols.COLON));

    // read two characters of lookahead
    advance();
    advance();
    }
    /**
     * Advance the scanner one character in the input stream.  This moves
     * next_char2 to next_char and then reads a new next_char2.
     *
     * @exception IOException whenever a problem reading from input is encountered
     */
    public void advance() throws IOException {
    if(retreated) {
        retreated = false;
        int tmp_char = old_char;
        old_char = next_char;
        next_char = next_char2;
        next_char2 = tmp_char;
    } else {
        old_char = next_char;
        next_char = next_char2;
        if (next_char == EOF_CHAR) {
        next_char2 = EOF_CHAR;
        } else {
        next_char2 = inReader.read();
        }
    }

    /*
     * want to ignore a new-line if preceeding character is a backslash
     */
    if (next_char == ‘\\’ && (next_char2 == ‘\n’ || next_char2 == ‘\r’)) {
        next_char = next_char2;
        next_char2 = inReader.read();
        if(next_char == ‘\r’ && next_char2 == ‘\n’) {
        next_char = next_char2;
        next_char2 = inReader.read();
        }
        next_char = next_char2;
        next_char2 = inReader.read();
    }

    /*
     * want to treat ‘\r’ or ‘\n’ or ‘\r”\n’ as end-of-line,
     * but in all cases return only ‘\n’
     */
    if(next_char == ‘\r’) {
        if(next_char2 == ‘\n’) {
        next_char2 = inReader.read();
        }
        next_char = ‘\n’;
    }
    // count this
    if (old_char == ‘\n’) {
        current_line++;
        old_position = current_position;
        current_position = 1;
    } else {
        current_position++;
    }
    }

    private void retreat() {
    if(retreated) return;
    retreated = true;
    if(old_char == ‘\n’) {
        current_line–;
        current_position = old_position;
    } else {
        current_position–;
    }
    int tmp_char = next_char2;
    next_char2 = next_char;
    next_char = old_char;
    old_char = tmp_char;
    }

    /**
     * Emit an error message.  The message will be marked with both the
     * current line number and the position in the line.  Error messages
     * are printed on print stream passed to Lexer (if any) and a
     * GraphParserException is thrown.
     *
     * @param message the message to print.
     */
    private void emit_error(String message) {
    String output = “Lexer” + getLocation() + “: ” + message;
    if(errWriter != null) {
        errWriter.println(“ERROR: ” + output);
    }
    error_count++;
    throw new GraphParserException(output);
    }

    /**
     * Get the current location in the form “[line_number(character_offser)]”.
     * 
     * @return info about the current position in the input
     */
    public String getLocation() {
    return “[” + current_line + “(” + current_position + “)]”;
    }
  

    /**
     * Emit a warning message.  The message will be marked with both the
     * current line number and the position in the line.  Messages are
     * printed on print stream passed to Lexer (if any).
     *
     * @param message the message to print.
     */
    private void emit_warn(String message) {
    if(errWriter != null) {
        errWriter.println(“WARNING: Lexer” + getLocation() + “: ” + message);
    }
    warning_count++;
    }

    /**
     * Check if character is a valid id character;
     * @param ch the character in question.
     */
    public static boolean id_char(int ch) {
    return(Lexer.id_char((char)ch));
    }

    /**
     * Check if character is a valid id character;
     * @param ch the character in question.
     */
    public static boolean id_char(char ch) {
    return((Character.isJavaIdentifierStart(ch) && Character.getType(ch) != Character.CURRENCY_SYMBOL) || Character.isDigit(ch));
    }

    /**
     * Try to look up a single character symbol, returns -1 for not found.
     * @param ch the character in question.
     */
    private int find_single_char(int ch) {
    Integer result;

    result = (Integer) char_symbols.get(new Integer((char) ch));
    if (result == null) {
        return -1;
    } else {
        return result.intValue();
    }
    }

    /**
     * Handle swallowing up a comment.  Both old style C and new style C++
     * comments are handled.
     */
    private void swallow_comment() throws IOException {
    // next_char == ‘/’ at this point.

    // Is it a traditional comment?
    if (next_char2 == ‘*’) {
        // swallow the opener
        advance();
        advance();

        // swallow the comment until end of comment or EOF
        for (;;) {
        // if its EOF we have an error
        if (next_char == EOF_CHAR) {
            emit_error(“Specification file ends inside a comment”);
            return;
        }
        // if we can see the closer we are done
        if (next_char == ‘*’ && next_char2 == ‘/’) {
            advance();
            advance();
            return;
        }
        // otherwise swallow char and move on
        advance();
        }
    }
    // is its a new style comment
    if (next_char2 == ‘/’) {

        // swallow the opener
        advance();
        advance();

        // swallow to ‘\n’, ‘\f’, or EOF
        while (next_char != ‘\n’ && next_char != ‘\f’ && next_char != EOF_CHAR) {
        advance();
        }

        return;

    }
    // shouldn’t get here, but… if we get here we have an error
    emit_error(“Malformed comment in specification — ignored”);
    advance();
    }

    /**
     * Swallow up a quote string.  Quote strings begin with a double quote
     * and include all characters up to the first occurrence of another double
     * quote (there is no way to include a double quote inside a quote string).
     * The routine returns a Symbol object suitable for return by the scanner.
     */
    private Symbol do_quote_string() throws IOException {
    String result_str;

    // at this point we have lookahead of a double quote — swallow that
    advance();

    synchronized(cmnstrbuf) {
        cmnstrbuf.delete(0,cmnstrbuf.length()); // faster than cmnstrbuf.setLength(0)!
        // save chars until we see a double quote
        while (!(next_char == ‘”‘)) {
        // skip line break
        if (next_char == ‘\\’ && next_char2 == ‘”‘) {
            advance();
        }
        // if we have run off the end issue a message and break out of loop
        if (next_char == EOF_CHAR) {
            emit_error(“Specification file ends inside a code string”);
            break;
        }
        // otherwise record the char and move on
        cmnstrbuf.append(new Character((char) next_char));
        advance();
        }

        result_str = cmnstrbuf.toString();
    }

    // advance past the closing double quote and build a return Symbol
    advance();
    haveId = true;
    return new Symbol(Symbols.ATOM, result_str);
    }

    /**
     * Process an identifier.  Identifiers begin with a letter, underscore,
     * or dollar sign, which is followed by zero or more letters, numbers,
     * underscores or dollar signs.  This routine returns an Symbol suitable
     * for return by the scanner.
     */
    private Symbol do_id() throws IOException {
    String result_str;
    Integer keyword_num;
    char buffer[] = new char[1];

    // next_char holds first character of id
    buffer[0] = (char) next_char;

    synchronized(cmnstrbuf) {
        cmnstrbuf.delete(0,cmnstrbuf.length()); // faster than cmnstrbuf.setLength(0)!
        cmnstrbuf.append(buffer, 0, 1);
        advance();

        // collect up characters while they fit in id
        while (id_char(next_char)) {
        buffer[0] = (char) next_char;
        cmnstrbuf.append(buffer, 0, 1);
        advance();
        }
        // extract a string and try to look it up as a keyword
        result_str = cmnstrbuf.toString();
    }

    keyword_num = (Integer) keywords.get(result_str);

    // if we found something, return that keyword
    if (keyword_num != null) {
        haveId = false;
        return new Symbol(keyword_num.intValue());
    }

    // otherwise build and return an id Symbol with an attached string
    haveId = true;
    return new Symbol(Symbols.ATOM, result_str);
    }

    /**
     * The actual routine to return one Symbol.  This is normally called from
     * next_token(), but for debugging purposes can be called indirectly from
     * debug_next_token().
     */
    private Symbol real_next_token() throws IOException {
    int sym_num;

    for (;;) {
        // look for white space
        if (next_char == ‘ ‘ || next_char == ‘\t’ || next_char == ‘\n’ ||
        next_char == ‘\f’) {

        // advance past it and try the next character
        advance();
        continue;
        }

        // look for edge operator
        if (next_char == ‘-‘) {
        if (next_char2 == ‘>’) {
            advance();
            advance();
            haveId = false;
            return new Symbol(Symbols.D_EDGE_OP);
        } else if (next_char2 == ‘-‘) {
            advance();
            advance();
            haveId = false;
            return new Symbol(Symbols.ND_EDGE_OP);
        }
        }

        // look for a single character symbol
        sym_num = find_single_char(next_char);
        if (sym_num != -1) {
        if (sym_num == Symbols.LCUR && !haveId) {
            Symbol result = new Symbol(Symbols.SUBGRAPH);
            haveId = true;
            retreat();
            return result;
        }

        // found one — advance past it and return a Symbol for it
        advance();
        haveId = false;
        return new Symbol(sym_num);
        }

        // look for quoted string
        if (next_char == ‘”‘) {
        return do_quote_string();
        }

        // look for a comment
        if (next_char == ‘/’ && (next_char2 == ‘*’ || next_char2 == ‘/’)) {
        // swallow then continue the scan
        swallow_comment();
        continue;
        }

        // look for an id or keyword
        if (id_char(next_char)) {
        return do_id();
        }

        // look for EOF
        if (next_char == EOF_CHAR) {
        haveId = false;
        return new Symbol(Symbols.EOF);
        }

        // if we get here, we have an unrecognized character
        emit_warn(“Unrecognized character ‘” +
              new Character((char) next_char) + “‘(” + next_char +
              “) — ignored”);

        // advance past it
        advance();
    }
    }

    /**
     * Return one Symbol.  This method is the main external interface to
     * the scanner.
     * It consumes sufficient characters to determine the next input Symbol
     * and returns it.
     *
     * @exception IOException if advance() does
     */
    public Symbol next_token(int debugLevel) throws IOException {
    if(debugLevel > 0) {
        Symbol result = real_next_token();
        if(errWriter != null && debugLevel >= 5) {
        errWriter.println(“DEBUG: Lexer: next_token() => ” + result.sym);
        }
        return result;
    } else {
        return real_next_token();
    }
    }
}

att/grappa/Node.java
att/grappa/Node.java/*
 *  This software may only be used by you under license from AT&T Corp.
 *  (“AT&T”).  A copy of AT&T’s Source Code Agreement is available at
 *  AT&T’s Internet website having the URL:
 *  
 *  If you received this software without first entering into a license
 *  with AT&T, you have an infringing copy of this software and cannot use
 *  it without violating AT&T’s intellectual property rights.
 */

package att.grappa;

import java.util.*;
import java.io.*;

import java.awt.Color;
import java.awt.geom.Point2D;

/**
 * This class describes a node.
 *
 * @version 1.2, 26 May 2005; Copyright 1996 – 2005 by AT&T Corp.
 * @author  John Mocenigo, Research @ AT&T Labs
 */
public class Node extends Element
{
    /**
     * Default node name prefix used by setName().
     *
     * @see setName()
     */
    public final static String defaultNamePrefix = “N”;

    // vector of edges going into the node
    private Vector inEdges = null;
    // vector of edges going out of the node
    private Vector outEdges = null;

    // vector of edge ports (not used yet)
    private Vector Ports = null;

    /**
     * Use this constructor when creating a node within a subgraph.
     *
     * @param subg the parent subgraph.
     * @param name the name of this node.
     */
    public Node(Subgraph subg, String name) {
    super(Grappa.NODE,subg);
    setName(name);

    nodeAttrsOfInterest();
    }

    /**
     * Use this constructor when creating a node within a subgraph
     * with an automatically generated name.
     *
     * @param subg the parent subgraph.
     * @see setName()
     */
    public Node(Subgraph subg) {
    this(subg,(String)null);
    }

    // a listing of the attributes of interest for Nodes
    private void nodeAttrsOfInterest() {
    attrOfInterest(DISTORTION_ATTR);
    attrOfInterest(HEIGHT_ATTR);
    attrOfInterest(ORIENTATION_ATTR);
    attrOfInterest(PERIPHERIES_ATTR);
    attrOfInterest(POS_ATTR);
    attrOfInterest(SIDES_ATTR);
    attrOfInterest(SKEW_ATTR);
    attrOfInterest(STYLE_ATTR);
    attrOfInterest(WIDTH_ATTR);
    }

    // override Element methods

    /**
     * Check if this element is a node.
     * Useful for testing the subclass type of an Element object.
     *
     * @return true if this object is a Node.
     */
    public boolean isNode() {
    return(true);
    }

    /**
     * Get the type of this element.
     * Useful for distinguishing among Element objects.
     *
     * @return the class variable constant Grappa.NODE
     * @see Grappa#NODE
     */
    public int getType() {
    return(Grappa.NODE);
    }

    /**
     * Generates and sets the name for this node.
     * The generated name is the concatenation of Node.defaultNamePrefix
     * with the numeric id of this node instance.
     *
     * @see Node#defaultNamePrefix
     * @see Element#getId()
     */
    void setName() {
    String oldName = name;
    
    while(true) {
        name = Node.defaultNamePrefix + getId() + “_” + System.currentTimeMillis();
        if(getGraph().findNodeByName(name) == null) {
        break;
        }
    }

    // update subgraph node dictionary
    if(oldName != null) {
        getSubgraph().removeNode(oldName);
    }
    getSubgraph().addNode(this);

    canonName = null;
    resetEdgeNames();
    }

    /**
     * Sets the node name to the supplied argument.
     * When the argument is null, setName() is called.
     *
     * @exception IllegalArgumentException when newName is not unique.
     * @param newName the new name for the node.
     * @see Node#setName()
     */
    public void setName(String newName) throws IllegalArgumentException {
    if(newName == null) {
        setName();
        return;
    }

    String oldName = name;
    
    // test if name is the same as the old name (if any)
    if(oldName != null && oldName.equals(newName)) {
        return;
    }

    // is name unique?
    if(getGraph().findNodeByName(newName) != null) {
        throw new IllegalArgumentException(“node name (” + newName + “) is not unique”);
    }

    // update subgraph node dictionary
    if(oldName != null) {
        getSubgraph().removeNode(oldName);
    }
    name = newName;
    getSubgraph().addNode(this);

    canonName = null;
    resetEdgeNames();
    }

    private void resetEdgeNames() {
    Edge edge;

    if (inEdges != null) {
        for(int i = 0; i < inEdges.size(); i++) {         edge = (Edge)(inEdges.elementAt(i));         edge.canonName = null;         }     }     if (outEdges != null) {         for(int i = 0; i < outEdges.size(); i++) {         edge = (Edge)(outEdges.elementAt(i));         edge.canonName = null;         }     }     }     /**      * Add the given edge to this node's inEdges or outEdges dictionaries,      * if it is not already there.      * The boolean indicates whether the edge terminates at (inEdge) or      * emanates from (outEdge) the node.      *      * @param edge the edge to be added to this node's dictionary.      * @param inEdge if set true, add to inEdges dictionary otherwise add      *               to outEdges dictionary.      * @see Edge      */     synchronized public void addEdge(Edge edge, boolean inEdge) {     if(edge == null) return;     if(inEdge) {         if(inEdges == null) {         inEdges = new Vector();         }         if(!inEdges.contains(edge)) {         inEdges.addElement(edge);         }     } else {         if(outEdges == null) {         outEdges = new Vector();         }         outEdges.addElement(edge);         if(!outEdges.contains(edge)) {         outEdges.addElement(edge);         }     }     }     /**      * Find an outbound edge given its head and key.      *      * @param head the Node at the head of the edge      * @param key the key String associated with the edge      *      * @return the matching edge or null      */     public Edge findOutEdgeByKey(Node head, String key) {     if(head == null || key == null || outEdges == null) {         return null;     }     Edge edge = null;     for(int i = 0; i < outEdges.size(); i++) {         edge = (Edge)(outEdges.elementAt(i));         if(head == edge.getHead() && key.equals(edge.getKey())) {         return edge;         }     }     return null;     }     /**      * Find an inbound edge given its tail and key.      *      * @param tail the Node at the tail of the edge      * @param key the key String associated with the edge      *      * @return the matching edge or null      */     public Edge findInEdgeByKey(Node tail, String key) {     if(tail == null || key == null || inEdges == null) {         return null;     }     Edge edge = null;     for(int i = 0; i < inEdges.size(); i++) {         edge = (Edge)(inEdges.elementAt(i));         if(tail == edge.getTail() && key.equals(edge.getKey())) {         return edge;         }     }     return null;     }     /**      * Returns the center point of the node.      * as determined from the height      * and width attributes of the node.      *      * @return the node's center point.      */     public GrappaPoint getCenterPoint() {     GrappaPoint pt = (GrappaPoint)getAttributeValue(POS_ATTR);     if(pt == null) { // this should never be null, but just in case...         pt = new GrappaPoint();     } else if(!Grappa.centerPointNodes) {         Double w = (Double)getAttributeValue(WIDTH_ATTR);         Double h = (Double)getAttributeValue(HEIGHT_ATTR);         if(w != null && h != null) { // these should never be null, but...         pt = new GrappaPoint(pt.x - (w.doubleValue()/2.0), pt.y - (h.doubleValue()/2.0));         }     } else {         pt = new GrappaPoint(pt.x, pt.y); // return copy     }     return(pt);     }     /**      * Remove the given edge from this node's inEdges or outEdges dictionaries.      * The boolean indicates whether the edge terminates at (inEdge) or      * emanates from (outEdge) the node.      *      * @param edge the edge to be removed from this node's dictionary.      * @param inEdge if set true, remove from inEdges dictionary otherwise      *               remove from outEdges dictionary.      * @see Edge      */     synchronized public void removeEdge(Edge edge, boolean inEdge) {     if(edge == null) return;     if(inEdge) {         if(inEdges == null) return;         inEdges.removeElement(edge);     } else {         if(outEdges == null) return;         outEdges.removeElement(edge);     }     }     /**      * Print the node description to the provided stream.      *      * @param out the output text stream for writing the description.      */     public void printNode(PrintWriter out) {     this.printElement(out);     }     /**      * Returns the attribute conversion type for the supplied attribute name.      * After node specific attribute name/type mappings are checked, mappings      * at the element level are checked.      *      * @param attrname the attribute name      * @return the currently associated attribute type      */     public static int attributeType(String attrname) {     int convtype = -1;     int hashCode;     if(attrname != null) {         hashCode = attrname.hashCode();         if(hashCode == DISTORTION_HASH && attrname.equals(DISTORTION_ATTR)) {         convtype = DOUBLE_TYPE;         } else if(hashCode == ORIENTATION_HASH && attrname.equals(ORIENTATION_ATTR)) {         convtype = DOUBLE_TYPE;         } else if(hashCode == PERIPHERIES_HASH && attrname.equals(PERIPHERIES_ATTR)) {         convtype = INTEGER_TYPE;         } else if(hashCode == POS_HASH && attrname.equals(POS_ATTR)) {         convtype = POINT_TYPE;         } else if(hashCode == SHAPE_HASH && attrname.equals(SHAPE_ATTR)) {         convtype = SHAPE_TYPE;         } else if(hashCode == SIDES_HASH && attrname.equals(SIDES_ATTR)) {         convtype = INTEGER_TYPE;         } else if(hashCode == SKEW_HASH && attrname.equals(SKEW_ATTR)) {         convtype = DOUBLE_TYPE;         } else {         return(Element.attributeType(attrname));         }     }     return(convtype);     }     /**      * Get an Enumeration of the edges directed to or from this node.      *      * @return an Enumeration of all the edges (in or out) associated with this node.      */     public Enumeration edgeElements() {     return new Enumerator(inEdges, outEdges);     }     /**      * Get an Enumeration of the edges directed to this node.      *      * @return an Enumeration of all the inbound edges associated with this node.      */     public Enumeration inEdgeElements() {     return new Enumerator(inEdges, null);     }     /**      * Get an Enumeration of the edges directed from this node.      *      * @return an Enumeration of all the outbound edges associated with this node.      */     public Enumeration outEdgeElements() {     return new Enumerator(null, outEdges);     }     class Enumerator implements Enumeration {     int inCnt = 0;     int outCnt = 0;     Vector inEdges = null;     Vector outEdges = null;     Enumerator(Vector inEdges, Vector outEdges) {         inCnt = (inEdges == null) ? 0 : inEdges.size();         outCnt = (outEdges == null) ? 0 : outEdges.size();         this.inEdges = inEdges;         this.outEdges = outEdges;     }        public boolean hasMoreElements() {         int tmp;         if(inCnt > 0 && inCnt > (tmp = inEdges.size())) inCnt = tmp;
        if(outCnt > 0 && outCnt > (tmp = outEdges.size())) outCnt = tmp;
        return((inCnt+outCnt) > 0);
    }

    public Object nextElement() {
        synchronized (Node.this) {
        int tmp;
        if(inCnt > 0 && inCnt > (tmp = inEdges.size())) inCnt = tmp;
        if(inCnt > 0) {
            return inEdges.elementAt(–inCnt);
        }
        if(outCnt > 0 && outCnt > (tmp = outEdges.size())) outCnt = tmp;
        if(outCnt > 0) {
            return outEdges.elementAt(–outCnt);
        }
        throw new NoSuchElementException(“Node$Enumerator”);
        }
    }
    }
}

// JavaCup specification for a graph definition of the type generated by `dot’
package att.grappa;

// The next 18 comment line need to be manually copied to Parser.java after JavaCup generation.
/**
* This class provides a parser for the dot graph representation format.
* It is used in conjunction with JavaCup, a yacc-like parser generator
* originally by:
*

*


* Scott E. Hudson
* Graphics Visualization and Usability Center
* Georgia Institute of Technology
*

*

* and more recently modified and maintained by
*
* a number of people at Princeton University
.
*
* @version 1.2, 14 Feb 2001; Copyright 1996 – 2001 by AT&T Corp.
* @author John Mocenigo, Research @ AT&T Labs
*/

import java.io.*;
import java.util.*;
import java_cup.runtime.*;

action code {:
// a list of variables used in action code during grammar translation
//Parser parser = null;
Subgraph rootSubgraph;
Subgraph lastSubgraph;
Graph graph;
Subgraph thisGraph;
Node thisNode;
Edge thisEdge;
Node fromNode;
Node toNode;
String portName = null;
String toPortName;
String fromPortName;
int thisAttrType;
int thisElemType;
boolean directed = true;
String graphType;
private int anon_id = 0;
Vector attrs = new Vector(8,4);
Vector nodes = new Vector(8,4);
Vector edges = new Vector(8,4);

void appendAttr(String name, String value) {
attrs.addElement(new Attribute(thisElemType,name,value));
}

void noMacros() {
parser.report_error(“attribute macros are not supported yet”, null);
}

void attrStmt(int kind, String macroName) {
if(macroName != null) {
noMacros();
return;
}
if(attrs.size() == 0) return;
Attribute attr = null;
for(int i = 0; i < attrs.size(); i++) { if((attr = (Attribute)(attrs.elementAt(i))).getValue() == null) { // null means to not attach the attribute to an element continue; } else { switch(kind) { case Grappa.NODE: parser.debug_message(1, "adding node default attr (" + attr.getName() + ") to thisGraph(" + thisGraph.getName() + ")"); thisGraph.setNodeAttribute(attr); break; case Grappa.EDGE: parser.debug_message(1, "adding edge default attr (" + attr.getName() + ") to thisGraph(" + thisGraph.getName() + ")"); thisGraph.setEdgeAttribute(attr); break; case Grappa.SUBGRAPH: parser.debug_message(1, "adding subg default attr (" + attr.getName() + ") to thisGraph(" + thisGraph.getName() + ")"); thisGraph.setAttribute(attr); break; } } } attrs.removeAllElements(); } void startGraph(String name, boolean type, boolean strict) { if(graph == null) { graph = new Graph(name, type, strict); } else { graph.reset(name, type, strict); } directed = type; rootSubgraph = (Subgraph)graph; parser.debug_message(1, "Creating top level graph (" + name + ")"); anon_id = 0; } void openGraph() { thisGraph = rootSubgraph; thisElemType = Grappa.SUBGRAPH; parser.debug_message(1, "thisGraph(" + thisGraph.getName() + ")"); } void closeGraph() { int level = 1; if(parser.getErrorWriter() != null && parser.getDebugLevel() >= level) {

parser.debug_message(level, “parsed graph follows:”);
rootSubgraph.printSubgraph(parser.getErrorWriter());
}
}

void openSubg(String name) {
thisGraph = new Subgraph(thisGraph, name);
parser.debug_message(1, “thisGraph(” + thisGraph.getName() + “)”);
thisElemType = Grappa.SUBGRAPH;
}

String anonStr() {
return Grappa.ANONYMOUS_PREFIX + anon_id++;
}

void closeSubg() {
lastSubgraph = thisGraph;
// getSubgraph() gets the parent subgraph
thisGraph = thisGraph.getSubgraph();
if(thisGraph == null) {
parser.report_error (“parser attempted to go above root Subgraph”, null);
thisGraph = rootSubgraph;
}
parser.debug_message(1, “Created subgraph (” + lastSubgraph.getName() + “) in subgraph (” + thisGraph.getName() + “)…”);
parser.debug_message(1, “thisGraph(” + thisGraph.getName() + “)”);
}

void appendNode(String name, String port) {
if((thisNode = rootSubgraph.findNodeByName(name)) == null) {
parser.debug_message(1, “Creating node in subgraph (” + thisGraph.getName() + “)…”);
thisNode = new Node(thisGraph, name);
} else {
parser.debug_message(1, “Node already in subgraph (” + thisNode.getSubgraph().getName() + “)…”);
}
Object[] pair = new Object[2];
pair[0] = thisNode;
pair[1] = port;
nodes.addElement(pair);
parser.debug_message(1, “thisNode(” + thisNode.getName() + “)”);
thisElemType = Grappa.NODE;
}

void nodeWrap() {
Object[] pair = null;
if(nodes.size() > 0 && attrs.size() > 0) {
for(int i = 0; i < nodes.size(); i++) { pair = (Object[])(nodes.elementAt(i)); applyAttrs((Element)pair[0],null,null); } } attrs.removeAllElements(); nodes.removeAllElements(); } void bufferEdges() { Object[] pair = new Object[2]; if(nodes.size() > 0) {
pair[0] = nodes;
nodes = new Vector(8,4);
pair[1] = new Boolean(true);
} else if(lastSubgraph != null) {
pair[0] = lastSubgraph;
lastSubgraph = null;
pair[1] = new Boolean(false);
} else {
parser.report_error (“EDGE_OP without clear antecedent nodelist or subgraph”, null);
return;
}
edges.addElement(pair);
}

void edgeWrap() {
bufferEdges();
Attribute key = null;
Attribute name = null;
Attribute attr = null;
int skip = -1;
for(int i = 0; i < attrs.size(); i++) { attr = (Attribute)(attrs.elementAt(i)); if(attr.getName().equals("key")) { key = attr; if(name != null) break; } else if(attr.getName().equals("__nAmE__")) { name = attr; if(key != null) break; } } Object[] tailPair = (Object[])(edges.elementAt(0)); Object[] headPair = null; // note: when node list is used, a non-null name will cause errors // due to lack of uniqueness for(int i = 1; i < edges.size(); i++) { headPair = (Object[])(edges.elementAt(i)); if(((Boolean)(tailPair[1])).booleanValue()) { // true if node list Vector list = (Vector)(tailPair[0]); Object[] nodePair = null; for(int j = 0; j < list.size(); j++) { nodePair = (Object[])(list.elementAt(j)); edgeRHS((Node)(nodePair[0]),(String)(nodePair[1]),headPair,key,name); } list.removeAllElements(); } else { Subgraph subg = (Subgraph)(tailPair[0]); Enumeration enm = subg.elements(Grappa.NODE); while(enm.hasMoreElements()) { edgeRHS((Node)(enm.nextElement()),null,headPair,key,name); } } tailPair = headPair; } edges.removeAllElements(); attrs.removeAllElements(); } void edgeRHS(Node tail, String tailPort, Object[] headPair, Attribute keyAttr, Attribute nameAttr) { String key = (keyAttr == null) ? null : keyAttr.getStringValue(); String name = (nameAttr == null) ? null : nameAttr.getStringValue(); if(((Boolean)(headPair[1])).booleanValue()) { // true if node list Vector list = (Vector)(headPair[0]); Object[] nodePair = null; for(int j = 0; j < list.size(); j++) { nodePair = (Object[])(list.elementAt(j)); thisEdge = new Edge(thisGraph, tail, tailPort, (Node)(nodePair[0]), (String)(nodePair[1]), key, name); parser.debug_message(1, "Creating edge in subgraph (" + thisGraph.getName() + ")..."); parser.debug_message(1, "thisEdge(" + thisEdge.getName() + ")"); thisElemType = Grappa.EDGE; applyAttrs((Element)thisEdge,keyAttr,nameAttr); } } else { Subgraph subg = (Subgraph)(headPair[0]); Enumeration enm = subg.elements(Grappa.NODE); while(enm.hasMoreElements()) { thisEdge = new Edge(thisGraph, tail, tailPort, (Node)(enm.nextElement()), null, key, name); parser.debug_message(1, "Creating edge in subgraph (" + thisGraph.getName() + ")..."); parser.debug_message(1, "thisEdge(" + thisEdge.getName() + ")"); thisElemType = Grappa.EDGE; applyAttrs((Element)thisEdge,keyAttr,nameAttr); } } } void applyAttrs(Element elem, Attribute skip1, Attribute skip2) { Attribute attr = null; for(int i = 0; i < attrs.size(); i++) { attr = (Attribute)attrs.elementAt(i); if(attr == skip1) continue; else if(attr == skip2) continue; elem.setAttribute(attr); } } :}; // a method to get the final result from caller to the parser parser code {: private Graph theGraph = null; private Reader inReader; private PrintWriter errWriter; private Lexer lexer; private int debugLevel = 0; /** * Create an instance of Parser with input, error output and
* a supplied Graph object. The graph object is cleared (reset) before
* new graph components are added to it by this parsing operation.
*
* @param inputReader input Reader object
* @param errorWriter error output Writer object (or null to suppress error output)
* @param graph Graph object for storing parsed graph information (or null to create a new object)
*/
public Parser (Reader inputReader, PrintWriter errorWriter, Graph graph) {
super ();
inReader = inputReader;
errWriter = errorWriter;
theGraph = graph;
lexer = new Lexer (inputReader, errorWriter);
}

/**
* A convenience constructor equivalent to Parser(inputReader,errorWriter,null).
*
* @param inputReader input Reader object
* @param errorWriter error output Writer object (or null to suppress error output)
*/
public Parser (Reader inputReader, PrintWriter errorWriter) {
this(inputReader,errorWriter,null);
}

/**
* A convenience constructor equivalent to Parser(inputReader,null,null).
*
* @param inputReader input Reader object
*/
public Parser (Reader inputReader) {
this(inputReader,(PrintWriter)null,null);
}

/**
* Create an instance of Parser with input, error output and
* a supplied Graph object. The input stream is converted to
* a Reader and the error stream is converted to a Writer.
*
* @param inputStream input InputStream object
* @param errorStream error output OutputStream object (or null to suppress error output)
* @param graph Graph object for storing parsed graph information (or null to create a new object)
*/
public Parser (InputStream inputStream, OutputStream errorStream, Graph graph) {
this(new InputStreamReader(inputStream),new PrintWriter(errorStream,true),graph);
}

/**
* A convenience constructor equivalent to Parser(inputStream,errorStream,null).
*
* @param inputStream input InputStream object
* @param errorStream error output OutputStream object
*/
public Parser (InputStream inputStream, OutputStream errorStream) {
this(new InputStreamReader(inputStream),new PrintWriter(errorStream,true),null);
}

/**
* A convenience constructor equivalent to Parser(inputStream,null,null).
*
* @param inputStream input InputStream object
*/
public Parser (InputStream inputStream) {
this(new InputStreamReader(inputStream),(PrintWriter)null,null);
}

/**
* Get the Lexer object associated with this parser.
*
* @return the associated lexical analyzer.
*/
public Lexer getLexer() {
return lexer;
}

/**
* Get the error writer, if any, for this parser.
*
* @return the error writer for this parser.
*/
public PrintWriter getErrorWriter() {
return(errWriter);
}

/**
* Get the debug level for this parser.
* The debug level is set to a non-zero value by calling debug_parse.
*
* @return the debug level of this parser.
* @see Parser#debug_parse(int)
*/
public int getDebugLevel() {
return(debugLevel);
}

/**
* Report a fatal error.
* Calling this method will throw a GraphParserException.
*
* @param message the error message to send to the error stream and include in the thrown exception
* @param info not used
*
* @exception GraphParserException whenver this method is called
*/
public void report_error(String message, Object info) throws GraphParserException {
String loc = getLexer().getLocation();
if(errWriter != null) {
errWriter.println(“ERROR: Parser” + loc + “: ” + message);
}
throw new GraphParserException(“at ” + loc + “: ” + message);
}

/**
* Report a non-fatal error.
*
* @param message the warning message to send to the error stream, if the stream non-null.
* @param info not used
*/
public void report_warning(String message, Object info) {
String loc = getLexer().getLocation();
if(errWriter != null) {
errWriter.println(“WARNING: Parser” + loc + “: ” + message);
}
}

/**
* Write a debugging message to the error stream.
* The debug level of the message is 5.
*
* @param message the debug message to send to the error stream, if the stream non-null.
* @see Parser#debug_message(int,String)
*/
public void debug_message(String message) {
debug_message(5, message);
}

/**
* Write a debugging message to the error stream.
* A message is written only if the error stream is not null and the
* debug level of the message is greater than or equal to the debugging
* level of the parser.
*
* @param level the level of the message
* @param message the debug message to send to the error stream, if the stream non-null.
* @see Parser#getDebugLevel()
*/
public void debug_message(int level, String message) {
if(debugLevel < level) { return; } String loc = getLexer().getLocation(); if(errWriter != null) { errWriter.println("DEBUG: Parser" + loc + ": " + message); } } /** * Invokes the parser in debug mode. * The lowering the debug level reduces the amount of debugging output. * A level of 0 inhibits all debugging messages, generally a level of 10 * will let all messages get through. * * @param debug the debug level to use for filtering debug messages based on priority. * @exception Exception if parse() does
*/
public Symbol debug_parse(int debug) throws java.lang.Exception {
if(debug == 0) {
return parse();
}

debugLevel = debug;

/* the current action code */
int act;

/* the Symbol/stack element returned by a reduce */
Symbol lhs_sym = null;

/* information about production being reduced with */
short handle_size, lhs_sym_num;

/* set up direct reference to tables to drive the parser */
production_tab = production_table();
action_tab = action_table();
reduce_tab = reduce_table();

debug_message(5, “# Initializing parser”);

/* initialize the action encapsulation object */
init_actions();

/* do user initialization */
user_init();

/* the current Symbol */
cur_token = scan();

debug_message(5, “# Current Symbol is #” + cur_token.sym);

/* push dummy Symbol with start state to get us underway */
stack.push(new Symbol(0, start_state()));
tos = 0;

/* continue until we are told to stop */
for (_done_parsing = false; !_done_parsing; )
{
/* current state is always on the top of the stack */

/* look up action out of the current state with the current input */
act = get_action(((Symbol)stack.peek()).parse_state, cur_token.sym);

/* decode the action — > 0 encodes shift */
if (act > 0)
{
/* shift to the encoded state by pushing it on the stack */
cur_token.parse_state = act-1;
debug_shift(cur_token);
stack.push(cur_token);
tos++;

/* advance to the next Symbol */
cur_token = scan();
debug_message(5, “# Current token is #” + cur_token.sym);
}
/* if its less than zero, then it encodes a reduce action */
else if (act < 0) { /* perform the action for the reduce */ lhs_sym = do_action((-act)-1, this, stack, tos); /* look up information about the production */ lhs_sym_num = production_tab[(-act)-1][0]; handle_size = production_tab[(-act)-1][1]; debug_reduce((-act)-1, lhs_sym_num, handle_size); /* pop the handle off the stack */ for (int i = 0; i < handle_size; i++) { stack.pop(); tos--; } /* look up the state to go to from the one popped back to */ act = get_reduce(((Symbol)stack.peek()).parse_state, lhs_sym_num); /* shift to that state */ lhs_sym.parse_state = act; stack.push(lhs_sym); tos++; debug_message(5, "# Goto state #" + act); } /* finally if the entry is zero, we have an error */ else if (act == 0) { /* call user syntax error reporting routine */ syntax_error(cur_token); /* try to error recover */ if (!error_recovery(true)) { /* if that fails give up with a fatal syntax error */ unrecovered_syntax_error(cur_token); /* just in case that wasn't fatal enough, end parse */ done_parsing(); } else { lhs_sym = (Symbol)stack.peek(); } } } return lhs_sym; } CUP$Parser$actions getActionObject () { return action_obj; } /** * Get the graph resulting from the parsing operations. * * @return the graph generated from the input. */ public Graph getGraph () { return action_obj.graph; } :}; // Preliminaries to set up and use the scanner. init with {: lexer.init(); action_obj.graph = theGraph; //action_obj.parser = this; :}; scan with {: return lexer.next_token(debugLevel); :}; // Terminals (tokens returned by the scanner). terminal Integer GRAPH, NODE, EDGE, SUBGRAPH, D_EDGE_OP, ND_EDGE_OP; terminal STRICT, DIGRAPH, STRICTGRAPH, STRICTDIGRAPH; terminal SEMI, COMMA, LCUR, RCUR, LBR, RBR, EQUAL, COLON, ATSIGN; terminal String ATOM; // Non terminals non terminal Boolean optStrict, graphType, rCompound; non terminal Integer attrType; non terminal String optSubgHdr, optGraphName, optMacroName, optPort; non terminal graph, hdr, body, optStmtList, stmtList, stmt; non terminal attrStmt, optSemi, optSeparator, compound, simple, optAttr; non terminal nodeList, subgraph, node, attrList, graphAttrDefs, optAttrDefs; non terminal attrDefs, attrItem, attrAssignment, attrMacro, edge_op; start with graph; // the grammar graph ::= hdr {: openGraph(); :} body {: closeGraph(); :} | error:val {: //CUP$parser.report_error ("An error was encountered while graph parsing (" + val.toString() + ").", null); parser.report_error ("An error was encountered while graph parsing (" + val.toString() + ").", null); :} | /* empty */ {: graph = new Graph("empty"); //((Parser)(CUP$parser)).report_warning ("The graph to parse is empty.", null); ((Parser)(parser)).report_warning ("The graph to parse is empty.", null); :} ; hdr ::= optStrict:strict graphType:type optGraphName:name {: startGraph(name,type.booleanValue(),strict.booleanValue()); :} | STRICTGRAPH optGraphName:name {: startGraph(name,true,false); :} | STRICTDIGRAPH optGraphName:name {: startGraph(name,true,true); :} ; optStrict ::= STRICT {: RESULT = new Boolean(true); :} | /* empty */ {: RESULT = new Boolean(false); :} ; graphType ::= GRAPH {: RESULT = new Boolean(false); :} | DIGRAPH {: RESULT = new Boolean(true); :} ; optGraphName ::= ATOM:val {: RESULT = val; :} | // empty {: RESULT = anonStr(); :} ; body ::= LCUR optStmtList RCUR ; optStmtList ::= stmtList | // empty ; stmtList ::= stmtList stmt | stmt ; stmt ::= attrStmt optSemi | compound optSemi ; compound ::= simple rCompound:val optAttr {: if (val.booleanValue()) edgeWrap(); else nodeWrap(); :} ; simple ::= nodeList | subgraph ; edge_op ::= D_EDGE_OP {: if(!directed) { //CUP$parser.report_error ("attempt to create a directed edge in a non-directed graph",null); parser.report_error ("attempt to create a directed edge in a non-directed graph",null); } :} | ND_EDGE_OP {: if(directed) { //CUP$parser.report_error ("attempt to create a non-directed edge in a directed graph",null); parser.report_error ("attempt to create a non-directed edge in a directed graph",null); } :} ; rCompound ::= edge_op {: thisElemType = Grappa.EDGE; bufferEdges(); :} simple rCompound:val {: thisElemType = Grappa.EDGE; RESULT = new Boolean(true); :} | // empty {: thisElemType = Grappa.NODE; RESULT = new Boolean(false); :} ; nodeList ::= node | nodeList COMMA node ; node ::= ATOM:name optPort:port {: appendNode(name,port); :} ; optPort ::= COLON ATOM:val // ignore port IDs {: RESULT = val; :} | // empty {: RESULT = null; :} ; attrStmt ::= attrType:type optMacroName:name attrList {: attrStmt(type.intValue(),name); :} | graphAttrDefs {: attrStmt(Grappa.SUBGRAPH,null); :} ; attrType ::= GRAPH:val {: RESULT = new Integer(Grappa.SUBGRAPH); :} | NODE:val {: RESULT = new Integer(Grappa.NODE); :} | EDGE:val {: RESULT = new Integer(Grappa.EDGE); :} ; optMacroName ::= ATOM:val EQUAL {: RESULT = val; :} | // empty {: RESULT = null; :} ; optAttr ::= attrList | // empty ; attrList ::= optAttr LBR optAttrDefs RBR ; optAttrDefs ::= attrDefs | // empty ; attrDefs ::= attrItem | attrDefs optSeparator attrItem ; attrItem ::= attrAssignment | attrMacro ; attrAssignment ::= ATOM:name EQUAL ATOM:value {: appendAttr(name,value); :} ; attrMacro ::= ATSIGN ATOM:name // not yet implemented {: appendAttr(name,null); :} ; graphAttrDefs ::= attrAssignment ; subgraph ::= optSubgHdr:val {: openSubg(val); :} body {: closeSubg(); :} ; optSubgHdr ::= SUBGRAPH ATOM:val {: RESULT = val; :} | SUBGRAPH {: RESULT = anonStr(); :} | // empty {: RESULT = anonStr(); :} ; optSemi ::= SEMI | // empty ; optSeparator ::= SEMI | COMMA | // empty ; att/grappa/Parser.java att/grappa/Parser.java //---------------------------------------------------- // The following code was generated by CUP v0.10k // Wed May 25 19:10:51 EDT 2005 //---------------------------------------------------- package att.grappa; import java.io.*; import java.util.*; import java_cup.runtime.*; /** CUP v0.10k generated parser.   * @version Wed May 25 19:10:51 EDT 2005   */ /** * This class provides a parser for the dot graph representation format.
* It is used in conjunction with JavaCup, a yacc-like parser generator
* originally by:


Scott E. Hudson
* Graphics Visualization and Usability Center
* Georgia Institute of Technology

* and more recently modified and maintained by

* a number of people at Princeton University.
*
* @version 1.2, 26 May 2005; Copyright 1996 – 2005 by AT&T Corp.
* @author  John Mocenigo, Research @ AT&T Labs
*/
public class Parser extends java_cup.runtime.lr_parser {

  /** Default constructor. */
  public Parser() {super();}

  /** Constructor which sets the default scanner. */
  public Parser(java_cup.runtime.Scanner s) {super(s);}

  /** Production table. */
  protected static final short _production_table[][] = 
    unpackFromStrings(new String[] {
    “\000\077\000\002\002\004\000\002\042\002\000\002\013” +
    “\005\000\002\013\003\000\002\013\002\000\002\014\005” +
    “\000\002\014\004\000\002\014\004\000\002\003\003\000” +
    “\002\003\002\000\002\004\003\000\002\004\003\000\002” +
    “\010\003\000\002\010\002\000\002\015\005\000\002\016” +
    “\003\000\002\016\002\000\002\017\004\000\002\017\003” +
    “\000\002\020\004\000\002\020\004\000\002\024\005\000” +
    “\002\025\003\000\002\025\003\000\002\041\003\000\002” +
    “\041\003\000\002\043\002\000\002\005\006\000\002\005” +
    “\002\000\002\027\003\000\002\027\005\000\002\031\004” +
    “\000\002\012\004\000\002\012\002\000\002\021\005\000” +
    “\002\021\003\000\002\006\003\000\002\006\003\000\002” +
    “\006\003\000\002\011\004\000\002\011\002\000\002\026” +
    “\003\000\002\026\002\000\002\032\006\000\002\034\003” +
    “\000\002\034\002\000\002\035\003\000\002\035\005\000” +
    “\002\036\003\000\002\036\003\000\002\037\005\000\002” +
    “\040\004\000\002\033\003\000\002\044\002\000\002\030” +
    “\005\000\002\007\004\000\002\007\003\000\002\007\002” +
    “\000\002\022\003\000\002\022\002\000\002\023\003\000” +
    “\002\023\003\000\002\023\002” });

  /** Access to production table. */
  public short[][] production_table() {return _production_table;}

  /** Parse-action table. */
  protected static final short[][] _action_table = 
    unpackFromStrings(new String[] {
    “\000\121\000\020\002\ufffd\003\010\004\ufff8\012\007\013” +
    “\ufff8\014\004\015\005\001\002\000\006\020\ufff4\027\120” +
    “\001\002\000\006\020\ufff4\027\120\001\002\000\006\004” +
    “\117\013\116\001\002\000\006\004\ufff9\013\ufff9\001\002” +
    “\000\004\002\ufffe\001\002\000\004\002\114\001\002\000” +
    “\004\020\000\001\002\000\004\020\015\001\002\000\004” +
    “\002\uffff\001\002\000\020\004\026\005\037\006\025\007” +
    “\016\020\uffc8\021\ufff1\027\022\001\002\000\006\020\uffc9” +
    “\027\113\001\002\000\022\004\uffcd\005\uffcd\006\uffcd\007” +
    “\uffcd\016\uffcd\020\uffcd\021\uffcd\027\uffcd\001\002\000\022” +
    “\004\uffde\005\uffde\006\uffde\007\uffde\016\uffde\020\uffde\021” +
    “\uffde\027\uffde\001\002\000\004\021\112\001\002\000\036” +
    “\004\uffe0\005\uffe0\006\uffe0\007\uffe0\010\uffe0\011\uffe0\016” +
    “\uffe0\017\uffe0\020\uffe0\021\uffe0\022\uffe0\024\063\025\071” +
    “\027\uffe0\001\002\000\030\004\uffea\005\uffea\006\uffea\007” +
    “\uffea\010\uffea\011\uffea\016\uffea\020\uffea\021\uffea\022\uffea” +
    “\027\uffea\001\002\000\004\020\uffcc\001\002\000\006\022” +
    “\uffdb\027\uffdb\001\002\000\006\022\uffdd\027\uffdd\001\002” +
    “\000\032\004\uffe4\005\uffe4\006\uffe4\007\uffe4\010\uffe4\011” +
    “\uffe4\016\uffe4\017\uffe4\020\uffe4\021\uffe4\022\uffe4\027\uffe4” +
    “\001\002\000\022\004\uffc6\005\uffc6\006\uffc6\007\uffc6\016” +
    “\074\020\uffc6\021\uffc6\027\uffc6\001\002\000\030\004\uffe5” +
    “\005\uffe5\006\uffe5\007\uffe5\010\101\011\077\016\uffe5\020” +
    “\uffe5\021\uffe5\022\uffe5\027\uffe5\001\002\000\022\004\uffc6” +
    “\005\uffc6\006\uffc6\007\uffc6\016\074\020\uffc6\021\uffc6\027” +
    “\uffc6\001\002\000\032\004\uffeb\005\uffeb\006\uffeb\007\uffeb” +
    “\010\uffeb\011\uffeb\016\uffeb\017\066\020\uffeb\021\uffeb\022” +
    “\uffeb\027\uffeb\001\002\000\020\004\uffef\005\uffef\006\uffef” +
    “\007\uffef\020\uffef\021\uffef\027\uffef\001\002\000\006\022” +
    “\uffd9\027\041\001\002\000\020\004\026\005\037\006\025” +
    “\007\016\020\uffc8\021\ufff2\027\022\001\002\000\006\022” +
    “\uffdc\027\uffdc\001\002\000\020\004\ufff0\005\ufff0\006\ufff0” +
    “\007\ufff0\020\ufff0\021\ufff0\027\ufff0\001\002\000\004\024” +
    “\065\001\002\000\004\022\uffd7\001\002\000\004\022\045” +
    “\001\002\000\024\004\uffdf\005\uffdf\006\uffdf\007\uffdf\016” +
    “\uffdf\020\uffdf\021\uffdf\022\uffd8\027\uffdf\001\002\000\010” +
    “\023\uffd4\026\051\027\046\001\002\000\004\024\063\001” +
    “\002\000\014\016\060\017\057\023\uffd5\026\uffc3\027\uffc3” +
    “\001\002\000\014\016\uffd1\017\uffd1\023\uffd1\026\uffd1\027” +
    “\uffd1\001\002\000\004\027\056\001\002\000\014\016\uffd3” +
    “\017\uffd3\023\uffd3\026\uffd3\027\uffd3\001\002\000\014\016” +
    “\uffd0\017\uffd0\023\uffd0\026\uffd0\027\uffd0\001\002\000\004” +
    “\023\055\001\002\000\024\004\uffd6\005\uffd6\006\uffd6\007” +
    “\uffd6\016\uffd6\020\uffd6\021\uffd6\022\uffd6\027\uffd6\001\002” +
    “\000\014\016\uffce\017\uffce\023\uffce\026\uffce\027\uffce\001” +
    “\002\000\006\026\uffc4\027\uffc4\001\002\000\006\026\uffc5” +
    “\027\uffc5\001\002\000\006\026\051\027\046\001\002\000” +
    “\014\016\uffd2\017\uffd2\023\uffd2\026\uffd2\027\uffd2\001\002” +
    “\000\004\027\064\001\002\000\030\004\uffcf\005\uffcf\006” +
    “\uffcf\007\uffcf\016\uffcf\017\uffcf\020\uffcf\021\uffcf\023\uffcf” +
    “\026\uffcf\027\uffcf\001\002\000\004\022\uffda\001\002\000” +
    “\004\027\067\001\002\000\034\004\uffe0\005\uffe0\006\uffe0” +
    “\007\uffe0\010\uffe0\011\uffe0\016\uffe0\017\uffe0\020\uffe0\021” +
    “\uffe0\022\uffe0\025\071\027\uffe0\001\002\000\032\004\uffe3” +
    “\005\uffe3\006\uffe3\007\uffe3\010\uffe3\011\uffe3\016\uffe3\017” +
    “\uffe3\020\uffe3\021\uffe3\022\uffe3\027\uffe3\001\002\000\004” +
    “\027\073\001\002\000\032\004\uffe2\005\uffe2\006\uffe2\007” +
    “\uffe2\010\uffe2\011\uffe2\016\uffe2\017\uffe2\020\uffe2\021\uffe2” +
    “\022\uffe2\027\uffe2\001\002\000\032\004\uffe1\005\uffe1\006” +
    “\uffe1\007\uffe1\010\uffe1\011\uffe1\016\uffe1\017\uffe1\020\uffe1” +
    “\021\uffe1\022\uffe1\027\uffe1\001\002\000\020\004\uffc7\005” +
    “\uffc7\006\uffc7\007\uffc7\020\uffc7\021\uffc7\027\uffc7\001\002” +
    “\000\020\004\uffed\005\uffed\006\uffed\007\uffed\020\uffed\021” +
    “\uffed\027\uffed\001\002\000\010\007\uffe7\020\uffe7\027\uffe7” +
    “\001\002\000\010\007\uffe8\020\uffe8\027\uffe8\001\002\000” +
    “\024\004\uffd7\005\uffd7\006\uffd7\007\uffd7\016\uffd7\020\uffd7” +
    “\021\uffd7\022\uffd7\027\uffd7\001\002\000\010\007\uffe9\020” +
    “\uffe9\027\uffe9\001\002\000\024\004\uffec\005\uffec\006\uffec” +
    “\007\uffec\016\uffec\020\uffec\021\uffec\022\045\027\uffec\001” +
    “\002\000\024\004\uffd8\005\uffd8\006\uffd8\007\uffd8\016\uffd8” +
    “\020\uffd8\021\uffd8\022\uffd8\027\uffd8\001\002\000\010\007” +
    “\016\020\uffc8\027\067\001\002\000\030\004\uffe5\005\uffe5” +
    “\006\uffe5\007\uffe5\010\101\011\077\016\uffe5\020\uffe5\021” +
    “\uffe5\022\uffe5\027\uffe5\001\002\000\024\004\uffe6\005\uffe6” +
    “\006\uffe6\007\uffe6\016\uffe6\020\uffe6\021\uffe6\022\uffe6\027” +
    “\uffe6\001\002\000\020\004\uffee\005\uffee\006\uffee\007\uffee” +
    “\020\uffee\021\uffee\027\uffee\001\002\000\004\020\015\001” +
    “\002\000\030\004\uffcb\005\uffcb\006\uffcb\007\uffcb\010\uffcb” +
    “\011\uffcb\016\uffcb\020\uffcb\021\uffcb\022\uffcb\027\uffcb\001” +
    “\002\000\032\002\ufff3\004\ufff3\005\ufff3\006\ufff3\007\ufff3” +
    “\010\ufff3\011\ufff3\016\ufff3\020\ufff3\021\ufff3\022\ufff3\027” +
    “\ufff3\001\002\000\004\020\uffca\001\002\000\004\002\001” +
    “\001\002\000\006\020\ufff4\027\120\001\002\000\006\020” +
    “\ufff6\027\ufff6\001\002\000\006\020\ufff7\027\ufff7\001\002” +
    “\000\004\020\ufff5\001\002\000\004\020\ufffc\001\002\000” +
    “\004\020\ufffa\001\002\000\004\020\ufffb\001\002” });

  /** Access to parse-action table. */
  public short[][] action_table() {return _action_table;}

  /** reduce_goto table. */
  protected static final short[][] _reduce_table = 
    unpackFromStrings(new String[] {
    “\000\121\000\010\003\005\013\010\014\011\001\001\000” +
    “\004\010\122\001\001\000\004\010\121\001\001\000\004” +
    “\004\114\001\001\000\002\001\001\000\002\001\001\000” +
    “\002\001\001\000\004\042\012\001\001\000\004\015\013” +
    “\001\001\000\002\001\001\000\034\006\034\007\023\016” +
    “\020\017\035\020\033\021\027\024\031\025\030\027\032” +
    “\030\022\031\026\033\017\037\016\001\001\000\002\001” +
    “\001\000\002\001\001\000\002\001\001\000\002\001\001” +
    “\000\004\012\071\001\001\000\002\001\001\000\004\044” +
    “\107\001\001\000\002\001\001\000\002\001\001\000\002” +
    “\001\001\000\004\022\106\001\001\000\006\005\077\041” +
    “\075\001\001\000\004\022\074\001\001\000\002\001\001” +
    “\000\002\001\001\000\004\011\041\001\001\000\030\006” +
    “\034\007\023\020\037\021\027\024\031\025\030\027\032” +
    “\030\022\031\026\033\017\037\016\001\001\000\002\001” +
    “\001\000\002\001\001\000\002\001\001\000\006\026\042” +
    “\032\043\001\001\000\002\001\001\000\002\001\001\000” +
    “\014\034\053\035\046\036\051\037\047\040\052\001\001” +
    “\000\002\001\001\000\004\023\060\001\001\000\002\001” +
    “\001\000\002\001\001\000\002\001\001\000\002\001\001” +
    “\000\002\001\001\000\002\001\001\000\002\001\001\000” +
    “\002\001\001\000\002\001\001\000\010\036\061\037\047” +
    “\040\052\001\001\000\002\001\001\000\002\001\001\000” +
    “\002\001\001\000\002\001\001\000\004\031\067\001\001” +
    “\000\004\012\071\001\001\000\002\001\001\000\002\001” +
    “\001\000\002\001\001\000\002\001\001\000\002\001\001” +
    “\000\002\001\001\000\004\043\103\001\001\000\002\001” +
    “\001\000\006\026\101\032\102\001\001\000\002\001\001” +
    “\000\002\001\001\000\002\001\001\000\014\007\023\025” +
    “\104\027\032\030\022\031\026\001\001\000\006\005\105” +
    “\041\075\001\001\000\002\001\001\000\002\001\001\000” +
    “\004\015\110\001\001\000\002\001\001\000\002\001\001” +
    “\000\002\001\001\000\002\001\001\000\004\010\120\001” +
    “\001\000\002\001\001\000\002\001\001\000\002\001\001” +
    “\000\002\001\001\000\002\001\001\000\002\001\001” });

  /** Access to reduce_goto table. */
  public short[][] reduce_table() {return _reduce_table;}

  /** Instance of action encapsulation class. */
  protected CUP$Parser$actions action_obj;

  /** Action encapsulation object initializer. */
  protected void init_actions()
    {
      action_obj = new CUP$Parser$actions(this);
    }

  /** Invoke a user supplied parse action. */
  public java_cup.runtime.Symbol do_action(
    int                        act_num,
    java_cup.runtime.lr_parser parser,
    java.util.Stack            stack,
    int                        top)
    throws java.lang.Exception
  {
    /* call code in generated class */
    return action_obj.CUP$Parser$do_action(act_num, parser, stack, top);
  }

  /** Indicates start state. */
  public int start_state() {return 0;}
  /** Indicates start production. */
  public int start_production() {return 0;}

  /** EOF Symbol index. */
  public int EOF_sym() {return 0;}

  /** error Symbol index. */
  public int error_sym() {return 1;}

  /** User initialization code. */
  public void user_init() throws java.lang.Exception
    {

  lexer.init();
  action_obj.graph = theGraph;
  //action_obj.parser = this;

    }

  /** Scan to get the next Symbol. */
  public java_cup.runtime.Symbol scan()
    throws java.lang.Exception
    {
 return lexer.next_token(debugLevel); 
    }

  private Graph theGraph = null;
  private Reader inReader;
  private PrintWriter errWriter;
  private Lexer lexer;
  private int debugLevel = 0;

  /**
   * Create an instance of Parser with input, error output and
   * a supplied Graph object.  The graph object is cleared (reset) before
   * new graph components are added to it by this parsing operation.
   *
   * @param inputReader input Reader object
   * @param errorWriter error output Writer object (or null to suppress error output)
   * @param graph Graph object for storing parsed graph information (or null to create a new object)
   */
  public Parser (Reader inputReader, PrintWriter errorWriter, Graph graph) {
    super ();
    inReader = inputReader;
    errWriter = errorWriter;
    theGraph = graph;
    lexer = new Lexer (inputReader, errorWriter);
  }

  /**
   * A convenience constructor equivalent to Parser(inputReader,errorWriter,null).
   *
   * @param inputReader input Reader object
   * @param errorWriter error output Writer object (or null to suppress error output)
   */
  public Parser (Reader inputReader, PrintWriter errorWriter) {
    this(inputReader,errorWriter,null);
  }

  /**
   * A convenience constructor equivalent to Parser(inputReader,null,null).
   *
   * @param inputReader input Reader object
   */
  public Parser (Reader inputReader) {
    this(inputReader,(PrintWriter)null,null);
  }

  /**
   * Create an instance of Parser with input, error output and
   * a supplied Graph object.  The input stream is converted to
   * a Reader and the error stream is converted to a Writer.
   *
   * @param inputStream input InputStream object
   * @param errorStream error output OutputStream object (or null to suppress error output)
   * @param graph Graph object for storing parsed graph information (or null to create a new object)
   */
  public Parser (InputStream inputStream, OutputStream errorStream, Graph graph) {
    this(new InputStreamReader(inputStream),new PrintWriter(errorStream,true),graph);
  }

  /**
   * A convenience constructor equivalent to Parser(inputStream,errorStream,null).
   *
   * @param inputStream input InputStream object
   * @param errorStream error output OutputStream object
   */
  public Parser (InputStream inputStream, OutputStream errorStream) {
    this(new InputStreamReader(inputStream),new PrintWriter(errorStream,true),null);
  }

  /**
   * A convenience constructor equivalent to Parser(inputStream,null,null).
   *
   * @param inputStream input InputStream object
   */
  public Parser (InputStream inputStream) {
    this(new InputStreamReader(inputStream),(PrintWriter)null,null);
  }

  /**
   * Get the Lexer object associated with this parser.
   *
   * @return the associated lexical analyzer.
   */
  public Lexer getLexer() {
    return lexer;
  }

  /**
   * Get the error writer, if any, for this parser.
   *
   * @return the error writer for this parser.
   */
  public PrintWriter getErrorWriter() {
    return(errWriter);
  }

  /**
   * Get the debug level for this parser.
   * The debug level is set to a non-zero value by calling debug_parse.
   *
   * @return the debug level of this parser.
   * @see Parser#debug_parse(int)
   */
  public int getDebugLevel() {
    return(debugLevel);
  }

  /**
   * Report a fatal error.
   * Calling this method will throw a GraphParserException.
   *
   * @param message the error message to send to the error stream and include in the thrown exception
   * @param info not used
   *
   * @exception GraphParserException whenver this method is called
   */
  public void report_error(String message, Object info) throws GraphParserException {
    String loc = getLexer().getLocation();
    if(errWriter != null) {
      errWriter.println(“ERROR: Parser” + loc + “: ” + message);
    }
    throw new GraphParserException(“at ” + loc + “: ” + message);
  }

  /**
   * Report a non-fatal error.
   *
   * @param message the warning message to send to the error stream, if the stream non-null.
   * @param info not used
   */
  public void report_warning(String message, Object info) {
    String loc = getLexer().getLocation();
    if(errWriter != null) {
      errWriter.println(“WARNING: Parser” + loc + “: ” + message);
    }
  }

  /**
   * Write a debugging message to the error stream.
   * The debug level of the message is 5.
   *
   * @param message the debug message to send to the error stream, if the stream non-null.
   * @see Parser#debug_message(int,String)
   */
  public void debug_message(String message) {
    debug_message(5, message);
  }

  /**
   * Write a debugging message to the error stream.
   * A message is written only if the error stream is not null and the
   * debug level of the message is greater than or equal to the debugging
   * level of the parser.
   *
   * @param level the level of the message
   * @param message the debug message to send to the error stream, if the stream non-null.
   * @see Parser#getDebugLevel()
   */
  public void debug_message(int level, String message) {
    if(debugLevel < level) {       return;     }     String loc = getLexer().getLocation();     if(errWriter != null) {       errWriter.println("DEBUG: Parser" + loc + ": " + message);     }   }   /**    * Invokes the parser in debug mode.    * The lowering the debug level reduces the amount of debugging output.    * A level of 0 inhibits all debugging messages, generally a level of 10    * will let all messages get through.    *    * @param debug the debug level to use for filtering debug messages based on priority.     * @exception Exception if parse() does
   */
  public Symbol debug_parse(int debug) throws java.lang.Exception {
    if(debug == 0) {
      return parse();
    }

    debugLevel = debug;

    /* the current action code */
    int act;

    /* the Symbol/stack element returned by a reduce */
    Symbol lhs_sym = null;

    /* information about production being reduced with */
    short handle_size, lhs_sym_num;

    /* set up direct reference to tables to drive the parser */
    production_tab = production_table();
    action_tab     = action_table();
    reduce_tab     = reduce_table();

    debug_message(5, “# Initializing parser”);

    /* initialize the action encapsulation object */
    init_actions();

    /* do user initialization */
    user_init();

    /* the current Symbol */
    cur_token = scan(); 

    debug_message(5, “# Current Symbol is #” + cur_token.sym);

    /* push dummy Symbol with start state to get us underway */
    stack.push(new Symbol(0, start_state()));
    tos = 0;

    /* continue until we are told to stop */
    for (_done_parsing = false; !_done_parsing; )
      {
    /* current state is always on the top of the stack */

    /* look up action out of the current state with the current input */
    act = get_action(((Symbol)stack.peek()).parse_state, cur_token.sym);

    /* decode the action — > 0 encodes shift */
    if (act > 0)
      {
        /* shift to the encoded state by pushing it on the stack */
        cur_token.parse_state = act-1;
        debug_shift(cur_token);
        stack.push(cur_token);
        tos++;

        /* advance to the next Symbol */
        cur_token = scan();
        debug_message(5, “# Current token is #” + cur_token.sym);
      }
    /* if its less than zero, then it encodes a reduce action */
    else if (act < 0)       {         /* perform the action for the reduce */         lhs_sym = do_action((-act)-1, this, stack, tos);         /* look up information about the production */         lhs_sym_num = production_tab[(-act)-1][0];         handle_size = production_tab[(-act)-1][1];         debug_reduce((-act)-1, lhs_sym_num, handle_size);         /* pop the handle off the stack */         for (int i = 0; i < handle_size; i++)           {         stack.pop();         tos--;           }                    /* look up the state to go to from the one popped back to */         act = get_reduce(((Symbol)stack.peek()).parse_state, lhs_sym_num);         /* shift to that state */         lhs_sym.parse_state = act;         stack.push(lhs_sym);         tos++;         debug_message(5, "# Goto state #" + act);       }     /* finally if the entry is zero, we have an error */     else if (act == 0)       {         /* call user syntax error reporting routine */         syntax_error(cur_token);         /* try to error recover */         if (!error_recovery(true))           {         /* if that fails give up with a fatal syntax error */         unrecovered_syntax_error(cur_token);         /* just in case that wasn't fatal enough, end parse */         done_parsing();           } else {         lhs_sym = (Symbol)stack.peek();           }       }       }     return lhs_sym;   }   CUP$Parser$actions getActionObject () {     return action_obj;   }   /**    * Get the graph resulting from the parsing operations.    *    * @return the graph generated from the input.    */   public Graph getGraph () {     return action_obj.graph;   } } /** Cup generated class to encapsulate user supplied action code.*/ class CUP$Parser$actions {   // a list of variables used in action code during grammar translation   //Parser parser = null;   Subgraph rootSubgraph;   Subgraph lastSubgraph;   Graph    graph;   Subgraph thisGraph;   Node     thisNode;   Edge     thisEdge;   Node     fromNode;   Node     toNode;   String      portName = null;   String      toPortName;   String      fromPortName;   int         thisAttrType;   int         thisElemType;   boolean directed = true;   String      graphType;   private int anon_id = 0;   Vector attrs = new Vector(8,4);   Vector nodes = new Vector(8,4);   Vector edges = new Vector(8,4);   void appendAttr(String name, String value) {     attrs.addElement(new Attribute(thisElemType,name,value));   }   void noMacros() {     parser.report_error("attribute macros are not supported yet", null);   }   void attrStmt(int kind, String macroName) {     if(macroName != null) {       noMacros();       return;     }     if(attrs.size() == 0) return;     Attribute attr = null;     for(int i = 0; i < attrs.size(); i++) {       if((attr = (Attribute)(attrs.elementAt(i))).getValue() == null) {     // null means to not attach the attribute to an element     continue;       } else {     switch(kind) {     case Grappa.NODE:       parser.debug_message(1, "adding node default attr (" + attr.getName() + ") to thisGraph(" + thisGraph.getName() + ")");       thisGraph.setNodeAttribute(attr);       break;     case Grappa.EDGE:       parser.debug_message(1, "adding edge default attr (" + attr.getName() + ") to thisGraph(" + thisGraph.getName() + ")");       thisGraph.setEdgeAttribute(attr);       break;     case Grappa.SUBGRAPH:       parser.debug_message(1, "adding subg default attr (" + attr.getName() + ") to thisGraph(" + thisGraph.getName() + ")");       thisGraph.setAttribute(attr);       break;     }       }     }     attrs.removeAllElements();   }   void startGraph(String name, boolean type, boolean strict) {     if(graph == null) {       graph = new Graph(name, type, strict);     } else {       graph.reset(name, type, strict);     }     directed = type;     rootSubgraph = (Subgraph)graph;     parser.debug_message(1, "Creating top level graph (" + name + ")");     anon_id = 0;   }   void openGraph() {     thisGraph = rootSubgraph;     thisElemType = Grappa.SUBGRAPH;     parser.debug_message(1, "thisGraph(" + thisGraph.getName() + ")");   }   void closeGraph() {     int level = 1;     if(parser.getErrorWriter() != null && parser.getDebugLevel() >= level) {
               
      parser.debug_message(level, “parsed graph follows:”);
      rootSubgraph.printSubgraph(parser.getErrorWriter());
    }
  }

  void openSubg(String name) {
    thisGraph = new Subgraph(thisGraph, name);
    parser.debug_message(1, “thisGraph(” + thisGraph.getName() + “)”);
    thisElemType = Grappa.SUBGRAPH;
  }

  String anonStr() {
    return Grappa.ANONYMOUS_PREFIX + anon_id++;
  }

  void closeSubg() {
    lastSubgraph = thisGraph;
    // getSubgraph() gets the parent subgraph
    thisGraph = thisGraph.getSubgraph();
    if(thisGraph == null) {
      parser.report_error (“parser attempted to go above root Subgraph”, null);
      thisGraph = rootSubgraph;
    }
    parser.debug_message(1, “Created subgraph (” + lastSubgraph.getName() + “) in subgraph (” + thisGraph.getName() + “)…”);
    parser.debug_message(1, “thisGraph(” + thisGraph.getName() + “)”);
  }

  void appendNode(String name, String port) {
    if((thisNode = rootSubgraph.findNodeByName(name)) == null) {
      parser.debug_message(1, “Creating node in subgraph (” + thisGraph.getName() + “)…”);
      thisNode = new Node(thisGraph, name);
    } else {
      parser.debug_message(1, “Node already in subgraph (” + thisNode.getSubgraph().getName() + “)…”);
    }
    Object[] pair = new Object[2];
    pair[0] = thisNode;
    pair[1] = port;
    nodes.addElement(pair);
    parser.debug_message(1, “thisNode(” + thisNode.getName() + “)”);
    thisElemType = Grappa.NODE;
  }

  void nodeWrap() {
    Object[] pair = null;
    if(nodes.size() > 0 && attrs.size() > 0) {
      for(int i = 0; i < nodes.size(); i++) {     pair = (Object[])(nodes.elementAt(i));     applyAttrs((Element)pair[0],null,null);       }     }     attrs.removeAllElements();     nodes.removeAllElements();   }   void bufferEdges() {     Object[] pair = new Object[2];     if(nodes.size() > 0) {
      pair[0] = nodes;
      nodes = new Vector(8,4);
      pair[1] = new Boolean(true);
    } else if(lastSubgraph != null) {
      pair[0] = lastSubgraph;
      lastSubgraph = null;
      pair[1] = new Boolean(false);
    } else {
      parser.report_error (“EDGE_OP without clear antecedent nodelist or subgraph”, null);
      return;
    }
    edges.addElement(pair);
  }

  void edgeWrap() {
    bufferEdges();
    Attribute key = null;
    Attribute name = null;
    Attribute attr = null;
    int skip = -1;
    for(int i = 0; i < attrs.size(); i++) {       attr = (Attribute)(attrs.elementAt(i));       if(attr.getName().equals("key")) {     key = attr;     if(name != null)         break;       } else if(attr.getName().equals("__nAmE__")) {     name = attr;     if(key != null)         break;       }     }     Object[] tailPair = (Object[])(edges.elementAt(0));     Object[] headPair = null;     // note: when node list is used, a non-null name will cause errors     //       due to lack of uniqueness     for(int i = 1; i < edges.size(); i++) {       headPair = (Object[])(edges.elementAt(i));       if(((Boolean)(tailPair[1])).booleanValue()) { // true if node list     Vector list = (Vector)(tailPair[0]);     Object[] nodePair = null;     for(int j = 0; j < list.size(); j++) {       nodePair = (Object[])(list.elementAt(j));       edgeRHS((Node)(nodePair[0]),(String)(nodePair[1]),headPair,key,name);     }     list.removeAllElements();       } else {     Subgraph subg = (Subgraph)(tailPair[0]);     Enumeration enm = subg.elements(Grappa.NODE);     while(enm.hasMoreElements()) {       edgeRHS((Node)(enm.nextElement()),null,headPair,key,name);     }       }       tailPair = headPair;     }     edges.removeAllElements();     attrs.removeAllElements();   }   void edgeRHS(Node tail, String tailPort, Object[] headPair, Attribute keyAttr, Attribute nameAttr) {     String key = (keyAttr == null) ? null : keyAttr.getStringValue();     String name = (nameAttr == null) ? null : nameAttr.getStringValue();     if(((Boolean)(headPair[1])).booleanValue()) { // true if node list       Vector list = (Vector)(headPair[0]);       Object[] nodePair = null;       for(int j = 0; j < list.size(); j++) {     nodePair = (Object[])(list.elementAt(j));     thisEdge = new Edge(thisGraph, tail, tailPort, (Node)(nodePair[0]), (String)(nodePair[1]), key, name);     parser.debug_message(1, "Creating edge in subgraph (" + thisGraph.getName() + ")...");     parser.debug_message(1, "thisEdge(" + thisEdge.getName() + ")");     thisElemType = Grappa.EDGE;     applyAttrs((Element)thisEdge,keyAttr,nameAttr);       }     } else {       Subgraph subg = (Subgraph)(headPair[0]);       Enumeration enm = subg.elements(Grappa.NODE);       while(enm.hasMoreElements()) {     thisEdge = new Edge(thisGraph, tail, tailPort, (Node)(enm.nextElement()), null, key, name);     parser.debug_message(1, "Creating edge in subgraph (" + thisGraph.getName() + ")...");     parser.debug_message(1, "thisEdge(" + thisEdge.getName() + ")");     thisElemType = Grappa.EDGE;     applyAttrs((Element)thisEdge,keyAttr,nameAttr);       }     }   }   void applyAttrs(Element elem, Attribute skip1, Attribute skip2) {     Attribute attr = null;     for(int i = 0; i < attrs.size(); i++) {       attr = (Attribute)attrs.elementAt(i);       if(attr == skip1) continue;       else if(attr == skip2) continue;       elem.setAttribute(attr);     }   }   private final Parser parser;   /** Constructor */   CUP$Parser$actions(Parser parser) {     this.parser = parser;   }   /** Method with the actual generated action code. */   public final java_cup.runtime.Symbol CUP$Parser$do_action(     int                        CUP$Parser$act_num,     java_cup.runtime.lr_parser CUP$Parser$parser,     java.util.Stack            CUP$Parser$stack,     int                        CUP$Parser$top)     throws java.lang.Exception     {       /* Symbol object for return from actions */       java_cup.runtime.Symbol CUP$Parser$result;       /* select the action based on the action number */       switch (CUP$Parser$act_num)         {           /*. . . . . . . . . . . . . . . . . . . .*/           case 62: // optSeparator ::=              {               Object RESULT = null;               CUP$Parser$result = new java_cup.runtime.Symbol(17/*optSeparator*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 61: // optSeparator ::= COMMA              {               Object RESULT = null;               CUP$Parser$result = new java_cup.runtime.Symbol(17/*optSeparator*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 60: // optSeparator ::= SEMI              {               Object RESULT = null;               CUP$Parser$result = new java_cup.runtime.Symbol(17/*optSeparator*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 59: // optSemi ::=              {               Object RESULT = null;               CUP$Parser$result = new java_cup.runtime.Symbol(16/*optSemi*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 58: // optSemi ::= SEMI              {               Object RESULT = null;               CUP$Parser$result = new java_cup.runtime.Symbol(16/*optSemi*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 57: // optSubgHdr ::=              {               String RESULT = null;                           RESULT = anonStr();                               CUP$Parser$result = new java_cup.runtime.Symbol(5/*optSubgHdr*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 56: // optSubgHdr ::= SUBGRAPH              {               String RESULT = null;                           RESULT = anonStr();                               CUP$Parser$result = new java_cup.runtime.Symbol(5/*optSubgHdr*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 55: // optSubgHdr ::= SUBGRAPH ATOM              {               String RESULT = null;         int valleft = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left;         int valright = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right;         String val = (String)((java_cup.runtime.Symbol) CUP$Parser$stack.elementAt(CUP$Parser$top-0)).value;                           RESULT = val;                               CUP$Parser$result = new java_cup.runtime.Symbol(5/*optSubgHdr*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-1)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 54: // subgraph ::= optSubgHdr NT$2 body              {               Object RESULT = null;               // propagate RESULT from NT$2               if ( ((java_cup.runtime.Symbol) CUP$Parser$stack.elementAt(CUP$Parser$top-1)).value != null )                 RESULT = (Object) ((java_cup.runtime.Symbol) CUP$Parser$stack.elementAt(CUP$Parser$top-1)).value;         int valleft = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-2)).left;         int valright = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-2)).right;         String val = (String)((java_cup.runtime.Symbol) CUP$Parser$stack.elementAt(CUP$Parser$top-2)).value;                         closeSubg();                             CUP$Parser$result = new java_cup.runtime.Symbol(22/*subgraph*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-2)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 53: // NT$2 ::=              {               Object RESULT = null;         int valleft = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left;         int valright = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right;         String val = (String)((java_cup.runtime.Symbol) CUP$Parser$stack.elementAt(CUP$Parser$top-0)).value;                openSubg(val);                             CUP$Parser$result = new java_cup.runtime.Symbol(34/*NT$2*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 52: // graphAttrDefs ::= attrAssignment              {               Object RESULT = null;               CUP$Parser$result = new java_cup.runtime.Symbol(25/*graphAttrDefs*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 51: // attrMacro ::= ATSIGN ATOM              {               Object RESULT = null;         int nameleft = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left;         int nameright = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right;         String name = (String)((java_cup.runtime.Symbol) CUP$Parser$stack.elementAt(CUP$Parser$top-0)).value;                          appendAttr(name,null);                              CUP$Parser$result = new java_cup.runtime.Symbol(30/*attrMacro*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-1)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 50: // attrAssignment ::= ATOM EQUAL ATOM              {               Object RESULT = null;         int nameleft = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-2)).left;         int nameright = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-2)).right;         String name = (String)((java_cup.runtime.Symbol) CUP$Parser$stack.elementAt(CUP$Parser$top-2)).value;         int valueleft = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left;         int valueright = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right;         String value = (String)((java_cup.runtime.Symbol) CUP$Parser$stack.elementAt(CUP$Parser$top-0)).value;                               appendAttr(name,value);                                   CUP$Parser$result = new java_cup.runtime.Symbol(29/*attrAssignment*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-2)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 49: // attrItem ::= attrMacro              {               Object RESULT = null;               CUP$Parser$result = new java_cup.runtime.Symbol(28/*attrItem*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 48: // attrItem ::= attrAssignment              {               Object RESULT = null;               CUP$Parser$result = new java_cup.runtime.Symbol(28/*attrItem*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 47: // attrDefs ::= attrDefs optSeparator attrItem              {               Object RESULT = null;               CUP$Parser$result = new java_cup.runtime.Symbol(27/*attrDefs*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-2)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 46: // attrDefs ::= attrItem              {               Object RESULT = null;               CUP$Parser$result = new java_cup.runtime.Symbol(27/*attrDefs*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 45: // optAttrDefs ::=              {               Object RESULT = null;               CUP$Parser$result = new java_cup.runtime.Symbol(26/*optAttrDefs*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 44: // optAttrDefs ::= attrDefs              {               Object RESULT = null;               CUP$Parser$result = new java_cup.runtime.Symbol(26/*optAttrDefs*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 43: // attrList ::= optAttr LBR optAttrDefs RBR              {               Object RESULT = null;               CUP$Parser$result = new java_cup.runtime.Symbol(24/*attrList*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-3)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 42: // optAttr ::=              {               Object RESULT = null;               CUP$Parser$result = new java_cup.runtime.Symbol(20/*optAttr*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 41: // optAttr ::= attrList              {               Object RESULT = null;               CUP$Parser$result = new java_cup.runtime.Symbol(20/*optAttr*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 40: // optMacroName ::=              {               String RESULT = null;                             RESULT = null;                                 CUP$Parser$result = new java_cup.runtime.Symbol(7/*optMacroName*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 39: // optMacroName ::= ATOM EQUAL              {               String RESULT = null;         int valleft = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-1)).left;         int valright = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-1)).right;         String val = (String)((java_cup.runtime.Symbol) CUP$Parser$stack.elementAt(CUP$Parser$top-1)).value;                             RESULT = val;                                 CUP$Parser$result = new java_cup.runtime.Symbol(7/*optMacroName*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-1)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 38: // attrType ::= EDGE              {               Integer RESULT = null;         int valleft = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left;         int valright = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right;         Integer val = (Integer)((java_cup.runtime.Symbol) CUP$Parser$stack.elementAt(CUP$Parser$top-0)).value;                         RESULT = new Integer(Grappa.EDGE);                             CUP$Parser$result = new java_cup.runtime.Symbol(4/*attrType*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 37: // attrType ::= NODE              {               Integer RESULT = null;         int valleft = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left;         int valright = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right;         Integer val = (Integer)((java_cup.runtime.Symbol) CUP$Parser$stack.elementAt(CUP$Parser$top-0)).value;                         RESULT = new Integer(Grappa.NODE);                             CUP$Parser$result = new java_cup.runtime.Symbol(4/*attrType*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 36: // attrType ::= GRAPH              {               Integer RESULT = null;         int valleft = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left;         int valright = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right;         Integer val = (Integer)((java_cup.runtime.Symbol) CUP$Parser$stack.elementAt(CUP$Parser$top-0)).value;                         RESULT = new Integer(Grappa.SUBGRAPH);                             CUP$Parser$result = new java_cup.runtime.Symbol(4/*attrType*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 35: // attrStmt ::= graphAttrDefs              {               Object RESULT = null;                         attrStmt(Grappa.SUBGRAPH,null);                             CUP$Parser$result = new java_cup.runtime.Symbol(15/*attrStmt*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 34: // attrStmt ::= attrType optMacroName attrList              {               Object RESULT = null;         int typeleft = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-2)).left;         int typeright = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-2)).right;         Integer type = (Integer)((java_cup.runtime.Symbol) CUP$Parser$stack.elementAt(CUP$Parser$top-2)).value;         int nameleft = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-1)).left;         int nameright = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-1)).right;         String name = (String)((java_cup.runtime.Symbol) CUP$Parser$stack.elementAt(CUP$Parser$top-1)).value;                         attrStmt(type.intValue(),name);                             CUP$Parser$result = new java_cup.runtime.Symbol(15/*attrStmt*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-2)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 33: // optPort ::=              {               String RESULT = null;                        RESULT = null;                            CUP$Parser$result = new java_cup.runtime.Symbol(8/*optPort*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 32: // optPort ::= COLON ATOM              {               String RESULT = null;         int valleft = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left;         int valright = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right;         String val = (String)((java_cup.runtime.Symbol) CUP$Parser$stack.elementAt(CUP$Parser$top-0)).value;                        RESULT = val;                            CUP$Parser$result = new java_cup.runtime.Symbol(8/*optPort*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-1)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 31: // node ::= ATOM optPort              {               Object RESULT = null;         int nameleft = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-1)).left;         int nameright = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-1)).right;         String name = (String)((java_cup.runtime.Symbol) CUP$Parser$stack.elementAt(CUP$Parser$top-1)).value;         int portleft = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left;         int portright = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right;         String port = (String)((java_cup.runtime.Symbol) CUP$Parser$stack.elementAt(CUP$Parser$top-0)).value;                     appendNode(name,port);                         CUP$Parser$result = new java_cup.runtime.Symbol(23/*node*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-1)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 30: // nodeList ::= nodeList COMMA node              {               Object RESULT = null;               CUP$Parser$result = new java_cup.runtime.Symbol(21/*nodeList*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-2)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 29: // nodeList ::= node              {               Object RESULT = null;               CUP$Parser$result = new java_cup.runtime.Symbol(21/*nodeList*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 28: // rCompound ::=              {               Boolean RESULT = null;                  thisElemType = Grappa.NODE;                 RESULT = new Boolean(false);                              CUP$Parser$result = new java_cup.runtime.Symbol(3/*rCompound*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 27: // rCompound ::= edge_op NT$1 simple rCompound              {               Boolean RESULT = null;               // propagate RESULT from NT$1               if ( ((java_cup.runtime.Symbol) CUP$Parser$stack.elementAt(CUP$Parser$top-2)).value != null )                 RESULT = (Boolean) ((java_cup.runtime.Symbol) CUP$Parser$stack.elementAt(CUP$Parser$top-2)).value;         int valleft = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left;         int valright = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right;         Boolean val = (Boolean)((java_cup.runtime.Symbol) CUP$Parser$stack.elementAt(CUP$Parser$top-0)).value;                  thisElemType = Grappa.EDGE;                 RESULT = new Boolean(true);                              CUP$Parser$result = new java_cup.runtime.Symbol(3/*rCompound*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-3)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 26: // NT$1 ::=              {               Object RESULT = null;         thisElemType = Grappa.EDGE;                 bufferEdges();                              CUP$Parser$result = new java_cup.runtime.Symbol(33/*NT$1*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 25: // edge_op ::= ND_EDGE_OP              {               Object RESULT = null;                    if(directed) {         //CUP$parser.report_error ("attempt to create a non-directed edge in a directed graph",null);         parser.report_error ("attempt to create a non-directed edge in a directed graph",null);           }                        CUP$Parser$result = new java_cup.runtime.Symbol(31/*edge_op*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 24: // edge_op ::= D_EDGE_OP              {               Object RESULT = null;                    if(!directed) {         //CUP$parser.report_error ("attempt to create a directed edge in a non-directed graph",null);         parser.report_error ("attempt to create a directed edge in a non-directed graph",null);           }                        CUP$Parser$result = new java_cup.runtime.Symbol(31/*edge_op*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 23: // simple ::= subgraph              {               Object RESULT = null;               CUP$Parser$result = new java_cup.runtime.Symbol(19/*simple*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 22: // simple ::= nodeList              {               Object RESULT = null;               CUP$Parser$result = new java_cup.runtime.Symbol(19/*simple*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 21: // compound ::= simple rCompound optAttr              {               Object RESULT = null;         int valleft = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-1)).left;         int valright = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-1)).right;         Boolean val = (Boolean)((java_cup.runtime.Symbol) CUP$Parser$stack.elementAt(CUP$Parser$top-1)).value;                         if (val.booleanValue()) edgeWrap();                else nodeWrap();                             CUP$Parser$result = new java_cup.runtime.Symbol(18/*compound*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-2)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 20: // stmt ::= compound optSemi              {               Object RESULT = null;               CUP$Parser$result = new java_cup.runtime.Symbol(14/*stmt*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-1)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 19: // stmt ::= attrStmt optSemi              {               Object RESULT = null;               CUP$Parser$result = new java_cup.runtime.Symbol(14/*stmt*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-1)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 18: // stmtList ::= stmt              {               Object RESULT = null;               CUP$Parser$result = new java_cup.runtime.Symbol(13/*stmtList*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 17: // stmtList ::= stmtList stmt              {               Object RESULT = null;               CUP$Parser$result = new java_cup.runtime.Symbol(13/*stmtList*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-1)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 16: // optStmtList ::=              {               Object RESULT = null;               CUP$Parser$result = new java_cup.runtime.Symbol(12/*optStmtList*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 15: // optStmtList ::= stmtList              {               Object RESULT = null;               CUP$Parser$result = new java_cup.runtime.Symbol(12/*optStmtList*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 14: // body ::= LCUR optStmtList RCUR              {               Object RESULT = null;               CUP$Parser$result = new java_cup.runtime.Symbol(11/*body*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-2)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 13: // optGraphName ::=              {               String RESULT = null;                             RESULT = anonStr();                                 CUP$Parser$result = new java_cup.runtime.Symbol(6/*optGraphName*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 12: // optGraphName ::= ATOM              {               String RESULT = null;         int valleft = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left;         int valright = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right;         String val = (String)((java_cup.runtime.Symbol) CUP$Parser$stack.elementAt(CUP$Parser$top-0)).value;                             RESULT = val;                                 CUP$Parser$result = new java_cup.runtime.Symbol(6/*optGraphName*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 11: // graphType ::= DIGRAPH              {               Boolean RESULT = null;                          RESULT = new Boolean(true);                              CUP$Parser$result = new java_cup.runtime.Symbol(2/*graphType*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 10: // graphType ::= GRAPH              {               Boolean RESULT = null;                          RESULT = new Boolean(false);                              CUP$Parser$result = new java_cup.runtime.Symbol(2/*graphType*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 9: // optStrict ::=              {               Boolean RESULT = null;                          RESULT = new Boolean(false);                              CUP$Parser$result = new java_cup.runtime.Symbol(1/*optStrict*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 8: // optStrict ::= STRICT              {               Boolean RESULT = null;                          RESULT = new Boolean(true);                              CUP$Parser$result = new java_cup.runtime.Symbol(1/*optStrict*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 7: // hdr ::= STRICTDIGRAPH optGraphName              {               Object RESULT = null;         int nameleft = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left;         int nameright = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right;         String name = (String)((java_cup.runtime.Symbol) CUP$Parser$stack.elementAt(CUP$Parser$top-0)).value;                    startGraph(name,true,true);                        CUP$Parser$result = new java_cup.runtime.Symbol(10/*hdr*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-1)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 6: // hdr ::= STRICTGRAPH optGraphName              {               Object RESULT = null;         int nameleft = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left;         int nameright = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right;         String name = (String)((java_cup.runtime.Symbol) CUP$Parser$stack.elementAt(CUP$Parser$top-0)).value;                    startGraph(name,true,false);                        CUP$Parser$result = new java_cup.runtime.Symbol(10/*hdr*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-1)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 5: // hdr ::= optStrict graphType optGraphName              {               Object RESULT = null;         int strictleft = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-2)).left;         int strictright = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-2)).right;         Boolean strict = (Boolean)((java_cup.runtime.Symbol) CUP$Parser$stack.elementAt(CUP$Parser$top-2)).value;         int typeleft = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-1)).left;         int typeright = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-1)).right;         Boolean type = (Boolean)((java_cup.runtime.Symbol) CUP$Parser$stack.elementAt(CUP$Parser$top-1)).value;         int nameleft = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left;         int nameright = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right;         String name = (String)((java_cup.runtime.Symbol) CUP$Parser$stack.elementAt(CUP$Parser$top-0)).value;                    startGraph(name,type.booleanValue(),strict.booleanValue());                        CUP$Parser$result = new java_cup.runtime.Symbol(10/*hdr*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-2)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 4: // graph ::=              {               Object RESULT = null;                       graph = new Graph("empty");              //((Parser)(CUP$parser)).report_warning ("The graph to parse is empty.", null);              ((Parser)(parser)).report_warning ("The graph to parse is empty.", null);                          CUP$Parser$result = new java_cup.runtime.Symbol(9/*graph*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 3: // graph ::= error              {               Object RESULT = null;         int valleft = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left;         int valright = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right;         Object val = (Object)((java_cup.runtime.Symbol) CUP$Parser$stack.elementAt(CUP$Parser$top-0)).value;                  //CUP$parser.report_error ("An error was encountered while graph parsing (" + val.toString() + ").", null);         parser.report_error ("An error was encountered while graph parsing (" + val.toString() + ").", null);                          CUP$Parser$result = new java_cup.runtime.Symbol(9/*graph*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 2: // graph ::= hdr NT$0 body              {               Object RESULT = null;               // propagate RESULT from NT$0               if ( ((java_cup.runtime.Symbol) CUP$Parser$stack.elementAt(CUP$Parser$top-1)).value != null )                 RESULT = (Object) ((java_cup.runtime.Symbol) CUP$Parser$stack.elementAt(CUP$Parser$top-1)).value;                  closeGraph();                          CUP$Parser$result = new java_cup.runtime.Symbol(9/*graph*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-2)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 1: // NT$0 ::=              {               Object RESULT = null;         openGraph();                          CUP$Parser$result = new java_cup.runtime.Symbol(32/*NT$0*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           return CUP$Parser$result;           /*. . . . . . . . . . . . . . . . . . . .*/           case 0: // $START ::= graph EOF              {               Object RESULT = null;         int start_valleft = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-1)).left;         int start_valright = ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-1)).right;         Object start_val = (Object)((java_cup.runtime.Symbol) CUP$Parser$stack.elementAt(CUP$Parser$top-1)).value;         RESULT = start_val;               CUP$Parser$result = new java_cup.runtime.Symbol(0/*$START*/, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-1)).left, ((java_cup.runtime.Symbol)CUP$Parser$stack.elementAt(CUP$Parser$top-0)).right, RESULT);             }           /* ACCEPT */           CUP$Parser$parser.done_parsing();           return CUP$Parser$result;           /* . . . . . .*/           default:             throw new Exception(                "Invalid action number found in internal parse table");         }     } } att/grappa/Subgraph.java att/grappa/Subgraph.java/*  *  This software may only be used by you under license from AT&T Corp.  *  ("AT&T").  A copy of AT&T's Source Code Agreement is available at  *  AT&T's Internet website having the URL:  *  
 *  If you received this software without first entering into a license
 *  with AT&T, you have an infringing copy of this software and cannot use
 *  it without violating AT&T’s intellectual property rights.
 */

package att.grappa;

import java.io.*;
import java.util.*;

/**
 * This class describes a subgraph, which can consist of
 * nodes, edges and other subgraphs.  Note: The topmost
 * or root subgraph is the entire graph (the Graph object), which is
 * an extension of this class.
 *
 * @version 1.2, 26 May 2005; Copyright 1996 – 2005 by AT&T Corp.
 * @author  John Mocenigo, Research @ AT&T Labs
 * @see Graph
 */
public class Subgraph extends Element
    implements Comparator
{
    /**
     * Default graph name prefix used by setName().
     *
     * @see Subgraph#setName()
     */
    public final static String defaultNamePrefix = “G”;

    // node, edge and graph dictionaries for this subgraph
    private Hashtable nodedict = null;
    private Hashtable edgedict = null;
    private Hashtable graphdict = null;

    // indicators for dislaying element labels when drawing
    private boolean nodeLabels = true;
    private boolean edgeLabels = true;
    private boolean subgLabels = true;

    // default node attributes
    private Hashtable nodeAttributes = null;

    // default edge attributes
    private Hashtable edgeAttributes = null;

    // for cluster subgraphs
    private boolean cluster = false;

    /**
     * Reference to the current selection (or vector of selections).
     * Normally set and used by a GrappaAdapter.
     */
    public Object currentSelection = null;

    /**
     * This constructor is needed by the Graph constructor
     */
    Subgraph() {
    //super();
    cluster = true; // the root is a cluster subgraph

    subgraphAttrsOfInterest();
    }

    /**
     * Use this constructor when creating a subgraph within a subgraph.
     *
     * @param subg the parent subgraph.
     * @param name the name of this subgraph.
     */
    public Subgraph(Subgraph subg, String name) {
    super(SUBGRAPH,subg);
    setName(name);

    Enumeration enm = subg.getNodeAttributePairs();
    while(enm.hasMoreElements()) {
        setNodeAttribute((Attribute)enm.nextElement());
    }
    enm = subg.getEdgeAttributePairs();
    while(enm.hasMoreElements()) {
        setEdgeAttribute((Attribute)enm.nextElement());
    }
    enm = subg.getLocalAttributePairs();
    while(enm.hasMoreElements()) {
        setAttribute((Attribute)enm.nextElement());
    }

    subgraphAttrsOfInterest();
    }

    /**
     * Use this constructor when creating a subgraph within a subgraph
     * with an automatically generated name.
     *
     * @param subg the parent subgraph.
     * @see Subgraph#setName()
     */
    public Subgraph(Subgraph subg) {
    this(subg,(String)(null));
    }

    // a listing of the attributes of interest for Subgraphs
    private void subgraphAttrsOfInterest() {
    //attrOfInterest(BBOX_ATTR);
    attrOfInterest(MINBOX_ATTR);
    attrOfInterest(MINSIZE_ATTR);
    attrOfInterest(LABEL_ATTR);
    attrOfInterest(LP_ATTR);
    attrOfInterest(STYLE_ATTR);
    }

    /**
     * Check if this element is a subgraph.
     * Useful for testing the subclass type of a Element object.
     *
     * @return true if this object is a Subgraph.
     */
    public boolean isSubgraph() {
    return(true);
    }

    /**
     * Get the type of this element.
     * Useful for distinguishing Element objects.
     *
     * @return the class variable constant SUBGRAPH.
     * @see GrappaConstants#SUBGRAPH
     */
    public int getType() {
    return(SUBGRAPH);
    }

    /**
     * Generates and sets the name for this subgraph.
     * The generated name is the concatenation of the
     * Subgraph.defaultNamePrefix with the numeric
     * id of this subgraph Instance.
     * Implements the abstract Element method.
     *
     * @see Element#getId()
     */
    void setName() {
    String oldName = name;
    
    while(true) {
        name = Subgraph.defaultNamePrefix + getId() + “_” + System.currentTimeMillis();
        if(getGraph().findSubgraphByName(name) == null) {
        break;
        }
    }

    // update subgraph graph dictionary
    if(getSubgraph() != null) {
        if(oldName != null) {
        getSubgraph().removeSubgraph(oldName);
        }
        getSubgraph().addSubgraph(this);
    }

    canonName = null;
    }

    /**
     * Sets the subgraph name to a copy of the supplied argument.
     * When the argument is null, setName() is called.
     * When the name is not unique or when the name has the same format as
     * that generated by setName(), a IllegalArgumentException is thrown.
     *
     * @param newName the new name for the subgraph.
     * @see Subgraph#setName()
     */
    public void setName(String newName) throws IllegalArgumentException {
    if(newName == null) {
        setName();
        return;
    }

    String oldName = name;

    // test if the new name is the same as the old name (if any)
    if(oldName != null && oldName.equals(newName)) {
        return;
    }

    // is name unique?
    if(getGraph().findSubgraphByName(newName) != null) {
        throw new IllegalArgumentException(“graph name (” + newName + “) is not unique”);
    }
    name = newName;

    if(name.startsWith(“cluster”)) {
        cluster = true;
    }

    // update subgraph graph dictionary
    if(getSubgraph() != null) {
        if(oldName != null) {
        getSubgraph().removeSubgraph(oldName);
        }
        getSubgraph().addSubgraph(this);
    }

    canonName = null;
    }

    /**
     * Check if the subgraph is a cluster subgraph.
     *
     * @return true, if the graph is a cluster subgraph.
     */
    public boolean isCluster() {
    return cluster;
    }

    /**
     * Check if the subgraph is the root of the graph.
     *
     * @return true, if the graph is the root of the graph.
     */
    public boolean isRoot() {
    return (this == (Subgraph)getGraph());
    }

    /**
     * Gets the subgraph-specific default attribute for the named node attribute.
     *
     * @param key the name of the node attribute pair to be retrieved.
     * @return the requested attribute pair or null if not found.
     */
    public Attribute getNodeAttribute(String key) {
    if(nodeAttributes == null) return(null);
    return((Attribute)(nodeAttributes.get(key)));
    }

    /**
     * Gets the subgraph-specific default value for the named node attribute.
     *
     * @param key the name of the node attribute pair to be retrieved.
     * @return the requested attribute value or null if not found.
     */
    public Object getNodeAttributeValue(String key) {
    Attribute attr;
    if(nodeAttributes == null) return(null);
    if((attr = (Attribute)(nodeAttributes.get(key))) == null) return(null);
    return(attr.getValue());
    }

    /**
     * Gets an enumeration of the subgraph-specific node attribute keys
     *
     * @return an enumeration of String objects.
     */
    public Enumeration getNodeAttributeKeys() {
    if(nodeAttributes == null) return Grappa.emptyEnumeration.elements();
    return(nodeAttributes.keys());
    }

    /**
     * Gets an enumeration of the subgraph-specific node attributes
     *
     * @return an enumeration of Attribute objects.
     */
    public Enumeration getNodeAttributePairs() {
    if(nodeAttributes == null) return Grappa.emptyEnumeration.elements();
    return(nodeAttributes.elements());
    }

    /**
     * Sets the subgraph-specific default for the specified node attribute.
     * If the attribute is not from the parent subgraph, then
     * setNodeAttribute(attr.getName(), attr.getValue()) is called.
     *
     * @param attr the node Attribute object to set as a default.
     * @return the Attribute object previously stored for this attribute, if any.
     * @see Subgraph#setNodeAttribute(java.lang.String, java.lang.String)
     */
    public Object setNodeAttribute(Attribute attr) {
    if(attr == null) return null;
    if(nodeAttributes == null) {
        nodeAttributes = new Hashtable();
    }
    // check to see if attr is being passed down the subgraph chain
    Subgraph sg = getSubgraph();
    Attribute prntAttr = (sg==null)?null:sg.getNodeAttribute(attr.getName());
    if(attr != prntAttr) {
        // it’s not, so use the other method;
        // use getStringValue to make sure value is treated properly
        // when converted to an Object
        return setNodeAttribute(attr.getName(),attr.getStringValue());
    }
    Object oldValue = null;
    Attribute newAttr = null;
    Attribute crntAttr =  getNodeAttribute(attr.getName());
    if(attr == crntAttr) return attr.getValue();
    if(crntAttr == null) {
        if(attr.getValue() == null) {
        return null;
        }
        nodeAttributes.put(attr.getName(),crntAttr = attr);
        //System.err.println(“Adding passthru1 node attr(“+attr.getName()+”,”+attr.getValue()+”) to “+getName());
        // it’s a pass down, so no need to set observers
    } else {
        oldValue = crntAttr.getValue();
        crntAttr.setChanged(); // so notifyObservers is sure to be called
        // it’s a pass down, so pass it down
        nodeAttributes.put(attr.getName(),attr);
        //System.err.println(“Adding passthru2 node attr(“+attr.getName()+”,”+attr.getValue()+”) to “+getName());
        // this is why we need notifyObservers called
        newAttr = attr;
    }
    // this should only be possible when “else” above has occurred
    if(crntAttr.hasChanged()) {
        crntAttr.notifyObservers(new Object[] { newAttr, new Long(System.currentTimeMillis()) });
    }
    return oldValue;
    }

    /**
     * Sets the subgraph-specific default using the specified name/value
     * pair.  A new attribute will be created if needed.
     *
     * @param name the node attribute name
     * @param value the node attribute value
     * @return the Attribute object previously stored for this attribute, if any.
     */
    public Object setNodeAttribute(String name, Object value) {
    if(nodeAttributes == null) {
        nodeAttributes = new Hashtable();
    }
    if(name == null) {
        throw new IllegalArgumentException(“cannot set an attribute using a null name”);
    }
    // check to see if this name value is the same as the parent default
    Subgraph sg = getSubgraph();
    Attribute prntAttr = (sg==null)?null:sg.getNodeAttribute(name);
    //if(prntAttr != null && value != null ) {
    //System.err.println(“check new node attr (“+name+”,”+value+”) against (“+prntAttr.getName()+”,”+prntAttr.getValue()+”)”);
    //if(name.equals(prntAttr.getName()) && value.equals(prntAttr.getValue())) {
    // it is, so call other form
    //System.err.println(“set node attr to same as default (“+name+”,”+value+”)”);
    //return setNodeAttribute(prntAttr);
    //}
    //}
    Object oldValue = null;
    Attribute crntAttr =  getNodeAttribute(name);
    if(crntAttr == null || crntAttr == prntAttr) {
        if(value == null) {
        return null;
        }
        nodeAttributes.put(name,(crntAttr = new Attribute(NODE,name,value)));
        // TODO: scan subnodes to see if this attr is of interest and then add it
        //       to observer list, but for now leave it
        //
        //System.err.println(“adding new node attr(“+name+”,”+value+”) to “+getName());
        /*
         * just concerned with subgraphs that share the same default (or null)
         * and nodes that do not have a local attribute
         */
    } else {
        oldValue = crntAttr.getValue();
        if(value == null) {
        if(prntAttr == null) {
            removeNodeAttribute(name);
            return oldValue;
        } else {
            return setNodeAttribute(prntAttr);
        }
        } else {
        crntAttr.setValue(value);
        //System.err.println(“changing node attr(“+name+”,”+value+”) in “+getName());
        }
    }
    if(crntAttr.hasChanged()) {
        crntAttr.notifyObservers(new Long(System.currentTimeMillis()));
    }
    return oldValue;
    }

    /*
     * Remove named default node attribute (assumes there is no default in
     * the subgraph chain).
     *
     * @param name the name of the attribute to remove
     */
    private void removeNodeAttribute(String name) {
    if(name == null || nodeAttributes == null) return;
    //System.err.println(“Remove ‘” + name + “‘ from ” + getName());
    Attribute attr = (Attribute)nodeAttributes.remove(name);
    if(attr == null) return;
    attr.setValue(“”);
    if(attr.hasChanged()) attr.notifyObservers(new Long(System.currentTimeMillis()));
    attr.deleteObservers();
    }

    /**
     * Sets the subgraph-specific default for the specified edge attribute.
     * If the attribute is not from the parent subgraph, then
     * setEdgeAttribute(attr.getName(), attr.getValue()) is called.
     *
     * @param attr the edge attribute pair to set.
     * @return the attribute pair previously stored for this attribute.
     * @see Subgraph#setEdgeAttribute(java.lang.String, java.lang.String)
     */
    public Object setEdgeAttribute(Attribute attr) {
    if(attr == null) return null;
    if(edgeAttributes == null) {
        edgeAttributes = new Hashtable();
    }
    // check to see if attr is being passed down the subgraph chain
    Subgraph sg = getSubgraph();
    Attribute prntAttr = (sg==null)?null:sg.getEdgeAttribute(attr.getName());
    if(attr != prntAttr) {
        // it’s not, so use the other method;
        // use getStringValue to make sure value is treated properly
        // when converted to an Object
        return setEdgeAttribute(attr.getName(),attr.getStringValue());
    }
    Object oldValue = null;
    Attribute newAttr = null;
    Attribute crntAttr = getEdgeAttribute(attr.getName());
    if(attr == crntAttr) return attr.getValue();
    if(crntAttr == null) {
        if(attr.getValue() == null) {
        return null;
        }
        edgeAttributes.put(attr.getName(),crntAttr = attr);
        //System.err.println(“Adding passthru1 edge attr(“+attr.getName()+”,”+attr.getValue()+”) to “+getName());
        // it’s a pass down, so no need to set observers
    } else {
        oldValue = crntAttr.getValue();
        crntAttr.setChanged(); // so notifyObservers is sure to be called
        // it’s a pass down, so pass it down
        edgeAttributes.put(attr.getName(),attr);
        //System.err.println(“Adding passthru2 edge attr(“+attr.getName()+”,”+attr.getValue()+”) to “+getName());
        newAttr = attr;
    }
    // this should only be possible when “else” above has occurred
    if(crntAttr.hasChanged()) {
        crntAttr.notifyObservers(new Object[] { newAttr, new Long(System.currentTimeMillis()) });
    }
    return oldValue;
    }

    /**
     * Sets the subgraph-specific default using the specified name/value
     * pair.  A new attribute will be created if needed.
     *
     * @param name the edge attribute name
     * @param value the edge attribute value
     * @return the attribute pair previously stored for this attribute.
     */
    public Object setEdgeAttribute(String name, Object value) {
    if(edgeAttributes == null) {
        edgeAttributes = new Hashtable();
    }
    if(name == null) {
        throw new IllegalArgumentException(“cannot set an attribute using a null name”);
    }
    // check to see if this name value is the same as the parent default
    Subgraph sg = getSubgraph();
    Attribute prntAttr = (sg==null)?null:sg.getEdgeAttribute(name);
    //if(prntAttr != null && value != null ) {
    //if(name.equals(prntAttr.getName()) && value.equals(prntAttr.getValue())) {
    // it is, so call other form
    //return setEdgeAttribute(prntAttr);
    //}
    //}
    Object oldValue = null;
    Attribute crntAttr =  getEdgeAttribute(name);
    if(crntAttr == null || crntAttr == prntAttr) {
        if(value == null) {
        return null;
        }
        edgeAttributes.put(name,(crntAttr = new Attribute(EDGE,name,value)));
        //System.err.println(“adding new edge attr(“+name+”,”+value+”) to “+getName());
        /*
         * just concerned with subgraphs that share the same default (or null)
         * and edges that do not have a local attribute
         */
    } else {
        oldValue = crntAttr.getValue();
        if(value == null) {
        if(prntAttr == null) {
            removeEdgeAttribute(name);
            return oldValue;
        } else {
            return setEdgeAttribute(prntAttr);
        }
        } else {
        crntAttr.setValue(value);
        //System.err.println(“changing edge attr(“+name+”,”+value+”) in “+getName());
        }
    }
    if(crntAttr.hasChanged()) {
        crntAttr.notifyObservers(new Long(System.currentTimeMillis()));
    }
    return oldValue;
    }

    /*
     * Remove named default edge attribute (assumes there is no default in
     * the subgraph chain).
     *
     * @param name the name of the attribute to remove
     */
    private void removeEdgeAttribute(String name) {
    if(name == null || edgeAttributes == null) return;
    Attribute attr = (Attribute)edgeAttributes.remove(name);
    if(attr == null) return;
    attr.setValue(“”);
    if(attr.hasChanged()) attr.notifyObservers(new Long(System.currentTimeMillis()));
    attr.deleteObservers();
    }

    /**
     * Sets the subgraph-specific default for the specified graph attribute.
     * If the attribute is not from the parent subgraph, then
     * setAttribute(attr.getName(), attr.getValue()) is called.
     * Overrides Element method.
     *
     * @param attr the graph attribute pair to set.
     * @return the attribute pair previously stored for this attribute.
     * @see Subgraph#setAttribute(java.lang.String, java.lang.String)
     */
    public Object setAttribute(Attribute attr) {
    if(attr == null) return null;
    if(attributes == null) {
        attributes = new Hashtable();
    }
    // check to see if attr is being passed down the subgraph chain
    Subgraph sg = getSubgraph();
    Attribute prntAttr = (sg==null)?null:sg.getLocalAttribute(attr.getName());
    if(attr != prntAttr) {
        // it’s not, so use the other method;
        // use getStringValue to make sure value is treated properly
        // when converted to an Object
        return setAttribute(attr.getName(),attr.getStringValue());
    }
    Object oldValue = null;
    Attribute newAttr = null;
    Attribute crntAttr =  getLocalAttribute(attr.getName());
    if(attr == crntAttr) return attr.getValue();
    if(crntAttr == null) {
        if(attr.getValue() == null) {
        return null;
        }
        attributes.put(attr.getName(),crntAttr = attr);
        //System.err.println(“Adding passthru1 graph attr(“+attr.getName()+”,”+attr.getValue()+”) to “+getName());
        // it’s a pass down, so no need to set observers
    } else {
        oldValue = crntAttr.getValue();
        crntAttr.setChanged(); // so notifyObservers is sure to be called
        // it’s a pass down, so pass it down
        attributes.put(attr.getName(),attr);
        //System.err.println(“Adding passthru2 graph attr(“+attr.getName()+”,”+attr.getValue()+”) to “+getName());
        // this is why we need notifyObservers called
        newAttr = attr;
    }
    // this should only be possible when “else” above has occurred
    if(crntAttr.hasChanged()) {
        crntAttr.notifyObservers(new Object[] { newAttr, new Long(System.currentTimeMillis()) });
    }
    return oldValue;
    }

    /**
     * Sets the subgraph-specific default using the specified name/value
     * pair.  A new attribute will be created if needed.
     * Overrides Element method.
     *
     * @param name the graph attribute name
     * @param value the graph attribute value
     * @return the attribute pair previously stored for this attribute.
     */
    public Object setAttribute(String name, Object value) {
    if(attributes == null) {
        attributes = new Hashtable();
    }
    if(name == null) {
        throw new IllegalArgumentException(“cannot set an attribute using a null name”);
    }
    // check to see if this name value is the same as the parent default
    Subgraph sg = getSubgraph();
    Attribute prntAttr = (sg==null)?null:sg.getLocalAttribute(name);
    //if(prntAttr != null && value != null ) {
    //if(name.equals(prntAttr.getName()) && value.equals(prntAttr.getValue())) {
    // it is, so call other form
    //return setAttribute(prntAttr);
    //}
    //}
    Object oldValue = null;
    Attribute crntAttr = getLocalAttribute(name);
    if(crntAttr == null || crntAttr == prntAttr) {
        if(value == null) {
        return null;
        } else if(value instanceof String && ((String)value).trim().length() == 0 && Attribute.attributeType(getType(),name) != STRING_TYPE) {
        return null;
        }
        attributes.put(name,(crntAttr = new Attribute(SUBGRAPH,name,value)));
            if(grappaNexus != null && isOfInterest(name)) {
                crntAttr.addObserver(grappaNexus);
            }

        //System.err.println(“adding new graph attr(“+name+”,”+value+”) to “+getName());
    } else {
        oldValue = crntAttr.getValue();
        if(value == null) {
        if(prntAttr == null) {
            //System.err.println(“removing graph attr(“+name+”,”+value+”) in “+getName());
            super.setAttribute(name,null);
            return oldValue;
        } else {
            //System.err.println(“defaulting graph attr(“+name+”,”+value+”) in “+getName());
            return setAttribute(prntAttr);
        }
        } else if(value instanceof String && ((String)value).trim().length() == 0 && Attribute.attributeType(getType(),name) != STRING_TYPE) {
        if(prntAttr == null) {
            //System.err.println(“removing graph attr(“+name+”,”+value+”) in “+getName());
            super.setAttribute(name,null);
            return oldValue;
        } else {
            //System.err.println(“defaulting graph attr(“+name+”,”+value+”) in “+getName());
            return setAttribute(prntAttr);
        }
        } else {
        crntAttr.setValue(value);
        //System.err.println(“changing graph attr(“+name+”,”+value+”) in “+getName());
        }
    }
    if(crntAttr.hasChanged()) {
        crntAttr.notifyObservers(new Long(System.currentTimeMillis()));
    }
    return oldValue;
    }

    /**
     * Gets the subgraph-specific default attribute for the named edge attribute.
     *
     * @param key the name of the edge attribute pair to be retrieved.
     * @return the requested attribute pair or null if not found.
     */
    public Attribute getEdgeAttribute(String key) {
    if(edgeAttributes == null) return(null);
    return((Attribute)(edgeAttributes.get(key)));
    }

    /**
     * Gets the subgraph-specific default value for the named edge attribute.
     *
     * @param key the name of the edge attribute pair to be retrieved.
     * @return the requested attribute value or null if not found.
     */
    public Object getEdgeAttributeValue(String key) {
    Attribute attr;
    if(edgeAttributes == null) return(null);
    if((attr = (Attribute)(edgeAttributes.get(key))) == null) return(null);
    return(attr.getValue());
    }

    /**
     * Gets an enumeration of the subgraph-specific edge attribute keys
     *
     * @return an enumeration of String objects.
     */
    public Enumeration getEdgeAttributeKeys() {
    if(edgeAttributes == null) return Grappa.emptyEnumeration.elements();
    return(edgeAttributes.keys());
    }

    /**
     * Gets an enumeration of the subgraph-specific edge attributes
     *
     * @return an enumeration of Attribute objects.
     */
    public Enumeration getEdgeAttributePairs() {
    if(edgeAttributes == null) return Grappa.emptyEnumeration.elements();
    return(edgeAttributes.elements());
    }

    /**
     * Get the bounding box of the subgraph.
     *
     * @return the bounding box of the subgraph.
     */
    public java.awt.geom.Rectangle2D getBoundingBox() {
    java.awt.geom.Rectangle2D bbox = null;
    if(grappaNexus == null || (bbox = grappaNexus.bbox) == null) {
        if(grappaNexus == null) {
        buildShape();
        }
        bbox = null;
        Element elem = null;
        GraphEnumeration enm = elements();
        while(enm.hasMoreElements()) {
        elem = enm.nextGraphElement();
        if(elem == (Element)this) continue;
        switch(elem.getType()) {
        case NODE:
        case EDGE:
            elem.buildShape();
            if(bbox == null) {
            bbox = elem.grappaNexus.getBounds2D();
            } else {
            bbox.add(elem.grappaNexus.getBounds2D());
            }
            break;
        case SUBGRAPH:
            if(bbox == null) {
            bbox = ((Subgraph)elem).getBoundingBox();
            } else {
            bbox.add(((Subgraph)elem).getBoundingBox());
            }
            break;
        default: // cannot happen
            throw new InternalError(“unknown type (” + elem.getType() + “)”);
        }
        }
        GrappaSize minSize = (GrappaSize)getAttributeValue(MINSIZE_ATTR);
        if(minSize != null) {
        if(bbox == null) {
            bbox = new java.awt.geom.Rectangle2D.Double(0,0,minSize.getWidth(),minSize.getHeight());
        } else {
            bbox.add(new java.awt.geom.Rectangle2D.Double(bbox.getCenterX()-(minSize.getWidth()/2.0),bbox.getCenterY()-(minSize.getHeight()/2.0),minSize.getWidth(),minSize.getHeight()));
        }
        }
        GrappaBox minBox = (GrappaBox)getThisAttributeValue(MINBOX_ATTR);
        if(minBox != null) {
        if(bbox == null) {
            bbox = new java.awt.geom.Rectangle2D.Double(minBox.x,minBox.y,minBox.width,minBox.height);
        } else {
            bbox.add(new java.awt.geom.Rectangle2D.Double(minBox.x,minBox.y,minBox.width,minBox.height));
        }
        }
        minBox = (GrappaBox)getThisAttributeValue(BBOX_ATTR);
        if(minBox != null) {
        if(bbox == null) {
            bbox = new java.awt.geom.Rectangle2D.Double(minBox.x,minBox.y,minBox.width,minBox.height);
        } else {
            bbox.add(new java.awt.geom.Rectangle2D.Double(minBox.x,minBox.y,minBox.width,minBox.height));
        }
        }
        if(bbox == null) {
        bbox = new java.awt.geom.Rectangle2D.Double();
        }
        bbox.add(bbox.getX()+bbox.getWidth()+1,bbox.getY()+bbox.getHeight()+1);

        grappaNexus.bbox = bbox;
        if(Grappa.provideBBoxAttribute) {
        setAttribute(BBOX_ATTR, new GrappaBox(bbox));
        }
        grappaNexus.updateShape();
    }
    return((java.awt.geom.Rectangle2D)(bbox.clone()));
    }

    /**
     * Removes bounding box information from this subgraph and any
     * contained subgraphs including the BBOX_ATTR value and then
     * recomputes the bounding boxes.
     *
     * @return the new bounding box of the subgraph.
     */
    public java.awt.geom.Rectangle2D resetBoundingBox() {
    Element elem = null;
    GraphEnumeration enm = elements(SUBGRAPH);
    while(enm.hasMoreElements()) {
        elem = enm.nextGraphElement();
        elem.grappaNexus.bbox = null;
        elem.setAttribute(BBOX_ATTR, null);
    }
    return(getBoundingBox());
    }

    /**
     * Prints an ascii description of each graph element to the supplied stream.
     *
     * @param output the OutputStream for writing the graph description.
     */
    public void printSubgraph(PrintWriter out) {
    Graph graph = getGraph();
    String indent = new String(graph.getIndent());

    if(Grappa.printVisibleOnly && (!visible || grappaNexus.style.invis))
        return;

    if(getSubgraph() == null) {
        // this subgraph is the root
        out.println(indent + (graph.isStrict()?”strict “:””) + (graph.isDirected()?”digraph”:”graph”) + ” ” + graph.toString() + ” {“);
    } else if(getName().startsWith(ANONYMOUS_PREFIX)) {
        out.println(indent + “{“);
    } else {
        out.println(indent + “subgraph ” + this.toString() + ” {“);
    }

    graph.incrementIndent();

    printDflt(out,SUBGRAPH);
    printDflt(out,NODE);
    printDflt(out,EDGE);

    if(graphdict != null && !graphdict.isEmpty()) {
        Enumeration elems = graphdict.elements();
        while(elems.hasMoreElements()) {
        ((Subgraph)(elems.nextElement())).printSubgraph(out);
        }
    }

    if(nodedict != null && !nodedict.isEmpty()) {
        Enumeration elems = nodedict.elements();
        while(elems.hasMoreElements()) {
        ((Node)(elems.nextElement())).printNode(out);
        }
    }

    if(edgedict != null && !edgedict.isEmpty()) {
        Enumeration elems = edgedict.elements();
        while(elems.hasMoreElements()) {
        ((Edge)(elems.nextElement())).printEdge(out);
        }
    }

    graph.decrementIndent();

    out.println(indent + “}”);
    }

    // print the subgraph default elements
    private void printDflt(PrintWriter out, int type) {
    String indent = new String(getGraph().getIndent());
    Hashtable attr = null;
    String label = null;

    switch(type) {
    case SUBGRAPH:
        attr = attributes;
        label = “graph”;
        break;
    case NODE:
        attr = nodeAttributes;
        label = “node”;
        break;
    case EDGE:
        attr = edgeAttributes;
        label = “edge”;
        break;
    }

    if(attr == null || attr.isEmpty()) {
        getGraph().printError(“no ” + label + ” atrtibutes for ” + getName());
        return;
    }

    getGraph().incrementIndent();
    printDfltAttr(out,attr,type,indent + label + ” [“, indent + “];”);
    getGraph().decrementIndent();
    }

    // print the subgraph default element attribute values
    private void printDfltAttr(PrintWriter out, Hashtable dfltAttr, int type, String prefix, String suffix) {
    String indent = new String(getGraph().getIndent());
    String value;
    String key;
    Attribute attr;
    int nbr = 0;
    Enumeration attrs = dfltAttr.elements();
    Subgraph sg = getSubgraph();
    Hashtable printlist = null;

    if(type == SUBGRAPH && (Grappa.usePrintList || usePrintList)) {
        printlist = (Hashtable)getAttributeValue(PRINTLIST_ATTR);
    }

    while(attrs.hasMoreElements()) {
        attr = (Attribute)(attrs.nextElement());
        if(attr == null) continue;
        key = attr.getName();
        if(printlist != null && printlist.get(key) == null) continue;
        value = attr.getStringValue();
        if(Grappa.elementPrintAllAttributes || Grappa.elementPrintDefaultAttributes || printAllAttributes || printDefaultAttributes || !attr.equalsValue(getParentDefault(type,key))) {
        nbr++;
        if(nbr == 1) {
            out.println(prefix);
            out.print(indent + key + ” = ” + canonString(value));
        } else {
            out.println(“,”);
            out.print(indent + key + ” = ” + canonString(value));
        }
        }
    }
    if(nbr > 0) {
        out.println();
        out.println(suffix);
        out.println();
    }
    }

    /**
     * Returns the attribute conversion type for the supplied attribute name.
     * After subgraph specific attribute name/type mappings are checked,
     * mappings at the element level are checked.
     *
     * @param attrname the attribute name
     * @return the currently associated attribute type
     */
    public static int attributeType(String attrname) {
    int convtype = -1;
    int hashCode;

    if(attrname != null) {
        hashCode = attrname.hashCode();

        if(hashCode == MARGIN_HASH && attrname.equals(MARGIN_ATTR)) {
        convtype = SIZE_TYPE;
        } else if(hashCode == MCLIMIT_HASH && attrname.equals(MCLIMIT_ATTR)) {
        convtype = DOUBLE_TYPE;
        } else if(hashCode == MINBOX_HASH && attrname.equals(MINBOX_ATTR)) {
        convtype = BOX_TYPE;
        } else if(hashCode == NODESEP_HASH && attrname.equals(NODESEP_ATTR)) {
        convtype = DOUBLE_TYPE;
        } else if(hashCode == MINSIZE_HASH && attrname.equals(MINSIZE_ATTR)) {
        convtype = SIZE_TYPE;
        } else if(hashCode == NODESEP_HASH && attrname.equals(NODESEP_ATTR)) {
        convtype = DOUBLE_TYPE;
        } else if(hashCode == RANKSEP_HASH && attrname.equals(RANKSEP_ATTR)) {
        convtype = DOUBLE_TYPE;
        } else if(hashCode == SIZE_HASH && attrname.equals(SIZE_ATTR)) {
        convtype = SIZE_TYPE;
        } else {
        return(Element.attributeType(attrname));
        }
    }

    return(convtype);
    }

    // get the parent default attribute value for the specified type and key
    private Attribute getParentDefault(int type, String key) {
    Attribute attr = null;
    Subgraph subg = getSubgraph();
    switch(type) {
    case SUBGRAPH:
        while(subg != null && (attr = subg.getLocalAttribute(key)) == null) {
        subg = subg.getSubgraph();
        }
        if(attr == null) {
        attr = getGraph().getGlobalAttribute(SUBGRAPH,key);
        }
        return attr;
    case NODE:
        while(subg != null && (attr = subg.getNodeAttribute(key)) == null) {
        subg = subg.getSubgraph();
        }
        if(attr == null) {
        attr = getGraph().getGlobalAttribute(NODE,key);
        }
        return attr;
    case EDGE:
        while(subg != null && (attr = subg.getEdgeAttribute(key)) == null) {
        subg = subg.getSubgraph();
        }
        if(attr == null) {
        attr = getGraph().getGlobalAttribute(EDGE,key);
        }
        return attr;
    }
    return null;
    }

    /*
     * Find an Element by name.
     *
     * @param type the type of the element
     * @param name the name of the element
     * @return the found element or null
     *
     * @see Subgraph#findNodeByName(java.lang.String)
     * @see Subgraph#findEdgeByName(java.lang.String)
     * @see Subgraph#findSubgraphByName(java.lang.String)
     */
    private Element findElementByName(int type, String name) {
    if(name == null) {
        return(null);
    }

    return findElementInSubgraphByName(type,name);
    }

    // used above
    private Element findElementInSubgraphByName(int type, String name) {
    Element elem = null;

    switch(type) {
    case NODE:
        if(nodedict != null) elem = (Element)nodedict.get(name);
        break;
    case EDGE:
        if(edgedict != null) elem = (Element)edgedict.get(name);
        break;
    case SUBGRAPH:
        if(graphdict != null) elem = (Element)graphdict.get(name);
        break;
    }

    if(elem != null || graphdict == null) return elem;

    Enumeration enm = graphdict.elements();
    while(enm.hasMoreElements()) {
        if((elem = ((Subgraph)enm.nextElement()).findElementInSubgraphByName(type,name)) != null) {
        return elem;
        }
    }

    return elem;
    }

    /**
     * Searches current subgraph and, by recursion, descendent subgraphs
     * for the node matching the supplied name.
     *
     * @param nodeName the name of the node to be found.
     * @return the Node matching the name or null, if there is no match.
     */
    public Node findNodeByName(String nodeName) {
    return (Node)findElementByName(NODE,nodeName);
    }

    /**
     * Searches current subgraph and, by recursion, descendent subgraphs
     * for the edge matching the supplied name.
     *
     * @param edgeName the name of the edge to be found.
     * @return the Edge matching the name or null, if there is no match.
     */
    public Edge findEdgeByName(String edgeName) {
    return (Edge)findElementByName(EDGE,edgeName);
    }

    /**
     * Searches current subgraph and, by recursion, descendent subgraphs
     * for the subgraph matching the supplied name.
     *
     * @param graphName the name of the subgraph to be found.
     * @return the Subgraph matching the name or null, if there is no match.
     */
    public Subgraph findSubgraphByName(String graphName) {
    return (Subgraph)findElementByName(SUBGRAPH,graphName);
    }

    /**
     * Creates a new element and adds it to the subgraph’s element dictionary.
     * For nodes, the info vector can be null or contains:
     * 

         *   

  •  String – name of the node (optional, for automatic name generation)
         * 

     * For edges, the info vector must contain (in this order) at least:
     * 

         *   

  •  Node – head node,
         *   

  •  String – headport tag (or null),
         *   

  •  Node – tail node,
         * 

     * Optionally, the info vector can also contain at its end (in this order):
     * 

         *   

  •  String – tailport tag (or null),
         *   

  •  String – a key for distinguishing multiple edges between the same nodes (or null),
         * 

     * For subgraphs, the info vector can be null or contains:
     * 

         *   

  •  String – name of the subgraph (optional, for automatic name generation)
         * 

     *
     * @param type type of the element to be added
     * @param info a vector specifics for the particular type of element being created
     * @param attrs attributes describing the element to be created
     * @exception InstantiationException whenever element cannot be created
     */
    public Element createElement(int type, Object[] info, Attribute[] attrs) {
    Element elem = null;

    switch(type) {
    case NODE:
        String nodeName = null;
        if(info != null && info.length >= 1) {
        nodeName = (String)info[0];
        }
        Node node = new Node(this,nodeName);
        if(attrs != null) {
        for(int i = 0; i < attrs.length; i++) {             node.setAttribute(attrs[i]);         }         }         elem =  (Element)node;         break;     case EDGE:         if(info == null || info.length < 3) {         throw new IllegalArgumentException("insufficient info supplied for edge creation");         }         Node head = (Node)info[0];         String headPort = (String)info[1];         Node tail = (Node)info[2];         String tailPort = null;         String key = null;         if(info.length > 3) {
        tailPort = (String)info[3];
        if(info.length > 4) {
            key = (String)info[4];
        }
        }
        Edge edge = new Edge(this,tail,tailPort,head,headPort,key);
        if(attrs != null) {
        for(int i = 0; i < attrs.length; i++) {             edge.setAttribute(attrs[i]);         }         }         elem =  (Element)edge;         break;     case SUBGRAPH:         String subgName = null;         if(info != null && info.length >= 1) {
        subgName = (String)info[0];
        }
        Subgraph newSubg = new Subgraph(this,subgName);
        if(attrs != null) {
        for(int i = 0; i < attrs.length; i++) {             newSubg.setAttribute(attrs[i]);         }         }         elem =  (Subgraph)newSubg;         break;     default:         return null;     }     return elem;     }     /**      * Adds the specified node to the subgraph's Node dictionary.      *      * @param newNode the node to be added to the dictionary.      */     public void addNode(Node newNode) {     if(newNode == null) return;     if(nodedict == null) {         nodedict = new Hashtable();     }     nodedict.put(newNode.getName(),newNode);     }     /**      * Removes the node matching the specified name from the subgraph's Node dictionary.      *      * @param nodeName the name of the node to be removed from the dictionary.      * @return the node that was removed.      */     public Node removeNode(String nodeName) {     if(nodedict == null) return(null);     return((Node)(nodedict.remove(nodeName)));     }     /**      * Adds the specified edge to the subgraph's Edge dictionary.      *      * @param newEdge the edge to be added to the dictionary.      */     public void addEdge(Edge newEdge) {     if(newEdge == null) return;     if(edgedict == null) {         edgedict = new Hashtable();     }     edgedict.put(newEdge.getName(),newEdge);     }     /**      * Removes the edge matching the specified name from the subgraph's Edge dictionary.      *      * @param edgeName the name of the edge to be removed from the dictionary.      * @return the edge that was removed.      */     public Edge removeEdge(String edgeName) {     if(edgedict == null) return(null);     return((Edge)(edgedict.remove(edgeName)));     }     /**      * Adds the specified subgraph to the subgraph's graph dictionary.      *      * @param newGraph the subgraph to be added to the dictionary.      */     public void addSubgraph(Subgraph newGraph) {     if(newGraph == null) return;     if(graphdict == null) {         graphdict = new Hashtable();     }     graphdict.put(newGraph.getName(),newGraph);     }     /**      * Removes the subgraph matching the specified name from the      * subgraph's graph dictionary.      *      * @param graphName the name of the subgraph to be removed from the dictionary.      * @return the subgraph that was removed.      */     public Subgraph removeSubgraph(String graphName) {     if(graphdict == null) return(null);     return((Subgraph)(graphdict.remove(graphName)));     }     /**      * Set flag to indicate if subgraph labels should be rendered      *      * @return the previous value      */     public boolean setShowSubgraphLabels(boolean value) {     boolean oldValue = subgLabels;     subgLabels = value;     return(oldValue);     }     /**      * Set flag to indicate if node labels should be rendered      *      * @return the previous value      */     public boolean setShowNodeLabels(boolean value) {     boolean oldValue = nodeLabels;     nodeLabels = value;     return(oldValue);     }     /**      * Set flag to indicate if edge labels should be rendered      *      * @return the previous value      */     public boolean setShowEdgeLabels(boolean value) {     boolean oldValue = edgeLabels;     edgeLabels = value;     return(oldValue);     }     /**      * Get flag that indicates if subgraph labels should be rendered      *      * @return the flag value      */     public boolean getShowSubgraphLabels() {     return(subgLabels);     }     /**      * Get flag that indicates if node labels should be rendered      *      * @return the flag value      */     public boolean getShowNodeLabels() {     return(nodeLabels);     }     /**      * Get flag that indicates if edge labels should be rendered      *      * @return the flag value      */     public boolean getShowEdgeLabels() {     return(edgeLabels);     }     /**      * Check if the orientation of this subgraph is LR (left-to-right) as opposed      * to TB (top-to-bottom).      *      * @return true if the orientation is left-to-right.      */     public boolean isLR() {     Attribute attr = getAttribute("rankdir");     if(attr == null) return false; // the default     String value = attr.getStringValue();     if(value == null) return false; // the default     if(value.equals("LR")) return true;     return false;     }     /**      * Adds a default tag for the specified element type within this subgraph.      *       * @param type the element type for this tag operation      * @param tag the tag to associate with this element type.      */     public void addTypeTag(int type, String tag) {     if(tag == null || tag.indexOf(',') >= 0) {
        throw new RuntimeException(“tag value null or contains a comma (” + tag + “)”);
    }
    Attribute attr = null;
    Hashtable tags;
    switch(type) {
    case NODE:
        attr = getNodeAttribute(TAG_ATTR);
        break;
    case EDGE:
        attr = getEdgeAttribute(TAG_ATTR);
        break;
    case SUBGRAPH:
        attr = getLocalAttribute(TAG_ATTR);
        break;
    }
    if(attr == null) {
        attr = new Attribute(type,TAG_ATTR,new Hashtable());
        setAttribute(attr);
        switch(type) {
        case NODE:
        setNodeAttribute(attr);
        break;
        case EDGE:
        setEdgeAttribute(attr);
        break;
        case SUBGRAPH:
        setAttribute(attr);
        break;
        }
    }
    tags = (Hashtable)(attr.getValue());

    tags.put(tag,tag);
    // if it becomes desireable to retain the original order, we
    // could always use the value in the following (instead of
    // what is done above) to reconstruct the original order
    // (Note that no code makes use of the value at this point,
    // so that would all have to be added in printAttributes, for
    // example)
    // tags.put(tag,new Long(System.currentTimeMillis()));
    }

    /**
     * Check if the specified element type has the supplied default tag within
     * this subgraph.
     *
     * @param type the element type for this tag operation
     * @param tag tag value to be searched for
     * @return true, if this subgraph contains the supplied tag as a default for the given type
     */
    public boolean hasTypeTag(int type, String tag) {
    Attribute attr = null;
    Hashtable tags;
    switch(type) {
    case NODE:
        attr = getNodeAttribute(TAG_ATTR);
        break;
    case EDGE:
        attr = getEdgeAttribute(TAG_ATTR);
        break;
    case SUBGRAPH:
        attr = getLocalAttribute(TAG_ATTR);
        break;
    }
    if(attr == null) return false;
    tags = (Hashtable)(attr.getValue());
    if(tags == null || tags.size() == 0) return false;
    return(tags.containsKey(tag));
    }

    /**
     * Check if this element type has any default tags at all.
     *
     * @param type the element type for this tag operation
     * @return true, if this Element has any tags
     */
    public boolean hasTypeTags(int type) {
    Attribute attr = null;
    Hashtable tags;
    switch(type) {
    case NODE:
        attr = getNodeAttribute(TAG_ATTR);
        break;
    case EDGE:
        attr = getEdgeAttribute(TAG_ATTR);
        break;
    case SUBGRAPH:
        attr = getLocalAttribute(TAG_ATTR);
        break;
    }
    if(attr == null) return false;
    tags = (Hashtable)(attr.getValue());
    if(tags == null || tags.size() == 0) return false;
    return(true);
    }

    /**
     * Removes any and all default tags associated with this element type.
     * @param type the element type for this tag operation
     */
    public void removeTypeTags(int type) {
    Attribute attr = null;
    Hashtable tags;
    switch(type) {
    case NODE:
        attr = getNodeAttribute(TAG_ATTR);
        break;
    case EDGE:
        attr = getEdgeAttribute(TAG_ATTR);
        break;
    case SUBGRAPH:
        attr = getLocalAttribute(TAG_ATTR);
        break;
    }
    if(attr == null) return;
    tags = (Hashtable)(attr.getValue());
    if(tags == null || tags.size() == 0) return;
    tags.clear();
    }

    /**
     * Removes the specified tag from this element.
     *
     * @param type the element type for this tag operation
     * @param tag the tag value to remove
     */
    public void removeTypeTag(int type, String tag) {
    Attribute attr = null;
    Hashtable tags;
    switch(type) {
    case NODE:
        attr = getNodeAttribute(TAG_ATTR);
        break;
    case EDGE:
        attr = getEdgeAttribute(TAG_ATTR);
        break;
    case SUBGRAPH:
        attr = getLocalAttribute(TAG_ATTR);
        break;
    }
    if(attr == null) return;
    tags = (Hashtable)(attr.getValue());
    if(tags == null || tags.size() == 0) return;
    tags.remove(tag);
    }

    /**
     * Get a count of elements in this subgraph.  No recursion to descendants
     * is done.
     *
     * @param types a bitwise-oring of NODE, EDGE, SUBGRAPH to
     *        determine which element types should be in the count
     * @return a count of the specified elements in this subgraph.
     * @see GrappaConstants#NODE
     * @see GrappaConstants#EDGE
     * @see GrappaConstants#SUBGRAPH
     */
    public int countOfLocalElements(int types) {
    int count = 0;
    if((types&NODE) != 0 && nodedict != null) count += nodedict.size(); 
    if((types&EDGE) != 0 && edgedict != null) count += edgedict.size(); 
    if((types&SUBGRAPH) != 0 && graphdict != null) count += graphdict.size(); 
    return count;
    }

    /**
     * Get a count of elements in this subgraph and, by recursion, descendant
     * subgraphs. The subgraph itself is not counted.
     *
     * @param types a bitwise-oring of NODE, EDGE, SUBGRAPH to
     *        determine which element types should be in the count
     * @return a count of the specified elements in this subgraph and its descendants.
     * @see GrappaConstants#NODE
     * @see GrappaConstants#EDGE
     * @see GrappaConstants#SUBGRAPH
     */
    public int countOfElements(int types) {
    int count = 0;
    if((types&NODE) != 0 && nodedict != null) count += nodedict.size(); 
    if((types&EDGE) != 0 && edgedict != null) count += edgedict.size(); 
    if(graphdict != null) {
        if((types&SUBGRAPH) != 0) count += graphdict.size(); 
        Enumeration enm = graphdict.elements();
        while(enm.hasMoreElements()) {
        count += ((Subgraph)enm.nextElement()).countOfElements(types);
        }
    }
    return count;
    }

    /**
     * Delete this subgraph or any contained subgraph, at any depth, if the
     * subgraph contains no elements.
     */
    public void removeEmptySubgraphs() {
    if(
       (graphdict == null || graphdict.size() == 0)
       &&
       (nodedict == null || nodedict.size() == 0)
       &&
       (edgedict == null || edgedict.size() == 0)
       ) {
        delete();
        return;
    }
    if(graphdict != null) {
        Enumeration enm = graphdict.elements();
        while(enm.hasMoreElements()) {
        ((Subgraph)enm.nextElement()).removeEmptySubgraphs();
        }
    }
    }

    /**
     * @return true if this subgraph or any subgraph contained within
     *              this subgraph, at any depth, is empty.
     */
     public boolean hasEmptySubgraphs() {
    if(
       (graphdict == null || graphdict.size() == 0)
       &&
       (nodedict == null || nodedict.size() == 0)
       &&
       (edgedict == null || edgedict.size() == 0)
       ) {
        return(true);
    }
    if(graphdict != null) {
        Enumeration enm = graphdict.elements();
        while(enm.hasMoreElements()) {
        if(((Subgraph)enm.nextElement()).hasEmptySubgraphs()) {
            return(true);
        }
        }
    }
    return(false);
    }

    //
    // Start PatchWork (similar to TreeMap) stuff
    //

    private double PATCHEDGE = 2;
    private double PATCHEDGE2 = 2.0 * PATCHEDGE;
    private Element[] sgPatches = null;
    private Element[] elPatches = null;
    private GrappaBox patch = null;

    public void clearPatchWork() {

    prepPatchWork(null, -1);
    }

    public void patchWork(java.awt.geom.Rectangle2D.Double r, boolean square, int mode) {

    preparePatchWork(mode);
    computePatchWork(r instanceof GrappaBox ? r : new GrappaBox(r), square);
    if(mode == 0) {
        Subgraph sg;
        String style;
        Attribute attr;
        Enumeration enm = elements(Grappa.SUBGRAPH);
        while(enm.hasMoreElements()) {
        sg = (Subgraph)(enm.nextElement());
        if(sg != this) {
            attr = sg.getAttribute(STYLE_ATTR);
            if(attr != null) {
            style = attr.getStringValue();
            sg.setAttribute(STYLE_ATTR,style != null && style.length() > 0 ? style + “,filled(false)” : null);
            }
        }
        }
    } else {
        float sgtot = countOfElements(Grappa.SUBGRAPH) – 2;
        float nbr = 0;
        Subgraph sg;
        String style;
        Attribute attr;
        Enumeration enm = elements(Grappa.SUBGRAPH);
        while(enm.hasMoreElements()) {
        sg = (Subgraph)(enm.nextElement());
        if(sg != this) {
            sg.setAttribute(COLOR_ATTR,java.awt.Color.getHSBColor((float)(0.05+0.9*(nbr++/sgtot)),(float)1.0,(float)1.0));
            attr = sg.getAttribute(STYLE_ATTR);
            if(attr == null) {
            sg.setAttribute(STYLE_ATTR, “filled”);
            } else {
            style = attr.getStringValue();
            sg.setAttribute(STYLE_ATTR,style == null || style.length() == 0 ? “filled” : style + “,filled”);
            }
        }
        }
    }
    }

    public double preparePatchWork(int mode) {

    double total;

    total = prepPatchWork(PATCH_ATTR, mode);

    if(mode == 0) {
        combPatchWork();
        if(elPatches != null)
        Arrays.sort(elPatches,0,elPatches.length,this);
    }

    return(total);
    }

    Element[] getPatches() {
    return(elPatches);
    }

    private void combPatchWork() {

    Hashtable dict;
    Subgraph sg;
    Element[] patches;
    Element[] elpat;
    Element[] sgpat;
    Element[] tmparr;

    patches = elPatches;

    sgpat = sgPatches; // snapshot
    
    if(sgpat != null && sgpat.length > 0) {
        for(int i = 0; i < sgpat.length; i++) {         sg = (Subgraph)sgpat[i];         sg.combPatchWork();         elpat = sg.getPatches();         if(elpat != null && elpat.length > 0) {
            if(patches == null || patches.length == 0)
            patches = elpat;
            else {
            tmparr = new Element[patches.length + elpat.length];
            System.arraycopy(patches,0,tmparr,0,patches.length);
            System.arraycopy(elpat,0,tmparr,patches.length,elpat.length);
            patches = tmparr;
            }
        }
        }
    }

    sgPatches = null;
    elPatches = patches;
    }

    private double prepPatchWork(String attrname, int mode) {

    double total;
    Enumeration enm;
    Hashtable dict;
    Object obj;
    int m;
    int n;
    Subgraph sg;
    Element el;
    Element[] tmparr;

    total = 0;

    dict = graphdict; // snapshot

    sgPatches = null;

    if(dict != null && dict.size() > 0) {
        if(attrname != null)
        sgPatches = new Element[dict.size()];
        n = 0;
        enm = dict.elements();
        while(enm.hasMoreElements()) {
        sg = (Subgraph)enm.nextElement();
        total += sg.prepPatchWork(attrname, mode);
        if(attrname != null)
            sgPatches[n++] = sg;
        }
    }

    dict = nodedict; // snapshot;

    elPatches = null;

    if(attrname != null && dict != null && dict.size() > 0) {
        m = 0;
        n = 0;
        if(mode <= 0)         elPatches = new Element[dict.size()];         else if(sgPatches == null)         elPatches = new Element[dict.size()];         else {         n = sgPatches.length;         elPatches = new Element[n + dict.size()];         System.arraycopy(sgPatches,0,elPatches,0,n);         sgPatches = null;         }         enm = dict.elements();         while(enm.hasMoreElements()) {         el = (Element)enm.nextElement();         if((obj = el.getAttributeValue(attrname)) != null) {             if(obj instanceof Number) {             el.setPatchSize(((Number)obj).doubleValue());             total += el.getPatchSize();             elPatches[n++] = el;             } else m++;         } else m++;         }         if(m > 0) {
        if(n == m)
            elPatches = null;
        else {
            tmparr = new Element[n-m];
            System.arraycopy(elPatches,0,tmparr,0,tmparr.length);
            elPatches = tmparr;
        }
        }
    }

    if(mode != 0) {
        if(sgPatches != null)
        Arrays.sort(sgPatches,0,sgPatches.length,this);
        if(elPatches != null)
        Arrays.sort(elPatches,0,elPatches.length,this);
    }

    setPatchSize(total);

    return(total);
    }

    // squarified layout
    double aspect(java.awt.geom.Rectangle2D.Double r) { return(r.getWidth() == 0 ? 1 : r.getHeight()/r.getWidth()); }
    double score(double wd, double ht) { return((ht <= PATCHEDGE2 || wd <= PATCHEDGE2) ? Double.MAX_VALUE : (ht > wd ? (wd == 0 ? (ht == 0 ? 1 : Double.MAX_VALUE) : ht/wd) : (ht == 0 ? (wd == 0 ? 1 : Double.MAX_VALUE) : wd/ht))); }

    public void computePatchWork(java.awt.geom.Rectangle2D.Double r, boolean square)
    {
    if(square)
        compSqPatchWork(r, true);
    else
        compStdPatchWork(r, true);
    }

    private void compSqPatchWork(java.awt.geom.Rectangle2D.Double r, boolean top)
    {
    double frac;
    double total;
    double previous;
    double next;
    double tot;
    double prv;
    double nxt;
    double dir;
    java.awt.geom.Rectangle2D.Double box;
    java.awt.geom.Rectangle2D.Double p;
    java.awt.geom.Rectangle2D.Double pp;
    Element el;
    Attribute attr;
    String style;
    int i;
    int j;
    double pscore;
    double nscore;
    double sz;
    double psz;
    double tsz;
    double tfrac;

    setPatch(r);
    setAttribute(MINSIZE_ATTR, new GrappaSize(r.getWidth(), r.getHeight()+(top?0:1)));
    dir = aspect(r);

    total = getPatchSize();
    if(top)
        box = new GrappaBox(r);
    else
        box = new GrappaBox(r.getX()+PATCHEDGE, r.getY()+PATCHEDGE, r.getWidth()-PATCHEDGE2, r.getHeight()-PATCHEDGE2);

    if(dir > 1)
        previous = box.getY();
    else
        previous = box.getX();

    if(sgPatches != null) {
        i = 0;
        while(i < sgPatches.length) {         el = (Element)sgPatches[i];         sz = el.getPatchSize();         if((i+1) < sgPatches.length) {             psz = 0;             frac = sz / total;             if(dir > 1) {
            pscore = score(box.getWidth(),frac*box.getHeight());
            } else {
            pscore = score(frac*box.getWidth(),box.getHeight());
            }
            j = i + 1;
            
            for(;;) {
            tsz = ((Element)sgPatches[j]).getPatchSize();
            tot = psz + sz + tsz;
            tfrac = tot / total;
            if(dir > 1) {
                nscore = score(box.getWidth() * sz / tot, tfrac*box.getHeight());
            } else {
                nscore = score(tfrac*box.getWidth(), box.getHeight() * sz / tot);
            }
            if(nscore <= pscore) {                 if(dir > 1) {
                pscore = score(box.getWidth() * tsz / tot, tfrac*box.getHeight());
                } else {
                pscore = score(tfrac*box.getWidth(), box.getHeight() * tsz / tot);
                }
                psz += sz;
                sz = tsz;
                tsz = 0;
                j++;
                if(j < sgPatches.length)                 continue;             } else {                 tsz = 0;             }             tot = psz + sz + tsz;             frac = tot / total;             if(dir > 1) {
                prv = box.getX();
                next = frac * box.getHeight();
            } else {
                prv = box.getY();
                next = frac * box.getWidth();
            }
            for(; i < j; i++) {                 el = (Element)sgPatches[i];                 if(dir > 1) {
                p = new GrappaBox(prv, previous, nxt = box.getWidth() * el.getPatchSize() / tot, next);
                } else {
                p = new GrappaBox(previous, prv, next, nxt = box.getHeight() * el.getPatchSize() / tot);
                }
                ((Subgraph)el).compSqPatchWork(p, false);
                prv += nxt;
            }
            break;
            }
        } else {
            frac = sz / total;
            if(dir > 1) {
            p = new GrappaBox(box.getX(), previous, box.getWidth(), (next = frac * box.getHeight()));
            } else {
            p = new GrappaBox(previous, box.getY(), (next = frac * box.getWidth()), box.getHeight());
            }
            ((Subgraph)el).compSqPatchWork(p, false);
            i++;
        }
        previous += next;
        }
    }
    if(elPatches != null) {
        i = 0;
        while(i < elPatches.length) {         el = (Element)elPatches[i];         sz = el.getPatchSize();         if((i+1) < elPatches.length) {             psz = 0;             frac = sz / total;             if(dir > 1) {
            pscore = score(box.getWidth(),frac*box.getHeight());
            } else {
            pscore = score(frac*box.getWidth(),box.getHeight());
            }
            j = i + 1;
            for(;;) {
            tsz = ((Element)elPatches[j]).getPatchSize();
            tot = psz + sz + tsz;
            tfrac = tot / total;
            if(dir > 1) {
                nscore = score(box.getWidth() * sz / tot, tfrac*box.getHeight());
            } else {
                nscore = score(tfrac*box.getWidth(), box.getHeight() * sz / tot);
            }
            if(nscore <= pscore) {                 if(dir > 1) {
                pscore = score(box.getWidth() * tsz / tot, tfrac*box.getHeight());
                } else {
                pscore = score(tfrac*box.getWidth(), box.getHeight() * tsz / tot);
                }
                psz += sz;
                sz = tsz;
                tsz = 0;
                j++;
                if(j < elPatches.length)                 continue;             } else {                 tsz = 0;             }             tot = psz + sz + tsz;             frac = tot / total;             if(dir > 1) {
                prv = box.getX();
                next = frac * box.getHeight();
            } else {
                prv = box.getY();
                next = frac * box.getWidth();
            }
            for(; i < j; i++) {                 el = (Element)elPatches[i];                 if(el instanceof Node) {                 if(dir > 1) {
                    el.setPatch(prv, previous, nxt = box.getWidth() * el.getPatchSize() / tot, next);
                } else {
                    el.setPatch(previous, prv, next, nxt = box.getHeight() * el.getPatchSize() / tot);
                }
                p = el.getPatch();
                el.setAttribute(POS_ATTR, new GrappaPoint(p.getCenterX(), -p.getCenterY()));
                el.setAttribute(WIDTH_ATTR, new Double(p.getWidth()/72.0));
                el.setAttribute(HEIGHT_ATTR, new Double(p.getHeight()/72.0));
                    if (el.getLocalAttribute(COLOR_ATTR) == null)
                    el.setAttribute(COLOR_ATTR,”white”);
                attr = el.getAttribute(STYLE_ATTR);
                if(attr == null) {
                    el.setAttribute(STYLE_ATTR, “filled,lineColor(black)”);
                } else {
                    style = attr.getStringValue();
                    el.setAttribute(STYLE_ATTR,style == null || style.length() == 0 ? “filled,lineColor(black)” : style + “,filled,lineColor(black)”);
                }
                } else {
                if(dir > 1) {
                    p = new GrappaBox(prv, previous, nxt = box.getWidth() * el.getPatchSize() / tot, next);
                } else {
                    p = new GrappaBox(previous, prv, next, nxt = box.getHeight() * el.getPatchSize() / tot);
                }
                ((Subgraph)el).compSqPatchWork(p, false);
                }
                prv += nxt;
            }
            break;
            }
        } else {
            frac = sz / total;
            if(el instanceof Node) {
            if(dir > 1) {
                el.setPatch(box.getX(), previous, box.getWidth(), (next = frac * box.getHeight()));
            } else {
                el.setPatch(previous, box.getY(), (next = frac * box.getWidth()), box.getHeight());
            }
            p = el.getPatch();
            el.setAttribute(POS_ATTR, new GrappaPoint(p.getCenterX(), -p.getCenterY()));
            el.setAttribute(WIDTH_ATTR, new Double(p.getWidth()/72.0));
            el.setAttribute(HEIGHT_ATTR, new Double(p.getHeight()/72.0));
                if (el.getLocalAttribute(COLOR_ATTR) == null)
                el.setAttribute(COLOR_ATTR,”white”);
            attr = el.getAttribute(STYLE_ATTR);
            if(attr == null) {
                el.setAttribute(STYLE_ATTR, “filled,lineColor(black)”);
            } else {
                style = attr.getStringValue();
                el.setAttribute(STYLE_ATTR,style == null || style.length() == 0 ? “filled,lineColor(black)” : style + “,filled,lineColor(black)”);
            }
            } else {
            if(dir > 1) {
                p = new GrappaBox(box.getX(), previous, box.getWidth(), (next = frac * box.getHeight()));
            } else {
                p = new GrappaBox(previous, box.getY(), (next = frac * box.getWidth()), box.getHeight());
            }
            ((Subgraph)el).compSqPatchWork(p, false);
            }
            i++;
        }
        previous += next;
        }
    }
    }

    private void compStdPatchWork(java.awt.geom.Rectangle2D.Double r, boolean top)
    {
    double sz;
    double frac;
    double total;
    double previous;
    double next;
    double dir;
    java.awt.geom.Rectangle2D.Double box;
    java.awt.geom.Rectangle2D.Double p;
    Element el;
    Attribute attr;
    String style;

    setPatch(r);
    setAttribute(MINSIZE_ATTR, new GrappaSize(r.getWidth(), r.getHeight()+(top?0:1)));
    dir = aspect(r);

    total = getPatchSize();
    if(top)
        box = new GrappaBox(r);
    else
        box = new GrappaBox(r.getX()+PATCHEDGE, r.getY()+PATCHEDGE, r.getWidth()-PATCHEDGE2, r.getHeight()-PATCHEDGE2);

    if(dir > 1)
        previous = box.getY();
    else
        previous = box.getX();

    if(sgPatches != null) {
        for(int i = 0; i < sgPatches.length; i++) {         el = (Element)sgPatches[i];         sz = el.getPatchSize();         frac = sz / total;         if(dir > 1) {
            ((Subgraph)el).compStdPatchWork(new GrappaBox(box.getX(), previous, box.getWidth(), (next = frac * box.getHeight())), false);
        } else {
            ((Subgraph)el).compStdPatchWork(new GrappaBox(previous, box.getY(), (next = frac * box.getWidth()), box.getHeight()), false);
        }
        previous += next;
        }
    }
    if(elPatches != null) {
        for(int i = 0; i < elPatches.length; i++) {         el = (Element)elPatches[i];         sz = el.getPatchSize();         frac = sz / total;         if(el instanceof Node) {             if(dir > 1) {
            el.setPatch(box.getX(), previous, box.getWidth(), (next = frac * box.getHeight()));
            } else {
            el.setPatch(previous, box.getY(), (next = frac * box.getWidth()), box.getHeight());
            }
            p = el.getPatch();
            el.setAttribute(POS_ATTR, new GrappaPoint(p.getCenterX(), -p.getCenterY()));
            el.setAttribute(WIDTH_ATTR, new Double(p.getWidth()/72.0));
            el.setAttribute(HEIGHT_ATTR, new Double(p.getHeight()/72.0));
            if (el.getLocalAttribute(COLOR_ATTR) == null)
                el.setAttribute(COLOR_ATTR,”white”);
            attr = el.getAttribute(STYLE_ATTR);
            if(attr == null) {
            el.setAttribute(STYLE_ATTR, “filled,lineColor(black)”);
            } else {
            style = attr.getStringValue();
            el.setAttribute(STYLE_ATTR,style == null || style.length() == 0 ? “filled,lineColor(black)” : style + “,filled,lineColor(black)”);
            }
        } else {
            if(dir > 1) {
            ((Subgraph)el).compStdPatchWork(new GrappaBox(box.getX(), previous, box.getWidth(), (next = frac * box.getHeight())), false);
            } else {
            ((Subgraph)el).compStdPatchWork(new GrappaBox(previous, box.getY(), (next = frac * box.getWidth()), box.getHeight()), false);
            }
        }
        previous += next;
        }
    }
    }

    // Comparator for patchArea

    public int compare(Object o1, Object o2) {

    if(o1 instanceof Element) {
        if(o2 instanceof Element) {
        // biggest to smallest
        double diff = ((Element)o2).getPatchSize() – ((Element)o1).getPatchSize();
        return(diff < 0 ? -1 : diff > 0 ? 1 : 0);
        } else return(0);
    } else return(0);
    }

    public boolean equals(Object obj) {

    // do not need
    return(false);
    }

    //
    // End PatchWork stuff
    //

    /**
     * Get an enumeration of the node elements in this subgraph.
     *
     * @return an Enumeration of Node objects
     */
    public Enumeration nodeElements() {
    if(nodedict == null) {
        return Grappa.emptyEnumeration.elements();
    }
    return nodedict.elements();
    }

    /**
     * Get an enumeration of the edge elements in this subgraph.
     *
     * @return an Enumeration of Edge objects
     */
    public Enumeration edgeElements() {
    if(edgedict == null) {
        return Grappa.emptyEnumeration.elements();
    }
    return edgedict.elements();
    }

    /**
     * Get an enumeration of the subgraph elements in this subgraph.
     *
     * @return an Enumeration of Subgraph objects
     */
    public Enumeration subgraphElements() {
    if(graphdict == null) {
        return Grappa.emptyEnumeration.elements();
    }
    return graphdict.elements();
    }
  
    /**
     * Get an enumeration of elements in this subgraph and any subgraphs under this one.
     *
     * @param types a bitwise-oring of NODE, EDGE, SUBGRAPH to
     *        determine which element types should be in the enumeration
     * @return a GraphEnumeration containing Element objects.
     * @see GrappaConstants#NODE
     * @see GrappaConstants#EDGE
     * @see GrappaConstants#SUBGRAPH
     */
    public GraphEnumeration elements(int types) {
    return new Enumerator(types);
    }
  
    /**
     * Get an enumeration of all elements in this subgraph and any subgraphs under this one.
     * A convenience method equivalent to:
     * 
     * elements(NODE|EDGE|SUBGRAPH)
     * 

     *
     * @return a GraphEnumeration containing Element objects.
     * @see Subgraph#elements(int)
     */
    public GraphEnumeration elements() {
    return new Enumerator(NODE|EDGE|SUBGRAPH);
    }

    class Enumerator implements GraphEnumeration {
    private Subgraph root = null;
    private int types = 0;
    private Enumeration enm = null;
    private GraphEnumeration subEnum = null;
    private Element elem = null;
    private int dictType = 0;
    
    Enumerator(int t) {
        root = Subgraph.this;
        types = t;

        if((types&SUBGRAPH) != 0) {
        elem = (Element)(root);
        } else {
        elem = null;
        }
        enm = subgraphElements();
        if(enm.hasMoreElements()) {
        dictType = SUBGRAPH;
        while(enm.hasMoreElements()) {
            subEnum = ((Subgraph)(enm.nextElement())).new Enumerator(types);
            if(subEnum.hasMoreElements()) {
            if(elem == null) {
                elem = (Element)subEnum.nextElement();
            }
            break;
            }
        }
        } else {
        dictType = 0;
        enm = null;
        subEnum = null;
        }
        if(enm == null) {
        if((types&NODE) != 0 && (enm = nodeElements()).hasMoreElements()) {
            dictType = NODE;
            if(elem == null) {
            elem = (Element)enm.nextElement();
            }
        } else if((types&EDGE) != 0 && (enm = edgeElements()).hasMoreElements()) {
            dictType = EDGE;
            if(elem == null) {
            elem = (Element)enm.nextElement();
            }
        } else {
            enm = null;
        }
        }
    }

    public boolean hasMoreElements() {
        return elem != null;
    }

    public Object nextElement() {
        if(elem == null) {
        throw new NoSuchElementException(“Subgraph$Enumerator”);
        }
        Element el = elem;
        if(subEnum != null && subEnum.hasMoreElements()) {
        elem = (Element)subEnum.nextElement();
        } else if(enm != null && enm.hasMoreElements()) {
        do {
            elem = (Element)enm.nextElement();
            if(elem.isSubgraph()) {
            subEnum = ((Subgraph)elem).new Enumerator(getEnumerationTypes());
            if(subEnum.hasMoreElements()) {
                elem = (Element)subEnum.nextElement();
                break;
            } else {
                elem = null;
            }
            } else {
            break;
            }
        } while(enm.hasMoreElements());
        } else {
        elem = null;
        }
        if(elem == null) {
        if(dictType != 0) {
            if(dictType == SUBGRAPH) {
            if((getEnumerationTypes()&NODE) != 0 && (enm = nodeElements()).hasMoreElements()) {
                dictType = NODE;
                elem = (Element)enm.nextElement();
            } else if((getEnumerationTypes()&EDGE) != 0 && (enm = edgeElements()).hasMoreElements()) {
                dictType = EDGE;
                elem = (Element)enm.nextElement();
            } else {
                dictType = 0;
                enm = null;
            }
            } else if(dictType == NODE) {
            if((getEnumerationTypes()&EDGE) != 0 && (enm = edgeElements()).hasMoreElements()) {
                dictType = EDGE;
                elem = (Element)enm.nextElement();
            } else {
                dictType = 0;
                enm = null;
            }
            } else {
            dictType = 0;
            enm = null;
            }
        }
        }
        return el;
    }

    public Element nextGraphElement() {
        return (Element)nextElement();
    }

    public Subgraph getSubgraphRoot() {
        return root;
    }

    public int getEnumerationTypes() {
        return types;
    }
    }

    /**
     * Get a vector of elements in this subgraph and, by recursion, descendant
     * subgraphs.
     *
     * @param types a bitwise-oring of NODE, EDGE, SUBGRAPH to
     *        determine which element types should be in the count
     * @return a vector of the specified elements in this subgraph and its descendants (excluding the current subgraph itself).
     * @see GrappaConstants#NODE
     * @see GrappaConstants#EDGE
     * @see GrappaConstants#SUBGRAPH
     */
    public Vector vectorOfElements(int types) {
    Vector retVec = new Vector();
    int count = 0;
    Enumeration elems = null;
    if((types&NODE) != 0 && nodedict != null) {
        count += nodedict.size(); 
        retVec.ensureCapacity(count);
        elems = nodedict.elements();
        while(elems.hasMoreElements()) {
        retVec.addElement(elems.nextElement());
        }
    }
    if((types&EDGE) != 0 && edgedict != null) {
        count += edgedict.size(); 
        retVec.ensureCapacity(count);
        elems = edgedict.elements();
        while(elems.hasMoreElements()) {
        retVec.addElement(elems.nextElement());
        }
    }
    if(graphdict != null) {
        if((types&SUBGRAPH) != 0) {
        count += graphdict.size(); 
        retVec.ensureCapacity(count);
        }
        elems = graphdict.elements();
        while(elems.hasMoreElements()) {
        ((Subgraph)(elems.nextElement())).recurseVectorOfElements(types,retVec,count);
        }
    }
    return(retVec);
    }
    
    // used above
    void recurseVectorOfElements(int types, Vector retVec, int count) {
    if((types&SUBGRAPH) != 0) retVec.addElement(this);
    Enumeration elems = null;
    if((types&NODE) != 0 && nodedict != null) {
        count += nodedict.size(); 
        retVec.ensureCapacity(count);
        elems = nodedict.elements();
        while(elems.hasMoreElements()) {
        retVec.addElement(elems.nextElement());
        }
    }
    if((types&EDGE) != 0 && edgedict != null) {
        count += edgedict.size(); 
        retVec.ensureCapacity(count);
        elems = edgedict.elements();
        while(elems.hasMoreElements()) {
        retVec.addElement(elems.nextElement());
        }
    }
    if(graphdict != null) {
        if((types&SUBGRAPH) != 0) {
        count += graphdict.size(); 
        retVec.ensureCapacity(count);
        }
        elems = graphdict.elements();
        while(elems.hasMoreElements()) {
        ((Subgraph)(elems.nextElement())).recurseVectorOfElements(types,retVec,count);
        }
    }
    }
}

att/grappa/Symbols.java
att/grappa/Symbols.java
//—————————————————-
// The following code was generated by CUP v0.10k
// Wed May 25 19:10:51 EDT 2005
//—————————————————-

package att.grappa;

/** CUP generated class containing symbol constants. */
public class Symbols {
  /* terminals */
  public static final int SUBGRAPH = 5;
  public static final int DIGRAPH = 9;
  public static final int RBR = 17;
  public static final int EQUAL = 18;
  public static final int D_EDGE_OP = 6;
  public static final int SEMI = 12;
  public static final int STRICTDIGRAPH = 11;
  public static final int ATSIGN = 20;
  public static final int COLON = 19;
  public static final int ATOM = 21;
  public static final int RCUR = 15;
  public static final int STRICT = 8;
  public static final int COMMA = 13;
  public static final int LCUR = 14;
  public static final int EOF = 0;
  public static final int ND_EDGE_OP = 7;
  public static final int EDGE = 4;
  public static final int GRAPH = 2;
  public static final int error = 1;
  public static final int LBR = 16;
  public static final int STRICTGRAPH = 10;
  public static final int NODE = 3;

  /* non terminals */
  static final int graphType = 2;
  static final int optAttr = 20;
  static final int attrAssignment = 29;
  static final int graph = 9;
  static final int hdr = 10;
  static final int graphAttrDefs = 25;
  static final int edge_op = 31;
  static final int optSeparator = 17;
  static final int optSemi = 16;
  static final int NT$2 = 34;
  static final int NT$1 = 33;
  static final int NT$0 = 32;
  static final int optStmtList = 12;
  static final int $START = 0;
  static final int subgraph = 22;
  static final int optGraphName = 6;
  static final int body = 11;
  static final int optSubgHdr = 5;
  static final int attrList = 24;
  static final int attrItem = 28;
  static final int node = 23;
  static final int rCompound = 3;
  static final int attrStmt = 15;
  static final int simple = 19;
  static final int optAttrDefs = 26;
  static final int optStrict = 1;
  static final int attrMacro = 30;
  static final int optMacroName = 7;
  static final int attrDefs = 27;
  static final int optPort = 8;
  static final int compound = 18;
  static final int nodeList = 21;
  static final int stmt = 14;
  static final int attrType = 4;
  static final int stmtList = 13;
}

Attribute

package att.grappa;
public final synchronized class Attribute extends java.util.Observable implements AttributeHandler, GrappaConstants {
private static AttributeHandler specialHandler;
private String name;
private String stringValue;
private Object value;
private int elementType;
private int attributeType;
private int nameHash;
public static AttributeHandler setAttributeHandler(AttributeHandler);
public void Attribute(int, String, Object);
public void Attribute(Attribute);
public final int getElementType();
public final int getAttributeType();
public final String getName();
public final Object getValue();
public final String getStringValue();
public final Object setValue(Object);
public final boolean equals(Attribute);
public final boolean equalsValue(Attribute);
public final int getNameHash();
public final void setChanged();
public final void clearChanged();
public String toString();
public String convertValue(int, String, Object, int);
public Object convertStringValue(int, String, String, int);
public Object copyValue(int, String, Object, int);
public static int attributeType(int, String);
static void ();
}

AttributeHandler

package att.grappa;
public abstract interface AttributeHandler {
public abstract String convertValue(int, String, Object, int);
public abstract Object convertStringValue(int, String, String, int);
public abstract Object copyValue(int, String, Object, int);
}

GrappaConstants

package att.grappa;
public abstract interface GrappaConstants {
public static final String PACKAGE_PREFIX = att.grappa.;
public static final String PKG_UPLOW = Grappa;
public static final String PKG_UPPER = GRAPPA;
public static final String PKG_LOWER = grappa;
public static final String NEW_LINE;
public static final char NBSP = 160;
public static final java.awt.geom.AffineTransform IDENTXFRM;
public static final int NODE = 1;
public static final int EDGE = 2;
public static final int SUBGRAPH = 4;
public static final int SYSTEM = 8;
public static final double LOG10;
public static final int SELECTION_MASK = 1;
public static final int DELETION_MASK = 2;
public static final int HIGHLIGHT_MASK = 3;
public static final int HIGHLIGHT_ON = 4;
public static final int HIGHLIGHT_OFF = 8;
public static final int HIGHLIGHT_TOGGLE = 16;
public static final int TYPES_SHIFT = 3;
public static final double PointsPerInch = 72.0;
public static final int PERIPHERY_GAP = 4;
public static final String ANONYMOUS_PREFIX = _anonymous_;
public static final String BBOX_ATTR = bb;
public static final String CLUSTERRANK_ATTR = clusterrank;
public static final String COLOR_ATTR = color;
public static final String CUSTOM_ATTR = custom;
public static final String DIR_ATTR = dir;
public static final String DISTORTION_ATTR = distortion;
public static final String FILLCOLOR_ATTR = fillcolor;
public static final String FONTCOLOR_ATTR = fontcolor;
public static final String FONTNAME_ATTR = fontname;
public static final String FONTSIZE_ATTR = fontsize;
public static final String FONTSTYLE_ATTR = fontstyle;
public static final String GRAPPA_BACKGROUND_COLOR_ATTR = grappaBackgroundColor;
public static final String GRAPPA_SELECTION_STYLE_ATTR = grappaSelectionColor;
public static final String GRAPPA_DELETION_STYLE_ATTR = grappaDeletionColor;
public static final String GRAPPA_FONTSIZE_ADJUSTMENT_ATTR = grappaFontsizeAdjustment;
public static final String HEIGHT_ATTR = height;
public static final String IMAGE_ATTR = image;
public static final String LABEL_ATTR = label;
public static final String LP_ATTR = lp;
public static final String HEADLABEL_ATTR = headlabel;
public static final String HEADLP_ATTR = head_lp;
public static final String TAILLABEL_ATTR = taillabel;
public static final String TAILLP_ATTR = tail_lp;
public static final String MARGIN_ATTR = margin;
public static final String MCLIMIT_ATTR = mclimit;
public static final String MINBOX_ATTR = minbox;
public static final String MINLEN_ATTR = minlen;
public static final String MINSIZE_ATTR = minsize;
public static final String NODESEP_ATTR = nodesep;
public static final String ORIENTATION_ATTR = orientation;
public static final String PATCH_ATTR = patch;
public static final String PERIPHERIES_ATTR = peripheries;
public static final String POS_ATTR = pos;
public static final String PRINTLIST_ATTR = printlist;
public static final String RANKDIR_ATTR = rankdir;
public static final String RANKSEP_ATTR = ranksep;
public static final String RECTS_ATTR = rects;
public static final String ROTATION_ATTR = rotation;
public static final String SHAPE_ATTR = shape;
public static final String SIDES_ATTR = sides;
public static final String SIZE_ATTR = size;
public static final String SKEW_ATTR = skew;
public static final String STYLE_ATTR = style;
public static final String TAG_ATTR = tag;
public static final String TIP_ATTR = tip;
public static final String WEIGHT_ATTR = weight;
public static final String WIDTH_ATTR = width;
public static final int BBOX_HASH;
public static final int COLOR_HASH;
public static final int CUSTOM_HASH;
public static final int DIR_HASH;
public static final int DISTORTION_HASH;
public static final int FILLCOLOR_HASH;
public static final int FONTCOLOR_HASH;
public static final int FONTNAME_HASH;
public static final int FONTSIZE_HASH;
public static final int FONTSTYLE_HASH;
public static final int GRAPPA_BACKGROUND_COLOR_HASH;
public static final int GRAPPA_SELECTION_STYLE_HASH;
public static final int GRAPPA_DELETION_STYLE_HASH;
public static final int GRAPPA_FONTSIZE_ADJUSTMENT_HASH;
public static final int HEIGHT_HASH;
public static final int IMAGE_HASH;
public static final int LABEL_HASH;
public static final int LP_HASH;
public static final int HEADLABEL_HASH;
public static final int HEADLP_HASH;
public static final int TAILLABEL_HASH;
public static final int TAILLP_HASH;
public static final int MARGIN_HASH;
public static final int MCLIMIT_HASH;
public static final int MINBOX_HASH;
public static final int MINLEN_HASH;
public static final int MINSIZE_HASH;
public static final int NODESEP_HASH;
public static final int ORIENTATION_HASH;
public static final int PATCH_HASH;
public static final int PERIPHERIES_HASH;
public static final int POS_HASH;
public static final int PRINTLIST_HASH;
public static final int RANKDIR_HASH;
public static final int RANKSEP_HASH;
public static final int RECTS_HASH;
public static final int ROTATION_HASH;
public static final int SHAPE_HASH;
public static final int SIDES_HASH;
public static final int SIZE_HASH;
public static final int SKEW_HASH;
public static final int STYLE_HASH;
public static final int TAG_HASH;
public static final int TIP_HASH;
public static final int WEIGHT_HASH;
public static final int WIDTH_HASH;
public static final int _NO_TYPE = 0;
public static final int BOX_TYPE = 1;
public static final int COLOR_TYPE = 2;
public static final int DIR_TYPE = 3;
public static final int DOUBLE_TYPE = 4;
public static final int FONTSTYLE_TYPE = 5;
public static final int HASHLIST_TYPE = 6;
public static final int INTEGER_TYPE = 7;
public static final int LINE_TYPE = 8;
public static final int POINT_TYPE = 9;
public static final int SHAPE_TYPE = 10;
public static final int SIZE_TYPE = 11;
public static final int STRING_TYPE = 12;
public static final int STYLE_TYPE = 13;
public static final int NO_SHAPE = 0;
public static final int LINE_SHAPE = 1;
public static final int BOX_SHAPE = 2;
public static final int DIAMOND_SHAPE = 3;
public static final int DOUBLECIRCLE_SHAPE = 4;
public static final int DOUBLEOCTAGON_SHAPE = 5;
public static final int EGG_SHAPE = 6;
public static final int HEXAGON_SHAPE = 7;
public static final int HOUSE_SHAPE = 8;
public static final int INVERTEDHOUSE_SHAPE = 9;
public static final int INVERTEDTRAPEZIUM_SHAPE = 10;
public static final int INVERTEDTRIANGLE_SHAPE = 11;
public static final int OCTAGON_SHAPE = 12;
public static final int OVAL_SHAPE = 13;
public static final int PARALLELOGRAM_SHAPE = 14;
public static final int PENTAGON_SHAPE = 15;
public static final int PLAINTEXT_SHAPE = 16;
public static final int POINT_SHAPE = 17;
public static final int POLYGON_SHAPE = 18;
public static final int RECORD_SHAPE = 19;
public static final int ROUNDEDBOX_SHAPE = 20;
public static final int TRAPEZIUM_SHAPE = 21;
public static final int TRIANGLE_SHAPE = 22;
public static final int TRIPLEOCTAGON_SHAPE = 23;
public static final int MCIRCLE_SHAPE = 24;
public static final int MDIAMOND_SHAPE = 25;
public static final int MRECORD_SHAPE = 26;
public static final int MSQUARE_SHAPE = 27;
public static final int CUSTOM_SHAPE = 28;
public static final int SHAPE_MASK = 1023;
public static final int GRAPPA_SHAPE = 1024;
static void ();
}

CustomRenderer

package att.grappa;
public abstract interface CustomRenderer {
public abstract void draw(java.awt.Graphics2D);
public abstract void fill(java.awt.Graphics2D);
public abstract void drawImage(java.awt.Graphics2D);
}

Edge$Enumerator

package att.grappa;
synchronized class Edge$Enumerator implements java.util.Enumeration {
Node node1;
Node node2;
Edge next;
java.util.Enumeration outEdges;
java.util.Enumeration inEdges;
void Edge$Enumerator(Node, Node);
private Edge getNext();
public boolean hasMoreElements();
public Object nextElement();
}

Edge

package att.grappa;
public synchronized class Edge extends Element {
public static final String defaultNamePrefix = E;
private Node headNode;
private String headPortId;
private Node tailNode;
private String tailPortId;
private String key;
int direction;
public void Edge(Subgraph, Node, Node);
public void Edge(Subgraph, Node, String, Node, String);
public void Edge(Subgraph, Node, String, Node, String, String) throws RuntimeException;
public void Edge(Subgraph, Node, Node, String) throws RuntimeException;
public void Edge(Subgraph, Node, String, Node, String, String, String) throws RuntimeException;
private void edgeAttrsOfInterest();
public static Edge findEdgeByKey(Node, Node, String);
public boolean isEdge();
public int getType();
void setName();
public String getKey();
public Node getHead();
public String getHeadPortId();
public Node getTail();
public String getTailPortId();
public String toString();
public void printEdge(java.io.PrintWriter);
public boolean goesForward();
public boolean goesReverse();
public static int attributeType(String);
public static java.util.Enumeration findEdgesByEnds(Node, Node);
}

Element

package att.grappa;
public abstract synchronized class Element implements GrappaConstants {
private Graph graph;
private Subgraph subgraph;
long visastamp;
private boolean deleteCalled;
private boolean busy;
private static java.util.Hashtable userAttributeTypeMap;
public Object object;
public boolean visible;
public boolean selectable;
public int linewidth;
public static boolean usePrintList;
public int counter;
public int highlight;
private Long idKey;
String name;
java.util.Hashtable attributes;
java.util.Hashtable attrsOfInterest;
GrappaNexus grappaNexus;
public boolean printAllAttributes;
public boolean printDefaultAttributes;
String canonName;
private double patchSize;
private java.awt.geom.Rectangle2D$Double patch;
protected void Element();
protected void Element(int, Subgraph);
private void elementAttrsOfInterest();
public abstract int getType();
public boolean isNode();
public boolean isEdge();
public boolean isSubgraph();
abstract void setName();
public String getName();
boolean reserve();
void release();
boolean setDelete(boolean);
private synchronized boolean setReserved(boolean, boolean);
protected void attrOfInterest(String);
protected void attrNotOfInterest(String);
public java.util.Enumeration listAttrsOfInterest();
public boolean isOfInterest(String);
public Object setAttribute(Attribute);
public Object setAttribute(String, Object);
private Attribute removeAttribute(String);
public Object setDefaultAttribute(String, Object);
public Object setDefaultAttribute(int, String, Object);
public Object setDefaultAttribute(Attribute);
public Object setDefaultAttribute(int, Attribute);
public java.util.Enumeration getLocalAttributeKeys();
public java.util.Enumeration getLocalAttributePairs();
public java.util.Enumeration getAttributePairs();
public Attribute getLocalAttribute(String);
public Attribute getThisAttribute(String);
public Object getThisAttributeValue(String);
public Attribute getDefaultAttribute(int, String);
public Attribute getDefaultAttribute(String);
public Attribute getAttribute(String);
public Object getAttributeValue(String);
public boolean hasAttributeForKey(String);
public Graph getGraph();
public Subgraph getSubgraph();
void setGraph(Graph);
public void setSubgraph(Subgraph);
protected void clearBBox();
public int getId();
public Long getIdKey();
protected void setIdKey(int);
public void printElement(java.io.PrintWriter);
private void printAttributes(java.io.PrintWriter, String);
public String toString();
public static String canonString(String);
public static final String typeString(int, boolean);
public static String canonValue(String);
boolean deleteCalled();
public final boolean delete();
public void addTag(String);
public boolean hasTag(String);
public boolean hasLocalTag(String);
public boolean hasDefaultTag(String);
public boolean hasTags();
public boolean hasLocalTags();
public boolean hasDefaultTags();
public void removeTags();
public void removeTag(String);
public static int setUserAttributeType(String, int);
public static int attributeType(String);
public void buildShape();
public GrappaNexus getGrappaNexus();
public java.util.Vector bdfs(int);
private static void doBDFS(int, int, long, int, java.util.Vector, java.util.Vector);
double getPatchSize();
void setPatchSize(double);
java.awt.geom.Rectangle2D$Double getPatch();
void setPatch(java.awt.geom.Rectangle2D$Double);
void setPatch(double, double, double, double);
static void ();
}

Node$Enumerator

package att.grappa;
synchronized class Node$Enumerator implements java.util.Enumeration {
int inCnt;
int outCnt;
java.util.Vector inEdges;
java.util.Vector outEdges;
void Node$Enumerator(Node, java.util.Vector, java.util.Vector);
public boolean hasMoreElements();
public Object nextElement();
}

Node

package att.grappa;
public synchronized class Node extends Element {
public static final String defaultNamePrefix = N;
private java.util.Vector inEdges;
private java.util.Vector outEdges;
private java.util.Vector Ports;
public void Node(Subgraph, String);
public void Node(Subgraph);
private void nodeAttrsOfInterest();
public boolean isNode();
public int getType();
void setName();
public void setName(String) throws IllegalArgumentException;
private void resetEdgeNames();
public synchronized void addEdge(Edge, boolean);
public Edge findOutEdgeByKey(Node, String);
public Edge findInEdgeByKey(Node, String);
public GrappaPoint getCenterPoint();
public synchronized void removeEdge(Edge, boolean);
public void printNode(java.io.PrintWriter);
public static int attributeType(String);
public java.util.Enumeration edgeElements();
public java.util.Enumeration inEdgeElements();
public java.util.Enumeration outEdgeElements();
}

Subgraph$Enumerator

package att.grappa;
synchronized class Subgraph$Enumerator implements GraphEnumeration {
private Subgraph root;
private int types;
private java.util.Enumeration enm;
private GraphEnumeration subEnum;
private Element elem;
private int dictType;
void Subgraph$Enumerator(Subgraph, int);
public boolean hasMoreElements();
public Object nextElement();
public Element nextGraphElement();
public Subgraph getSubgraphRoot();
public int getEnumerationTypes();
}

Subgraph

package att.grappa;
public synchronized class Subgraph extends Element implements java.util.Comparator {
public static final String defaultNamePrefix = G;
private java.util.Hashtable nodedict;
private java.util.Hashtable edgedict;
private java.util.Hashtable graphdict;
private boolean nodeLabels;
private boolean edgeLabels;
private boolean subgLabels;
private java.util.Hashtable nodeAttributes;
private java.util.Hashtable edgeAttributes;
private boolean cluster;
public Object currentSelection;
private double PATCHEDGE;
private double PATCHEDGE2;
private Element[] sgPatches;
private Element[] elPatches;
private GrappaBox patch;
void Subgraph();
public void Subgraph(Subgraph, String);
public void Subgraph(Subgraph);
private void subgraphAttrsOfInterest();
public boolean isSubgraph();
public int getType();
void setName();
public void setName(String) throws IllegalArgumentException;
public boolean isCluster();
public boolean isRoot();
public Attribute getNodeAttribute(String);
public Object getNodeAttributeValue(String);
public java.util.Enumeration getNodeAttributeKeys();
public java.util.Enumeration getNodeAttributePairs();
public Object setNodeAttribute(Attribute);
public Object setNodeAttribute(String, Object);
private void removeNodeAttribute(String);
public Object setEdgeAttribute(Attribute);
public Object setEdgeAttribute(String, Object);
private void removeEdgeAttribute(String);
public Object setAttribute(Attribute);
public Object setAttribute(String, Object);
public Attribute getEdgeAttribute(String);
public Object getEdgeAttributeValue(String);
public java.util.Enumeration getEdgeAttributeKeys();
public java.util.Enumeration getEdgeAttributePairs();
public java.awt.geom.Rectangle2D getBoundingBox();
public java.awt.geom.Rectangle2D resetBoundingBox();
public void printSubgraph(java.io.PrintWriter);
private void printDflt(java.io.PrintWriter, int);
private void printDfltAttr(java.io.PrintWriter, java.util.Hashtable, int, String, String);
public static int attributeType(String);
private Attribute getParentDefault(int, String);
private Element findElementByName(int, String);
private Element findElementInSubgraphByName(int, String);
public Node findNodeByName(String);
public Edge findEdgeByName(String);
public Subgraph findSubgraphByName(String);
public Element createElement(int, Object[], Attribute[]);
public void addNode(Node);
public Node removeNode(String);
public void addEdge(Edge);
public Edge removeEdge(String);
public void addSubgraph(Subgraph);
public Subgraph removeSubgraph(String);
public boolean setShowSubgraphLabels(boolean);
public boolean setShowNodeLabels(boolean);
public boolean setShowEdgeLabels(boolean);
public boolean getShowSubgraphLabels();
public boolean getShowNodeLabels();
public boolean getShowEdgeLabels();
public boolean isLR();
public void addTypeTag(int, String);
public boolean hasTypeTag(int, String);
public boolean hasTypeTags(int);
public void removeTypeTags(int);
public void removeTypeTag(int, String);
public int countOfLocalElements(int);
public int countOfElements(int);
public void removeEmptySubgraphs();
public boolean hasEmptySubgraphs();
public void clearPatchWork();
public void patchWork(java.awt.geom.Rectangle2D$Double, boolean, int);
public double preparePatchWork(int);
Element[] getPatches();
private void combPatchWork();
private double prepPatchWork(String, int);
double aspect(java.awt.geom.Rectangle2D$Double);
double score(double, double);
public void computePatchWork(java.awt.geom.Rectangle2D$Double, boolean);
private void compSqPatchWork(java.awt.geom.Rectangle2D$Double, boolean);
private void compStdPatchWork(java.awt.geom.Rectangle2D$Double, boolean);
public int compare(Object, Object);
public boolean equals(Object);
public java.util.Enumeration nodeElements();
public java.util.Enumeration edgeElements();
public java.util.Enumeration subgraphElements();
public GraphEnumeration elements(int);
public GraphEnumeration elements();
public java.util.Vector vectorOfElements(int);
void recurseVectorOfElements(int, java.util.Vector, int);
}

Graph

package att.grappa;
public synchronized class Graph extends Subgraph {
public static final String INDENT_STRING = ;
boolean filterMode;
private StringBuffer indent;
private java.io.PrintWriter errWriter;
private boolean paintCalled;
private boolean busy;
private boolean synchronizePaint;
private String toolTipText;
private java.util.List panelList;
private int gid;
private int nid;
private int eid;
private boolean editable;
private boolean menuable;
private boolean selectable;
private boolean directed;
private boolean strict;
java.util.Hashtable id2element;
private java.util.Hashtable grattributes;
private static java.util.Hashtable sysdfltNodeAttributes;
private static java.util.Hashtable sysdfltEdgeAttributes;
private static java.util.Hashtable sysdfltGraphAttributes;
public final java.awt.font.FontRenderContext REFCNTXT;
private static void putAttribute(java.util.Hashtable, int, String, String);
public void Graph(String, boolean, boolean);
public void Graph(String);
private void initialize(String);
private void setDirection(boolean);
public boolean setSynchronizePaint(boolean);
public boolean getSynchronizePaint();
public boolean dropcloth(boolean, boolean);
boolean setPaint(boolean);
private synchronized boolean setBlocked(boolean, boolean, boolean);
public Attribute getGrappaAttribute(String) throws IllegalArgumentException;
public Object getGrappaAttributeValue(String) throws IllegalArgumentException;
public Object setGrappaAttribute(String, String) throws IllegalArgumentException;
public static int attributeType(String);
public java.util.Enumeration getGrappaAttributeKeys();
public static boolean validGrappaAttributeKey(String);
public static Attribute getGlobalAttribute(int, String) throws IllegalArgumentException;
public static java.util.Enumeration getGlobalAttributeKeys(int) throws IllegalArgumentException;
public static java.util.Enumeration getGlobalAttributePairs(int) throws IllegalArgumentException;
public static int getGlobalAttributeSize(int) throws IllegalArgumentException;
Element addIdMapping(Element);
static Long idMapKey(int, int) throws IllegalArgumentException;
static int idKeyType(Long);
static int idKeyId(Long);
Element element4Id(Long);
void removeIdMapping(Element);
public void printGraph(java.io.Writer);
public void printGraph(java.io.OutputStream);
int nextId(int) throws IllegalArgumentException;
public int getId(int) throws IllegalArgumentException;
public String getIndent();
public void incrementIndent();
public void decrementIndent();
public boolean isDirected();
public boolean isStrict();
public String setToolTipText(String);
public String getToolTipText();
public void reset();
public void reset(String, boolean, boolean);
public boolean isEditable();
public boolean setEditable(boolean);
public boolean isSelectable();
public boolean setSelectable(boolean);
public boolean isMenuable();
public boolean setMenuable(boolean);
public java.io.PrintWriter setErrorWriter(java.io.PrintWriter);
public java.io.PrintWriter getErrorWriter();
public void printError(String);
public void printError(String, Exception);
public void buildShapes();
public void resync();
public void repaint();
public void paintImmediately();
public void addPanel(GrappaPanel);
public void removePanel(GrappaPanel);
static void ();
}

GrappaNexus

package att.grappa;
public synchronized class GrappaNexus implements GrappaConstants, Cloneable, java.awt.image.ImageObserver, java.util.Observer, java.awt.Shape {
public static double arcHeightFactor;
public static double arcWidthFactor;
java.awt.geom.Area textArea;
java.awt.Shape shape;
int shapeType;
java.awt.geom.Rectangle2D bbox;
GrappaStyle style;
java.awt.Color fillcolor;
java.awt.Color color;
java.awt.Image image;
boolean imageLoading;
boolean dirty;
java.awt.Stroke stroke;
private Object[] objs;
private Object custom_shape;
public boolean boundText;
public boolean clearText;
public boolean drawText;
Element element;
long lastUpdate;
private long lastShapeUpdate;
private long lastTextUpdate;
private long lastStyleUpdate;
private long lastDecorationUpdate;
private long lastImageUpdate;
java.awt.Font font;
String[] lstr;
GrappaPoint[] lpos;
java.awt.Color font_color;
private int windingRule;
public void GrappaNexus(Element);
public Element getElement();
public java.awt.Image getImage();
public boolean isImageLoading();
public int getWindingRule();
public void rebuild();
public void updateShape();
public void updateStyle();
public void updateText();
public void updateDecoration();
public void updateImage();
public final boolean imageUpdate(java.awt.Image, int, int, int, int, int);
private void bboxCheckSet();
public Object clone();
public boolean contains(double, double);
public boolean contains(double, double, double, double);
public boolean contains(java.awt.geom.Point2D);
public boolean contains(java.awt.geom.Rectangle2D);
public java.awt.Rectangle getBounds();
public java.awt.geom.Rectangle2D getBounds2D();
java.awt.geom.Rectangle2D rawBounds2D();
public java.awt.geom.PathIterator getPathIterator();
public java.awt.geom.PathIterator getPathIterator(java.awt.geom.AffineTransform);
public java.awt.geom.PathIterator getPathIterator(java.awt.geom.AffineTransform, double);
public boolean intersects(double, double, double, double);
public boolean intersects(java.awt.geom.Rectangle2D);
public void update(java.util.Observable, Object);
void draw(java.awt.Graphics2D);
void fill(java.awt.Graphics2D);
void drawImage(java.awt.Graphics2D);
static void ();
}

GrappaPoint

package att.grappa;
public synchronized class GrappaPoint extends java.awt.geom.Point2D$Double {
public void GrappaPoint();
public void GrappaPoint(double, double);
public void GrappaPoint(String);
public String toAttributeString();
public String toFormattedString(String);
public String toString();
}

GrappaBox

package att.grappa;
public synchronized class GrappaBox extends java.awt.geom.Rectangle2D$Double {
private boolean dimensioned;
public void GrappaBox();
public void GrappaBox(java.awt.geom.Rectangle2D);
public void GrappaBox(double, double, double, double);
public void GrappaBox(String, boolean);
public void GrappaBox(String);
public String toAttributeString();
public String toFormattedString(String);
public String toString();
public boolean isDimensioned();
}

GraphEnumeration

package att.grappa;
public abstract interface GraphEnumeration extends java.util.Enumeration {
public abstract Subgraph getSubgraphRoot();
public abstract int getEnumerationTypes();
public abstract Element nextGraphElement() throws java.util.NoSuchElementException;
}

GrappaPanel

package att.grappa;
public synchronized class GrappaPanel extends javax.swing.JPanel implements GrappaConstants, java.awt.event.ComponentListener, javax.swing.event.AncestorListener, javax.swing.event.PopupMenuListener, java.awt.event.MouseListener, java.awt.event.MouseMotionListener, java.awt.print.Printable, Runnable, javax.swing.Scrollable {
Graph graph;
Subgraph subgraph;
GrappaBacker backer;
boolean nodeLabels;
boolean edgeLabels;
boolean subgLabels;
java.awt.geom.AffineTransform transform;
java.awt.geom.AffineTransform oldTransform;
java.awt.geom.AffineTransform inverseTransform;
java.util.Vector elementVector;
int nextElement;
boolean scaleToFit;
GrappaSize scaleToSize;
GrappaListener grappaListener;
private Element pressedElement;
private GrappaPoint pressedPoint;
private int pressedModifiers;
private GrappaStyle selectionStyle;
private GrappaStyle deletionStyle;
private double scaleFactor;
private double scaleInfo;
private GrappaBox outline;
private GrappaBox savedOutline;
private GrappaBox zoomBox;
private boolean inMenu;
private boolean scaleChanged;
private boolean paintActive;
private java.awt.geom.Point2D panelcpt;
public void GrappaPanel(Subgraph);
public void GrappaPanel(Subgraph, GrappaBacker);
public GrappaListener addGrappaListener(GrappaListener);
public GrappaListener removeGrappaListener();
public int print(java.awt.Graphics, java.awt.print.PageFormat, int) throws java.awt.print.PrinterException;
public void paintComponent(java.awt.Graphics);
void setCPT(java.awt.geom.Point2D);
java.awt.geom.Point2D getCPT();
private java.awt.geom.Point2D componentPaint(java.awt.Graphics);
public void centerPanelAtPoint(java.awt.geom.Point2D);
public java.awt.geom.AffineTransform getTransform();
public java.awt.geom.AffineTransform getInverseTransform();
public void setToolTipText(String);
public String getToolTipText(java.awt.event.MouseEvent);
public void setScaleToFit(boolean);
public void setScaleToSize(java.awt.geom.Dimension2D);
public Subgraph getSubgraph();
public void resetZoom();
public boolean hasOutline();
public void clearOutline();
public GrappaBox zoomToOutline();
public GrappaBox zoomToOutline(GrappaBox);
public double multiplyScaleFactor(double);
private void paintSubgraph(java.awt.Graphics2D, Subgraph, java.awt.Shape, java.awt.Color);
private Element findContainingElement(Subgraph, java.awt.geom.Point2D);
private Element findContainingElement(Subgraph, java.awt.geom.Point2D, Element);
private Element reallyFindContainingElement(Subgraph, java.awt.geom.Point2D, Element[]);
public void ancestorMoved(javax.swing.event.AncestorEvent);
public void ancestorAdded(javax.swing.event.AncestorEvent);
public void ancestorRemoved(javax.swing.event.AncestorEvent);
public void componentHidden(java.awt.event.ComponentEvent);
public void componentMoved(java.awt.event.ComponentEvent);
public void componentResized(java.awt.event.ComponentEvent);
public void componentShown(java.awt.event.ComponentEvent);
public void popupMenuCanceled(javax.swing.event.PopupMenuEvent);
public void popupMenuWillBecomeInvisible(javax.swing.event.PopupMenuEvent);
public void popupMenuWillBecomeVisible(javax.swing.event.PopupMenuEvent);
public void mouseClicked(java.awt.event.MouseEvent);
public void mousePressed(java.awt.event.MouseEvent);
public void mouseReleased(java.awt.event.MouseEvent);
public void mouseEntered(java.awt.event.MouseEvent);
public void mouseExited(java.awt.event.MouseEvent);
public void mouseDragged(java.awt.event.MouseEvent);
public void mouseMoved(java.awt.event.MouseEvent);
public java.awt.Dimension getPreferredScrollableViewportSize();
public int getScrollableUnitIncrement(java.awt.Rectangle, int, int);
public int getScrollableBlockIncrement(java.awt.Rectangle, int, int);
public boolean getScrollableTracksViewportWidth();
public boolean getScrollableTracksViewportHeight();
public void run();
}

GrappaStyle

package att.grappa;
public synchronized class GrappaStyle implements GrappaConstants, Cloneable {
private int elementType;
public static final String DEFAULT_SET_STRING = __default__;
public static final int STYLE_SOLID = 0;
public static final int STYLE_DASHED = 1;
public static final int STYLE_DOTTED = 2;
public static final int STYLE_DASH = 3;
public static final int STYLE_DASH_PHASE = 4;
public static final int STYLE_LINE_WIDTH = 5;
public static final int STYLE_LINE_COLOR = 6;
public static final int STYLE_FILLED = 7;
public static final int STYLE_DIAGONALS = 8;
public static final int STYLE_ROUNDED = 9;
public static final int STYLE_CAP_BUTT = 10;
public static final int STYLE_CAP_ROUND = 11;
public static final int STYLE_CAP_SQUARE = 12;
public static final int STYLE_JOIN_BEVEL = 13;
public static final int STYLE_JOIN_MITER = 14;
public static final int STYLE_JOIN_ROUND = 15;
public static final int STYLE_MITER_LIMIT = 16;
public static final int STYLE_FIXED_SIZE = 17;
public static final int STYLE_INVIS = 18;
public static final int STYLE_OLD_BOLD = 100;
public static final int STYLE_OLD_ITALIC = 101;
public static final int STYLE_OLD_PLAIN = 102;
public static final java.awt.Color STYLE_LINE_COLOR_DEFAULT;
public static final int STYLE_LINE_STYLE_DEFAULT = 0;
public static final float STYLE_LINE_WIDTH_DEFAULT = 1.0;
public static final int STYLE_CAP_DEFAULT = 0;
public static final int STYLE_JOIN_DEFAULT = 2;
public static final float STYLE_MITER_LIMIT_DEFAULT = 0.0;
public static final float[] STYLE_DASH_DEFAULT;
public static final float STYLE_DASH_PHASE_DEFAULT = 0.0;
public static final boolean STYLE_ROUNDED_DEFAULT = 0;
public static final boolean STYLE_DIAGONALS_DEFAULT = 0;
public static final boolean STYLE_FILLED_DEFAULT = 0;
public static final boolean STYLE_INVIS_DEFAULT = 0;
public static final boolean STYLE_FIXED_SIZE_DEFAULT = 0;
static java.awt.BasicStroke defaultStroke;
static String defaultStrokeString;
private static java.util.Hashtable styleTypes;
private static java.util.Hashtable strokeCache;
java.awt.Color line_color;
int line_style;
float line_width;
int cap;
int join;
float miter_limit;
float[] dash;
float dash_phase;
boolean rounded;
boolean diagonals;
boolean filled;
boolean invis;
boolean fixed_size;
Integer font_style;
java.awt.BasicStroke stroke;
public void GrappaStyle(int, String);
public void updateStyle(String);
private static String generateStrokeString(float, int, int, float, float[], float);
public String toAttributeString();
public String toString();
private static String generateStyleString(java.awt.Color, int, float, int, int, float, float[], float, boolean, boolean, boolean, boolean, boolean, Integer, boolean, int);
public java.awt.Color getLineColor();
public int getLineStyle();
public float getLineWidth();
public int getCapStyle();
public int getJoinStyle();
public float getMiterLimit();
public float[] getDash();
public float getDashPhase();
public boolean getRounded();
public boolean getDiagonals();
public boolean getFilled();
public boolean getInvis();
public boolean getFixedSize();
public int getFontStyle();
public Object clone();
static void ();
}

GrappaBacker

package att.grappa;
public abstract interface GrappaBacker {
public abstract void drawBackground(java.awt.Graphics2D, Graph, java.awt.geom.Rectangle2D, java.awt.Shape);
}

GrappaSize

package att.grappa;
public synchronized class GrappaSize extends java.awt.geom.Dimension2D {
public double width;
public double height;
public void GrappaSize();
public void GrappaSize(double, double);
public void GrappaSize(String);
public double getWidth();
public double getHeight();
public void setSize(java.awt.geom.Dimension2D);
public void setSize(double, double);
public String toAttributeString();
public String toFormattedString(String);
public String toString();
}

GrappaListener

package att.grappa;
public abstract interface GrappaListener {
public abstract void grappaClicked(Subgraph, Element, GrappaPoint, int, int, GrappaPanel);
public abstract void grappaPressed(Subgraph, Element, GrappaPoint, int, GrappaPanel);
public abstract void grappaReleased(Subgraph, Element, GrappaPoint, int, Element, GrappaPoint, int, GrappaBox, GrappaPanel);
public abstract void grappaDragged(Subgraph, GrappaPoint, int, Element, GrappaPoint, int, GrappaBox, GrappaPanel);
public abstract String grappaTip(Subgraph, Element, GrappaPoint, int, GrappaPanel);
}

ExceptionDisplay$Display

package att.grappa;
synchronized class ExceptionDisplay$Display extends java.awt.Frame {
private java.awt.TextArea textarea;
private java.awt.Panel buttonPanel;
private java.awt.Button trace;
private java.awt.Button dismiss;
private ExceptionDisplay$Display$WindowObserver observer;
void ExceptionDisplay$Display(ExceptionDisplay, String);
void setText(String);
Exception getException();
}

ExceptionDisplay$Display$WindowObserver

package att.grappa;
synchronized class ExceptionDisplay$Display$WindowObserver extends java.awt.event.WindowAdapter implements java.awt.event.ActionListener {
void ExceptionDisplay$Display$WindowObserver(ExceptionDisplay$Display);
public void windowClosing(java.awt.event.WindowEvent);
private void dismiss();
public void actionPerformed(java.awt.event.ActionEvent);
}

ExceptionDisplay

package att.grappa;
public synchronized class ExceptionDisplay {
private String title;
Exception exception;
ExceptionDisplay$Display display;
public void ExceptionDisplay(String);
public void displayException(Exception);
public void displayException(Exception, String);
}

GraphParserException

package att.grappa;
public synchronized class GraphParserException extends RuntimeException {
public void GraphParserException();
public void GraphParserException(String);
}

Grappa

package att.grappa;
public abstract synchronized class Grappa implements GrappaConstants {
public static java.util.Hashtable keyToShape;
public static java.util.Hashtable shapeToKey;
public static java.awt.Toolkit toolkit;
private static final ExceptionDisplay exceptionDisplay;
public static boolean doDisplayException;
public static final java.util.Vector emptyEnumeration;
private static String toolTipText;
public static boolean elementPrintAllAttributes;
public static boolean elementPrintDefaultAttributes;
public static boolean shapeBoundText;
public static boolean shapeClearText;
public static boolean shapeDrawText;
public static boolean centerPointNodes;
public static boolean autoPositionNodeLabel;
public static boolean provideBBoxAttribute;
public static int windingRule;
public static boolean orientationInDegrees;
public static boolean rotationInDegrees;
public static boolean usePrintList;
public static boolean printVisibleOnly;
public static boolean useAntiAliasing;
public static boolean antiAliasText;
public static boolean useFractionalMetrics;
public static boolean negateStringYCoord;
public static boolean labelGraphBottom;
public static boolean labelGraphOutside;
public static boolean backgroundDrawing;
public static double nodeLabelsScaleCutoff;
public static boolean outlineSubgraphs;
public static double edgeLabelsScaleCutoff;
public static double subgLabelsScaleCutoff;
public static boolean synchronizePaint;
public static boolean waitForImages;
public static int elementSelection;
public void Grappa();
public static void displayException(Exception);
public static void displayException(Exception, String);
public static String setToolTipText(String);
public static String getToolTipText();
static void ();
}

GrappaAdapter

package att.grappa;
public synchronized class GrappaAdapter implements GrappaConstants, GrappaListener, java.awt.event.ActionListener {
public void GrappaAdapter();
public void grappaClicked(Subgraph, Element, GrappaPoint, int, int, GrappaPanel);
public void grappaPressed(Subgraph, Element, GrappaPoint, int, GrappaPanel);
public void grappaReleased(Subgraph, Element, GrappaPoint, int, Element, GrappaPoint, int, GrappaBox, GrappaPanel);
public void grappaDragged(Subgraph, GrappaPoint, int, Element, GrappaPoint, int, GrappaBox, GrappaPanel);
public String grappaTip(Subgraph, Element, GrappaPoint, int, GrappaPanel);
public void actionPerformed(java.awt.event.ActionEvent);
protected void drillDown(Subgraph, java.util.Vector, int, int);
}

GrappaColor

package att.grappa;
public abstract synchronized class GrappaColor {
private static java.util.Hashtable colorTable;
private static java.util.Hashtable colorLookUp;
public static final java.awt.Color defaultForeground;
public static final java.awt.Color defaultBackground;
public static final java.awt.Color defaultXOR;
public static final java.awt.Color defaultFontcolor;
public static final java.awt.Color defaultColor;
public void GrappaColor();
public static void addColor(String, java.awt.Color) throws IllegalArgumentException;
private static void doAddColor(String, java.awt.Color, boolean);
private static void doAddColor(String, java.awt.Color);
private static String canonColor(String, float[]);
public static java.awt.Color getColor(String, java.awt.Color);
public static String getColorName(java.awt.Color);
static void ();
}

GrappaLine

package att.grappa;
public synchronized class GrappaLine implements GrappaConstants, Cloneable, java.awt.Shape {
public static final double arrowLength = 10.0;
public static final double arrowWidth = 5.0;
public static final int NONE_ARROW_EDGE = 0;
public static final int HEAD_ARROW_EDGE = 1;
public static final int TAIL_ARROW_EDGE = 2;
public static final int BOTH_ARROW_EDGE = 3;
private java.awt.geom.GeneralPath path;
private java.awt.geom.GeneralPath testpath;
private int arrow;
private GrappaPoint[] gpts;
private int windingRule;
public void GrappaLine(GrappaPoint[], int);
public void GrappaLine(String);
public boolean equals(Object);
public int getArrowType();
public int getWindingRule();
public boolean startsNear(java.awt.geom.Point2D);
public String toAttributeString();
public String toFormattedString(String);
public String toString();
public boolean changeArrowType(int);
private void addArrow(java.awt.geom.GeneralPath, java.awt.geom.GeneralPath, GrappaPoint, GrappaPoint, double, double);
private void updateLine(String);
private void updateLine(GrappaPoint[], int);
public Object clone();
public final boolean contains(double, double);
public final boolean contains(double, double, double, double);
public final boolean contains(java.awt.geom.Point2D);
public final boolean contains(java.awt.geom.Rectangle2D);
public final java.awt.Rectangle getBounds();
public final java.awt.geom.Rectangle2D getBounds2D();
public final java.awt.geom.PathIterator getPathIterator();
public final java.awt.geom.PathIterator getPathIterator(java.awt.geom.AffineTransform);
public final java.awt.geom.PathIterator getPathIterator(java.awt.geom.AffineTransform, double);
public final boolean intersects(double, double, double, double);
public final boolean intersects(java.awt.geom.Rectangle2D);
}

GrappaPathIterator

package att.grappa;
public synchronized class GrappaPathIterator implements java.awt.geom.PathIterator {
GrappaNexus grappaNexus;
java.awt.geom.AffineTransform affine;
java.awt.geom.PathIterator shapeIterator;
java.awt.geom.PathIterator areaIterator;
double[] pts;
int type;
public void GrappaPathIterator(GrappaNexus);
public void GrappaPathIterator(GrappaNexus, java.awt.geom.AffineTransform);
public int currentSegment(double[]);
public int currentSegment(float[]);
public int getWindingRule();
public boolean isDone();
public void next();
}

GrappaShape

package att.grappa;
public synchronized class GrappaShape implements GrappaConstants, Cloneable, java.awt.Shape {
protected java.awt.geom.GeneralPath path;
private static final double RBCONST = 12.0;
private static final double RBCURVE = 0.5;
private static final double CIRCLE_XDIAG;
private static final double CIRCLE_YDIAG = 0.75;
public void GrappaShape(int, double, double, double, double, int, int, double, double, double, boolean, boolean, Object);
public Object clone();
public final boolean contains(double, double);
public final boolean contains(double, double, double, double);
public final boolean contains(java.awt.geom.Point2D);
public final boolean contains(java.awt.geom.Rectangle2D);
public final java.awt.Rectangle getBounds();
public final java.awt.geom.Rectangle2D getBounds2D();
public final java.awt.geom.PathIterator getPathIterator();
public final java.awt.geom.PathIterator getPathIterator(java.awt.geom.AffineTransform);
public final java.awt.geom.PathIterator getPathIterator(java.awt.geom.AffineTransform, double);
public final boolean intersects(double, double, double, double);
public final boolean intersects(java.awt.geom.Rectangle2D);
static void ();
}

GrappaSupport

package att.grappa;
public abstract synchronized class GrappaSupport implements GrappaConstants {
private static final short CN = 1;
private static final short WS = 2;
private static final short SP = 4;
private static final short PU = 8;
private static final short DG = 16;
private static final short OD = 32;
private static final short UC = 64;
private static final short HD = 128;
private static final short LC = 256;
private static final short[] ctype;
private static final short ALPHA = 320;
private static final short ALNUM = 336;
private static final short GRAPH = 344;
private static final short PRINT = 348;
private static final short ODIGIT = 32;
private static final short XDIGIT = 144;
private static final short LOWERTOUPPER = -32;
private static final short UPPERTOLOWER = 32;
public void GrappaSupport();
static boolean isalnum(int);
static boolean isalpha(int);
static boolean isascii(int);
static boolean iscntrl(int);
static boolean isdigit(int);
static boolean isgraph(int);
static boolean islower(int);
static boolean isoctal(int);
static boolean isprint(int);
static boolean ispunct(int);
static boolean isspace(int);
static boolean isupper(int);
static boolean isxdigit(int);
static int tolower(int);
static int toupper(int);
static String[] strsplit(String) throws IllegalArgumentException;
static float[] floatArrayForTuple(String) throws IllegalArgumentException, NumberFormatException;
static double[] arrayForTuple(String) throws IllegalArgumentException, NumberFormatException;
public static int xlateDirString(String);
public static String xlateDir(int);
public static int xlateFontStyleString(String);
public static String xlateFontStyle(int);
public static String canonize(String);
public static GrappaBox boxFromCorners(double, double, double, double);
public static GrappaBox boxFromCorners(GrappaBox, double, double, double, double);
public static Element findContainingElement(Subgraph, java.awt.geom.Point2D);
public static java.util.Vector findContainedElements(Subgraph, GrappaBox);
public static void setHighlight(Element, int, int);
public static boolean filterGraph(Graph, Object);
public static boolean filterGraph(Graph, Object, String);
public static boolean centerPanel(java.awt.geom.Point2D, GrappaPanel);
static void ();
}

GrappaSupportPrintf

package att.grappa;
public synchronized class GrappaSupportPrintf implements GrappaConstants {
public void GrappaSupportPrintf();
public static final String sprintf(Object[]);
}

PrintfParser

package att.grappa;
synchronized class PrintfParser implements GrappaConstants {
private boolean alternate;
private boolean rightpad;
private boolean sign;
private boolean space;
private boolean zeropad;
private boolean trim;
private int precision;
private int width;
private String plus;
private char padding;
private StringBuffer scratch;
void PrintfParser();
final int parse(char[]);
final int parse(char[], int);
final StringBuffer buildChar(StringBuffer, int);
final StringBuffer buildExp(StringBuffer, double, boolean);
final StringBuffer buildFlex(StringBuffer, double, boolean);
final StringBuffer buildPoint(StringBuffer, java.awt.geom.Point2D, boolean);
final StringBuffer buildSize(StringBuffer, java.awt.geom.Dimension2D, boolean);
final StringBuffer buildBox(StringBuffer, java.awt.geom.Rectangle2D, boolean, boolean);
final StringBuffer buildFloat(StringBuffer, double);
final StringBuffer buildHex(StringBuffer, int, boolean);
final StringBuffer buildInteger(StringBuffer, long);
final StringBuffer buildOctal(StringBuffer, int);
final StringBuffer buildString(StringBuffer, String);
private String doubleToString(double, String);
private StringBuffer strpad(StringBuffer, String, int, int, boolean);
}

GrappaSupportRects

package att.grappa;
public synchronized class GrappaSupportRects implements GrappaConstants {
static final double[] romanFontwidth;
static final double[] helveticaFontwidth;
static final double constantFontwidth = 0.6206;
static final int HASTEXT = 1;
static final int HASPORT = 2;
static final int HASTABLE = 4;
static final int INTEXT = 8;
static final int INPORT = 16;
static final char NBSP = 160;
private static char[] parseArray;
private static int arrayOffset;
private static int fields;
private static StringBuffer rbuf;
public void GrappaSupportRects();
protected static synchronized Object[] parseRecordInfo(Node);
private static boolean emitFields(TableField, Object[]);
private static TableField doParse(Node, boolean, boolean);
private static boolean isSpec(char);
static void ();
}

TableField

package att.grappa;
synchronized class TableField implements GrappaConstants {
private java.awt.Dimension size;
private java.awt.Rectangle bounds;
private java.awt.Rectangle textBounds;
private TableField[] subFields;
private int subFieldsUsed;
private boolean orientLR;
private String idTag;
private String text;
private TableField parent;
void TableField();
void setParent(TableField);
TableField getTopMost();
TableField getParent();
String getText();
String getIdentifier();
java.awt.Rectangle getBounds();
void setBounds(int, int, int, int);
void setBounds(java.awt.Rectangle);
java.awt.Dimension getSize();
void setSize(int, int);
void setSize(java.awt.Dimension);
boolean hasFields();
synchronized int subfields(int);
int fieldCount();
synchronized void addField(TableField);
TableField fieldAt(int);
boolean isLR();
void setLR(boolean);
String getId();
void setId(String);
java.awt.Dimension sizeFields();
private java.awt.Dimension sizeUpFields(TableField);
java.awt.Dimension resizeFields(java.awt.Dimension);
void resizeUpFields(TableField, java.awt.Dimension);
void positionFields(java.awt.Point);
private void posFields(TableField, java.awt.Point);
void setTextBounds(String, Node);
java.awt.Rectangle getTextBounds();
void debugID();
}

Lexer

package att.grappa;
public synchronized class Lexer {
private int next_char;
private int next_char2;
private int current_line;
private int current_position;
private static final int EOF_CHAR = -1;
private boolean haveId;
private int old_char;
private int old_position;
boolean retreated;
private int error_count;
private int warning_count;
private java.util.Hashtable keywords;
private java.util.Hashtable char_symbols;
private java.io.Reader inReader;
private java.io.PrintWriter errWriter;
private StringBuffer cmnstrbuf;
public void Lexer(java.io.Reader, java.io.PrintWriter) throws IllegalArgumentException;
public void init() throws java.io.IOException;
public void advance() throws java.io.IOException;
private void retreat();
private void emit_error(String);
public String getLocation();
private void emit_warn(String);
public static boolean id_char(int);
public static boolean id_char(char);
private int find_single_char(int);
private void swallow_comment() throws java.io.IOException;
private java_cup.runtime.Symbol do_quote_string() throws java.io.IOException;
private java_cup.runtime.Symbol do_id() throws java.io.IOException;
private java_cup.runtime.Symbol real_next_token() throws java.io.IOException;
public java_cup.runtime.Symbol next_token(int) throws java.io.IOException;
}

Parser

package att.grappa;
public synchronized class Parser extends java_cup.runtime.lr_parser {
protected static final short[][] _production_table;
protected static final short[][] _action_table;
protected static final short[][] _reduce_table;
protected CUP$Parser$actions action_obj;
private Graph theGraph;
private java.io.Reader inReader;
private java.io.PrintWriter errWriter;
private Lexer lexer;
private int debugLevel;
public void Parser();
public void Parser(java_cup.runtime.Scanner);
public short[][] production_table();
public short[][] action_table();
public short[][] reduce_table();
protected void init_actions();
public java_cup.runtime.Symbol do_action(int, java_cup.runtime.lr_parser, java.util.Stack, int) throws Exception;
public int start_state();
public int start_production();
public int EOF_sym();
public int error_sym();
public void user_init() throws Exception;
public java_cup.runtime.Symbol scan() throws Exception;
public void Parser(java.io.Reader, java.io.PrintWriter, Graph);
public void Parser(java.io.Reader, java.io.PrintWriter);
public void Parser(java.io.Reader);
public void Parser(java.io.InputStream, java.io.OutputStream, Graph);
public void Parser(java.io.InputStream, java.io.OutputStream);
public void Parser(java.io.InputStream);
public Lexer getLexer();
public java.io.PrintWriter getErrorWriter();
public int getDebugLevel();
public void report_error(String, Object) throws GraphParserException;
public void report_warning(String, Object);
public void debug_message(String);
public void debug_message(int, String);
public java_cup.runtime.Symbol debug_parse(int) throws Exception;
CUP$Parser$actions getActionObject();
public Graph getGraph();
static void ();
}

CUP$Parser$actions

package att.grappa;
synchronized class CUP$Parser$actions {
Subgraph rootSubgraph;
Subgraph lastSubgraph;
Graph graph;
Subgraph thisGraph;
Node thisNode;
Edge thisEdge;
Node fromNode;
Node toNode;
String portName;
String toPortName;
String fromPortName;
int thisAttrType;
int thisElemType;
boolean directed;
String graphType;
private int anon_id;
java.util.Vector attrs;
java.util.Vector nodes;
java.util.Vector edges;
private final Parser parser;
void appendAttr(String, String);
void noMacros();
void attrStmt(int, String);
void startGraph(String, boolean, boolean);
void openGraph();
void closeGraph();
void openSubg(String);
String anonStr();
void closeSubg();
void appendNode(String, String);
void nodeWrap();
void bufferEdges();
void edgeWrap();
void edgeRHS(Node, String, Object[], Attribute, Attribute);
void applyAttrs(Element, Attribute, Attribute);
void CUP$Parser$actions(Parser);
public final java_cup.runtime.Symbol CUP$Parser$do_action(int, java_cup.runtime.lr_parser, java.util.Stack, int) throws Exception;
}

Symbols

package att.grappa;
public synchronized class Symbols {
public static final int SUBGRAPH = 5;
public static final int DIGRAPH = 9;
public static final int RBR = 17;
public static final int EQUAL = 18;
public static final int D_EDGE_OP = 6;
public static final int SEMI = 12;
public static final int STRICTDIGRAPH = 11;
public static final int ATSIGN = 20;
public static final int COLON = 19;
public static final int ATOM = 21;
public static final int RCUR = 15;
public static final int STRICT = 8;
public static final int COMMA = 13;
public static final int LCUR = 14;
public static final int EOF = 0;
public static final int ND_EDGE_OP = 7;
public static final int EDGE = 4;
public static final int GRAPH = 2;
public static final int error = 1;
public static final int LBR = 16;
public static final int STRICTGRAPH = 10;
public static final int NODE = 3;
static final int graphType = 2;
static final int optAttr = 20;
static final int attrAssignment = 29;
static final int graph = 9;
static final int hdr = 10;
static final int graphAttrDefs = 25;
static final int edge_op = 31;
static final int optSeparator = 17;
static final int optSemi = 16;
static final int NT$2 = 34;
static final int NT$1 = 33;
static final int NT$0 = 32;
static final int optStmtList = 12;
static final int $START = 0;
static final int subgraph = 22;
static final int optGraphName = 6;
static final int body = 11;
static final int optSubgHdr = 5;
static final int attrList = 24;
static final int attrItem = 28;
static final int node = 23;
static final int rCompound = 3;
static final int attrStmt = 15;
static final int simple = 19;
static final int optAttrDefs = 26;
static final int optStrict = 1;
static final int attrMacro = 30;
static final int optMacroName = 7;
static final int attrDefs = 27;
static final int optPort = 8;
static final int compound = 18;
static final int nodeList = 21;
static final int stmt = 14;
static final int attrType = 4;
static final int stmtList = 13;
public void Symbols();
}

java_cup/runtime/Scanner.java
java_cup/runtime/Scanner.javapackage java_cup.runtime;

/**
 * Defines the Scanner interface, which CUP uses in the default
 * implementation of lr_parser.scan().  Integration
 * of scanners implementing Scanner is facilitated.
 *
 * @version last updated 23-Jul-1999
 * @author David MacMahon 
 */

/* *************************************************
  Interface Scanner
  
  Declares the next_token() method that should be
  implemented by scanners.  This method is typically
  called by lr_parser.scan().
 ***************************************************/
public interface Scanner {
    public Symbol next_token() throws java.lang.Exception;
}

java_cup/runtime/Symbol.java
java_cup/runtime/Symbol.javapackage java_cup.runtime;

/**
 * Defines the Symbol class, which is used to represent all terminals
 * and nonterminals while parsing.  The lexer should pass CUP Symbols 
 * and CUP returns a Symbol.
 *
 * @version last updated: 7/3/96
 * @author  Frank Flannery
 */

/* ****************************************************************
  Class Symbol
  what the parser expects to receive from the lexer. 
  the token is identified as follows:
  sym:    the symbol type
  parse_state: the parse state.
  value:  is the lexical value of type Object
  left :  is the left position in the original input file
  right:  is the right position in the original input file
******************************************************************/

public class Symbol {

/*******************************
  Constructor for l,r values
 *******************************/

  public Symbol(int id, int l, int r, Object o) {
    this(id);
    left = l;
    right = r;
    value = o;
  }

/*******************************
  Constructor for no l,r values
********************************/

  public Symbol(int id, Object o) {
    this(id);
    left = -1;
    right = -1;
    value = o;
  }

/*****************************
  Constructor for no value
  ***************************/

  public Symbol(int sym_num, int l, int r) {
    sym = sym_num;
    left = l;
    right = r;
    value = null;
  }

/***********************************
  Constructor for no value or l,r
***********************************/

  public Symbol(int sym_num) {
    this(sym_num, -1);
    left = -1;
    right = -1;
    value = null;
  }

/***********************************
  Constructor to give a start state
***********************************/
  public Symbol(int sym_num, int state)
    {
      sym = sym_num;
      parse_state = state;
    }

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

  /** The symbol number of the terminal or non terminal being represented */
  public int sym;

  /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

  /** The parse state to be recorded on the parse stack with this symbol.
   *  This field is for the convenience of the parser and shouldn’t be 
   *  modified except by the parser. 
   */
  public int parse_state;
  /** This allows us to catch some errors caused by scanners recycling
   *  symbols.  For the use of the parser only. [CSA, 23-Jul-1999] */
  boolean used_by_parser = false;

/*******************************
  The data passed to parser
 *******************************/

  public int left, right;
  public Object value;

  /*****************************
    Printing this token out. (Override for pretty-print).
    ****************************/
  public String toString() { return “#”+sym; }
}

java_cup/runtime/lr_parser.java
java_cup/runtime/lr_parser.java                    
package java_cup.runtime;

import java.util.Stack;

/** This class implements a skeleton table driven LR parser.  In general,
 *  LR parsers are a form of bottom up shift-reduce parsers.  Shift-reduce
 *  parsers act by shifting input onto a parse stack until the Symbols 
 *  matching the right hand side of a production appear on the top of the 
 *  stack.  Once this occurs, a reduce is performed.  This involves removing
 *  the Symbols corresponding to the right hand side of the production
 *  (the so called “handle”) and replacing them with the non-terminal from
 *  the left hand side of the production.  

 *
 *  To control the decision of whether to shift or reduce at any given point, 
 *  the parser uses a state machine (the “viable prefix recognition machine” 
 *  built by the parser generator).  The current state of the machine is placed
 *  on top of the parse stack (stored as part of a Symbol object representing
 *  a terminal or non terminal).  The parse action table is consulted 
 *  (using the current state and the current lookahead Symbol as indexes) to 
 *  determine whether to shift or to reduce.  When the parser shifts, it 
 *  changes to a new state by pushing a new Symbol (containing a new state) 
 *  onto the stack.  When the parser reduces, it pops the handle (right hand 
 *  side of a production) off the stack.  This leaves the parser in the state 
 *  it was in before any of those Symbols were matched.  Next the reduce-goto 
 *  table is consulted (using the new state and current lookahead Symbol as 
 *  indexes) to determine a new state to go to.  The parser then shifts to 
 *  this goto state by pushing the left hand side Symbol of the production 
 *  (also containing the new state) onto the stack.

 *
 *  This class actually provides four LR parsers.  The methods parse() and 
 *  debug_parse() provide two versions of the main parser (the only difference 
 *  being that debug_parse() emits debugging trace messages as it parses).  
 *  In addition to these main parsers, the error recovery mechanism uses two 
 *  more.  One of these is used to simulate “parsing ahead” in the input 
 *  without carrying out actions (to verify that a potential error recovery 
 *  has worked), and the other is used to parse through buffered “parse ahead” 
 *  input in order to execute all actions and re-synchronize the actual parser 
 *  configuration.

 *
 *  This is an abstract class which is normally filled out by a subclass
 *  generated by the JavaCup parser generator.  In addition to supplying
 *  the actual parse tables, generated code also supplies methods which 
 *  invoke various pieces of user supplied code, provide access to certain
 *  special Symbols (e.g., EOF and error), etc.  Specifically, the following
 *  abstract methods are normally supplied by generated code:
 *    *  

 short[][] production_table()
 *  

 Provides a reference to the production table (indicating the index of
 *       the left hand side non terminal and the length of the right hand side
 *       for each production in the grammar).
 *  

 short[][] action_table()
 *  

 Provides a reference to the parse action table.
 *  

 short[][] reduce_table()
 *  

 Provides a reference to the reduce-goto table.
 *  

 int start_state()      
 *  

 Indicates the index of the start state.
 *  

 int start_production() 
 *  

 Indicates the index of the starting production.
 *  

 int EOF_sym() 
 *  

 Indicates the index of the EOF Symbol.
 *  

 int error_sym() 
 *  

 Indicates the index of the error Symbol.
 *  

 Symbol do_action() 
 *  

 Executes a piece of user supplied action code.  This always comes at 
 *       the point of a reduce in the parse, so this code also allocates and 
 *       fills in the left hand side non terminal Symbol object that is to be 
 *       pushed onto the stack for the reduce.
 *  

 void init_actions()
 *  

 Code to initialize a special object that encapsulates user supplied
 *       actions (this object is used by do_action() to actually carry out the 
 *       actions).
 *  

 *  
 *  In addition to these routines that must be supplied by the 
 *  generated subclass there are also a series of routines that may 
 *  be supplied.  These include:
 *  

 *  

 Symbol scan()
 *  

 Used to get the next input Symbol from the scanner.
 *  

 Scanner getScanner()
 *  

 Used to provide a scanner for the default implementation of
 *       scan().
 *  

 int error_sync_size()
 *  

 This determines how many Symbols past the point of an error 
 *       must be parsed without error in order to consider a recovery to 
 *       be valid.  This defaults to 3.  Values less than 2 are not 
 *       recommended.
 *  

 void report_error(String message, Object info)
 *  

 This method is called to report an error.  The default implementation
 *       simply prints a message to System.err and where the error occurred.
 *       This method is often replaced in order to provide a more sophisticated
 *       error reporting mechanism.
 *  

 void report_fatal_error(String message, Object info)
 *  

 This method is called when a fatal error that cannot be recovered from
 *       is encountered.  In the default implementation, it calls 
 *       report_error() to emit a message, then throws an exception.
 *  

 void syntax_error(Symbol cur_token)
 *  

 This method is called as soon as syntax error is detected (but
 *       before recovery is attempted).  In the default implementation it 
 *       invokes: report_error(“Syntax error”, null);
 *  

 void unrecovered_syntax_error(Symbol cur_token)
 *  

 This method is called if syntax error recovery fails.  In the default
 *       implementation it invokes:
 
 *         report_fatal_error(“Couldn’t repair and continue parse”, null);
 *  

 *
 * @see     java_cup.runtime.Symbol
 * @see     java_cup.runtime.Symbol
 * @see     java_cup.runtime.virtual_parse_stack
 * @version last updated: 7/3/96
 * @author  Frank Flannery
 */

public abstract class lr_parser {

  /*———————————————————–*/
  /*— Constructor(s) —————————————-*/
  /*———————————————————–*/

  /** Simple constructor. */
  public lr_parser()
    {
      /* nothing to do here */
    }

  /** Constructor that sets the default scanner. [CSA/davidm] */
  public lr_parser(Scanner s) {
    this(); /* in case default constructor someday does something */
    setScanner(s);
  }

  /*———————————————————–*/
  /*— (Access to) Static (Class) Variables ——————*/
  /*———————————————————–*/

  /** The default number of Symbols after an error we much match to consider 
   *  it recovered from. 
   */
  protected final static int _error_sync_size = 3;

  /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

  /** The number of Symbols after an error we much match to consider it 
   *  recovered from. 
   */
  protected int error_sync_size() {return _error_sync_size; }

  /*———————————————————–*/
  /*— (Access to) Instance Variables ————————*/
  /*———————————————————–*/

  /** Table of production information (supplied by generated subclass).
   *  This table contains one entry per production and is indexed by 
   *  the negative-encoded values (reduce actions) in the action_table.  
   *  Each entry has two parts, the index of the non-terminal on the 
   *  left hand side of the production, and the number of Symbols 
   *  on the right hand side. 
   */
  public abstract short[][] production_table();

  /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

  /** The action table (supplied by generated subclass).  This table is
   *  indexed by state and terminal number indicating what action is to
   *  be taken when the parser is in the given state (i.e., the given state 
   *  is on top of the stack) and the given terminal is next on the input.  
   *  States are indexed using the first dimension, however, the entries for 
   *  a given state are compacted and stored in adjacent index, value pairs 
   *  which are searched for rather than accessed directly (see get_action()).  
   *  The actions stored in the table will be either shifts, reduces, or 
   *  errors.  Shifts are encoded as positive values (one greater than the 
   *  state shifted to).  Reduces are encoded as negative values (one less 
   *  than the production reduced by).  Error entries are denoted by zero. 
   * 
   * @see java_cup.runtime.lr_parser#get_action
   */
  public abstract short[][] action_table();

  /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

  /** The reduce-goto table (supplied by generated subclass).  This
   *  table is indexed by state and non-terminal number and contains
   *  state numbers.  States are indexed using the first dimension, however,
   *  the entries for a given state are compacted and stored in adjacent
   *  index, value pairs which are searched for rather than accessed 
   *  directly (see get_reduce()).  When a reduce occurs, the handle 
   *  (corresponding to the RHS of the matched production) is popped off 
   *  the stack.  The new top of stack indicates a state.  This table is 
   *  then indexed by that state and the LHS of the reducing production to 
   *  indicate where to “shift” to. 
   *
   * @see java_cup.runtime.lr_parser#get_reduce
   */
  public abstract short[][] reduce_table();

  /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

  /** The index of the start state (supplied by generated subclass). */
  public abstract int start_state();

  /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

  /** The index of the start production (supplied by generated subclass). */
  public abstract int start_production();

  /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

  /** The index of the end of file terminal Symbol (supplied by generated 
   *  subclass). 
   */
  public abstract int EOF_sym();

  /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

  /** The index of the special error Symbol (supplied by generated subclass). */
  public abstract int error_sym();

  /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

  /** Internal flag to indicate when parser should quit. */
  protected boolean _done_parsing = false;

  /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

  /** This method is called to indicate that the parser should quit.  This is 
   *  normally called by an accept action, but can be used to cancel parsing 
   *  early in other circumstances if desired. 
   */
  public void done_parsing()
    {
      _done_parsing = true;
    }

  /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
  /* Global parse state shared by parse(), error recovery, and 
   * debugging routines */
  /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

  /** Indication of the index for top of stack (for use by actions). */
  protected int tos;

  /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

  /** The current lookahead Symbol. */
  protected Symbol cur_token;

  /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

  /** The parse stack itself. */
  protected Stack stack = new Stack();

  /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

  /** Direct reference to the production table. */ 
  protected short[][] production_tab;

  /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

  /** Direct reference to the action table. */
  protected short[][] action_tab;

  /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

  /** Direct reference to the reduce-goto table. */
  protected short[][] reduce_tab;

  /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

  /** This is the scanner object used by the default implementation
   *  of scan() to get Symbols.  To avoid name conflicts with existing
   *  code, this field is private. [CSA/davidm] */
  private Scanner _scanner;

  /**
   * Simple accessor method to set the default scanner.
   */
  public void setScanner(Scanner s) { _scanner = s; }

  /**
   * Simple accessor method to get the default scanner.
   */
  public Scanner getScanner() { return _scanner; }

  /*———————————————————–*/
  /*— General Methods —————————————*/
  /*———————————————————–*/

  /** Perform a bit of user supplied action code (supplied by generated 
   *  subclass).  Actions are indexed by an internal action number assigned
   *  at parser generation time.
   *
   * @param act_num   the internal index of the action to be performed.
   * @param parser    the parser object we are acting for.
   * @param stack     the parse stack of that object.
   * @param top       the index of the top element of the parse stack.
   */
  public abstract Symbol do_action(
    int       act_num, 
    lr_parser parser, 
    Stack     stack, 
    int       top) 
    throws java.lang.Exception;

  /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

  /** User code for initialization inside the parser.  Typically this 
   *  initializes the scanner.  This is called before the parser requests
   *  the first Symbol.  Here this is just a placeholder for subclasses that 
   *  might need this and we perform no action.   This method is normally
   *  overridden by the generated code using this contents of the “init with”
   *  clause as its body.
   */
  public void user_init() throws java.lang.Exception { }

  /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

  /** Initialize the action object.  This is called before the parser does
   *  any parse actions. This is filled in by generated code to create
   *  an object that encapsulates all action code. 
   */ 
  protected abstract void init_actions() throws java.lang.Exception;

  /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

  /** Get the next Symbol from the input (supplied by generated subclass).
   *  Once end of file has been reached, all subsequent calls to scan 
   *  should return an EOF Symbol (which is Symbol number 0).  By default
   *  this method returns getScanner().next_token(); this implementation
   *  can be overriden by the generated parser using the code declared in
   *  the “scan with” clause.  Do not recycle objects; every call to
   *  scan() should return a fresh object.
   */
  public Symbol scan() throws java.lang.Exception {
    return getScanner().next_token();
  }

  /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

  /** Report a fatal error.  This method takes a  message string and an 
   *  additional object (to be used by specializations implemented in 
   *  subclasses).  Here in the base class a very simple implementation 
   *  is provided which reports the error then throws an exception. 
   *
   * @param message an error message.
   * @param info    an extra object reserved for use by specialized subclasses.
   */
  public void report_fatal_error(
    String   message, 
    Object   info)
    throws java.lang.Exception
    {
      /* stop parsing (not really necessary since we throw an exception, but) */
      done_parsing();

      /* use the normal error message reporting to put out the message */
      report_error(message, info);

      /* throw an exception */
      throw new Exception(“Can’t recover from previous error(s)”);
    }

  /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

  /** Report a non fatal error (or warning).  This method takes a message 
   *  string and an additional object (to be used by specializations 
   *  implemented in subclasses).  Here in the base class a very simple 
   *  implementation is provided which simply prints the message to 
   *  System.err. 
   *
   * @param message an error message.
   * @param info    an extra object reserved for use by specialized subclasses.
   */
  public void report_error(String message, Object info)
    {
      System.err.print(message);
      if (info instanceof Symbol)
    if (((Symbol)info).left != -1)
    System.err.println(” at character ” + ((Symbol)info).left + 
               ” of input”);
    else System.err.println(“”);
      else System.err.println(“”);
    }

  /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

  /** This method is called when a syntax error has been detected and recovery 
   *  is about to be invoked.  Here in the base class we just emit a 
   *  “Syntax error” error message.  
   *
   * @param cur_token the current lookahead Symbol.
   */
  public void syntax_error(Symbol cur_token)
    {
      report_error(“Syntax error”, cur_token);
    }

  /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

  /** This method is called if it is determined that syntax error recovery 
   *  has been unsuccessful.  Here in the base class we report a fatal error. 
   *
   * @param cur_token the current lookahead Symbol.
   */
  public void unrecovered_syntax_error(Symbol cur_token)
    throws java.lang.Exception
    {
      report_fatal_error(“Couldn’t repair and continue parse”, cur_token);
    }

  /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

  /** Fetch an action from the action table.  The table is broken up into
   *  rows, one per state (rows are indexed directly by state number).  
   *  Within each row, a list of index, value pairs are given (as sequential
   *  entries in the table), and the list is terminated by a default entry 
   *  (denoted with a Symbol index of -1).  To find the proper entry in a row 
   *  we do a linear or binary search (depending on the size of the row).  
   *
   * @param state the state index of the action being accessed.
   * @param sym   the Symbol index of the action being accessed.
   */
  protected final short get_action(int state, int sym)
    {
      short tag;
      int first, last, probe;
      short[] row = action_tab[state];

      /* linear search if we are < 10 entries */       if (row.length < 20)         for (probe = 0; probe < row.length; probe++)       {         /* is this entry labeled with our Symbol or the default? */         tag = row[probe++];         if (tag == sym || tag == -1)           {             /* return the next entry */             return row[probe];           }       }       /* otherwise binary search */       else     {       first = 0;        last = (row.length-1)/2 - 1;  /* leave out trailing default entry */       while (first <= last)         {           probe = (first+last)/2;           if (sym == row[probe*2])         return row[probe*2+1];           else if (sym > row[probe*2])
        first = probe+1;
          else
            last = probe-1;
        }

      /* not found, use the default at the end */
      return row[row.length-1];
    }

      /* shouldn’t happened, but if we run off the end we return the 
     default (error == 0) */
      return 0;
    }

  /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

  /** Fetch a state from the reduce-goto table.  The table is broken up into
   *  rows, one per state (rows are indexed directly by state number).  
   *  Within each row, a list of index, value pairs are given (as sequential
   *  entries in the table), and the list is terminated by a default entry 
   *  (denoted with a Symbol index of -1).  To find the proper entry in a row 
   *  we do a linear search.  
   *
   * @param state the state index of the entry being accessed.
   * @param sym   the Symbol index of the entry being accessed.
   */
  protected final short get_reduce(int state, int sym)
    {
      short tag;
      short[] row = reduce_tab[state];

      /* if we have a null row we go with the default */
      if (row == null)
        return -1;

      for (int probe = 0; probe < row.length; probe++)     {       /* is this entry labeled with our Symbol or the default? */       tag = row[probe++];       if (tag == sym || tag == -1)         {           /* return the next entry */           return row[probe];         }     }       /* if we run off the end we return the default (error == -1) */       return -1;     }   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/   /** This method provides the main parsing routine.  It returns only when     *  done_parsing() has been called (typically because the parser has     *  accepted, or a fatal error has been reported).  See the header     *  documentation for the class regarding how shift/reduce parsers operate    *  and how the various tables are used.    */   public Symbol parse() throws java.lang.Exception     {       /* the current action code */       int act;       /* the Symbol/stack element returned by a reduce */       Symbol lhs_sym = null;       /* information about production being reduced with */       short handle_size, lhs_sym_num;       /* set up direct reference to tables to drive the parser */       production_tab = production_table();       action_tab     = action_table();       reduce_tab     = reduce_table();       /* initialize the action encapsulation object */       init_actions();       /* do user initialization */       user_init();       /* get the first token */       cur_token = scan();        /* push dummy Symbol with start state to get us underway */       stack.removeAllElements();       stack.push(new Symbol(0, start_state()));       tos = 0;       /* continue until we are told to stop */       for (_done_parsing = false; !_done_parsing; )     {       /* Check current token for freshness. */       if (cur_token.used_by_parser)         throw new Error("Symbol recycling detected (fix your scanner).");       /* current state is always on the top of the stack */       /* look up action out of the current state with the current input */       act = get_action(((Symbol)stack.peek()).parse_state, cur_token.sym);       /* decode the action -- > 0 encodes shift */
      if (act > 0)
        {
          /* shift to the encoded state by pushing it on the stack */
          cur_token.parse_state = act-1;
          cur_token.used_by_parser = true;
          stack.push(cur_token);
          tos++;

          /* advance to the next Symbol */
          cur_token = scan();
        }
      /* if its less than zero, then it encodes a reduce action */
      else if (act < 0)         {           /* perform the action for the reduce */           lhs_sym = do_action((-act)-1, this, stack, tos);           /* look up information about the production */           lhs_sym_num = production_tab[(-act)-1][0];           handle_size = production_tab[(-act)-1][1];           /* pop the handle off the stack */           for (int i = 0; i < handle_size; i++)         {           stack.pop();           tos--;         }                      /* look up the state to go to from the one popped back to */           act = get_reduce(((Symbol)stack.peek()).parse_state, lhs_sym_num);           /* shift to that state */           lhs_sym.parse_state = act;           lhs_sym.used_by_parser = true;           stack.push(lhs_sym);           tos++;         }       /* finally if the entry is zero, we have an error */       else if (act == 0)         {           /* call user syntax error reporting routine */           syntax_error(cur_token);           /* try to error recover */           if (!error_recovery(false))         {           /* if that fails give up with a fatal syntax error */           unrecovered_syntax_error(cur_token);           /* just in case that wasn't fatal enough, end parse */           done_parsing();         } else {           lhs_sym = (Symbol)stack.peek();         }         }     }       return lhs_sym;     }   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/   /** Write a debugging message to System.err for the debugging version     *  of the parser.     *    * @param mess the text of the debugging message.    */   public void debug_message(String mess)     {       System.err.println(mess);     }   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/   /** Dump the parse stack for debugging purposes. */   public void dump_stack()     {       if (stack == null)     {       debug_message("# Stack dump requested, but stack is null");       return;     }       debug_message("============ Parse Stack Dump ============");       /* dump the stack */       for (int i=0; i“);
      if ((i%3)==2 || (i==(stack.size()-1))) {
          debug_message(sb.toString());
          sb = new StringBuffer(”         “);
      }
      }
  }

  /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

  /** Perform a parse with debugging output.  This does exactly the
   *  same things as parse(), except that it calls debug_shift() and
   *  debug_reduce() when shift and reduce moves are taken by the parser
   *  and produces various other debugging messages.  
   */
  public Symbol debug_parse()
    throws java.lang.Exception
    {
      /* the current action code */
      int act;

      /* the Symbol/stack element returned by a reduce */
      Symbol lhs_sym = null;

      /* information about production being reduced with */
      short handle_size, lhs_sym_num;

      /* set up direct reference to tables to drive the parser */
      production_tab = production_table();
      action_tab     = action_table();
      reduce_tab     = reduce_table();

      debug_message(“# Initializing parser”);

      /* initialize the action encapsulation object */
      init_actions();

      /* do user initialization */
      user_init();

      /* the current Symbol */
      cur_token = scan(); 

      debug_message(“# Current Symbol is #” + cur_token.sym);

      /* push dummy Symbol with start state to get us underway */
      stack.removeAllElements();
      stack.push(new Symbol(0, start_state()));
      tos = 0;

      /* continue until we are told to stop */
      for (_done_parsing = false; !_done_parsing; )
    {
      /* Check current token for freshness. */
      if (cur_token.used_by_parser)
        throw new Error(“Symbol recycling detected (fix your scanner).”);

      /* current state is always on the top of the stack */
      //debug_stack();

      /* look up action out of the current state with the current input */
      act = get_action(((Symbol)stack.peek()).parse_state, cur_token.sym);

      /* decode the action — > 0 encodes shift */
      if (act > 0)
        {
          /* shift to the encoded state by pushing it on the stack */
          cur_token.parse_state = act-1;
          cur_token.used_by_parser = true;
          debug_shift(cur_token);
          stack.push(cur_token);
          tos++;

          /* advance to the next Symbol */
          cur_token = scan();
              debug_message(“# Current token is ” + cur_token);
        }
      /* if its less than zero, then it encodes a reduce action */
      else if (act < 0)         {           /* perform the action for the reduce */           lhs_sym = do_action((-act)-1, this, stack, tos);           /* look up information about the production */           lhs_sym_num = production_tab[(-act)-1][0];           handle_size = production_tab[(-act)-1][1];           debug_reduce((-act)-1, lhs_sym_num, handle_size);           /* pop the handle off the stack */           for (int i = 0; i < handle_size; i++)         {           stack.pop();           tos--;         }                      /* look up the state to go to from the one popped back to */           act = get_reduce(((Symbol)stack.peek()).parse_state, lhs_sym_num);           debug_message("# Reduce rule: top state " +                  ((Symbol)stack.peek()).parse_state +                  ", lhs sym " + lhs_sym_num + " -> state ” + act); 

          /* shift to that state */
          lhs_sym.parse_state = act;
          lhs_sym.used_by_parser = true;
          stack.push(lhs_sym);
          tos++;

          debug_message(“# Goto state #” + act);
        }
      /* finally if the entry is zero, we have an error */
      else if (act == 0)
        {
          /* call user syntax error reporting routine */
          syntax_error(cur_token);

          /* try to error recover */
          if (!error_recovery(true))
        {
          /* if that fails give up with a fatal syntax error */
          unrecovered_syntax_error(cur_token);

          /* just in case that wasn’t fatal enough, end parse */
          done_parsing();
        } else {
          lhs_sym = (Symbol)stack.peek();
        }
        }
    }
      return lhs_sym;
    }

  /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
  /* Error recovery code */
  /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

  /** Attempt to recover from a syntax error.  This returns false if recovery 
   *  fails, true if it succeeds.  Recovery happens in 4 steps.  First we
   *  pop the parse stack down to a point at which we have a shift out
   *  of the top-most state on the error Symbol.  This represents the
   *  initial error recovery configuration.  If no such configuration is
   *  found, then we fail.  Next a small number of “lookahead” or “parse
   *  ahead” Symbols are read into a buffer.  The size of this buffer is 
   *  determined by error_sync_size() and determines how many Symbols beyond
   *  the error must be matched to consider the recovery a success.  Next, 
   *  we begin to discard Symbols in attempt to get past the point of error
   *  to a point where we can continue parsing.  After each Symbol, we attempt 
   *  to “parse ahead” though the buffered lookahead Symbols.  The “parse ahead”
   *  process simulates that actual parse, but does not modify the real 
   *  parser’s configuration, nor execute any actions. If we can  parse all 
   *  the stored Symbols without error, then the recovery is considered a 
   *  success.  Once a successful recovery point is determined, we do an
   *  actual parse over the stored input — modifying the real parse 
   *  configuration and executing all actions.  Finally, we return the the 
   *  normal parser to continue with the overall parse.
   *
   * @param debug should we produce debugging messages as we parse.
   */
  protected boolean error_recovery(boolean debug)
    throws java.lang.Exception
    {
      if (debug) debug_message(“# Attempting error recovery”);

      /* first pop the stack back into a state that can shift on error and 
     do that shift (if that fails, we fail) */
      if (!find_recovery_config(debug))
    {
      if (debug) debug_message(“# Error recovery fails”);
      return false;
    }

      /* read ahead to create lookahead we can parse multiple times */
      read_lookahead();

      /* repeatedly try to parse forward until we make it the required dist */
      for (;;)
    {
      /* try to parse forward, if it makes it, bail out of loop */
      if (debug) debug_message(“# Trying to parse ahead”);
      if (try_parse_ahead(debug))
        {
          break;
        }

      /* if we are now at EOF, we have failed */
      if (lookahead[0].sym == EOF_sym()) 
        {
          if (debug) debug_message(“# Error recovery fails at EOF”);
          return false;
        }

      /* otherwise, we consume another Symbol and try again */
      if (debug) 
      debug_message(“# Consuming Symbol #” + cur_err_token().sym);
      restart_lookahead();
    }

      /* we have consumed to a point where we can parse forward */
      if (debug) debug_message(“# Parse-ahead ok, going back to normal parse”);

      /* do the real parse (including actions) across the lookahead */
      parse_lookahead(debug);

      /* we have success */
      return true;
    }

  /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

  /** Determine if we can shift under the special error Symbol out of the 
   *  state currently on the top of the (real) parse stack. 
   */
  protected boolean shift_under_error()
    {
      /* is there a shift under error Symbol */
      return get_action(((Symbol)stack.peek()).parse_state, error_sym()) > 0;
    }

  /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

  /** Put the (real) parse stack into error recovery configuration by 
   *  popping the stack down to a state that can shift on the special 
   *  error Symbol, then doing the shift.  If no suitable state exists on 
   *  the stack we return false 
   *
   * @param debug should we produce debugging messages as we parse.
   */
  protected boolean find_recovery_config(boolean debug)
    {
      Symbol error_token;
      int act;

      if (debug) debug_message(“# Finding recovery state on stack”);

      /* Remember the right-position of the top symbol on the stack */
      int right_pos = ((Symbol)stack.peek()).right;
      int left_pos  = ((Symbol)stack.peek()).left;

      /* pop down until we can shift under error Symbol */
      while (!shift_under_error())
    {
      /* pop the stack */
      if (debug) 
        debug_message(“# Pop stack by one, state was # ” +
                      ((Symbol)stack.peek()).parse_state);
          left_pos = ((Symbol)stack.pop()).left;    
      tos–;

      /* if we have hit bottom, we fail */
      if (stack.empty()) 
        {
          if (debug) debug_message(“# No recovery state found on stack”);
          return false;
        }
    }

      /* state on top of the stack can shift under error, find the shift */
      act = get_action(((Symbol)stack.peek()).parse_state, error_sym());
      if (debug) 
    {
      debug_message(“# Recover state found (#” + 
            ((Symbol)stack.peek()).parse_state + “)”);
      debug_message(“# Shifting on error to state #” + (act-1));
    }

      /* build and shift a special error Symbol */
      error_token = new Symbol(error_sym(), left_pos, right_pos);
      error_token.parse_state = act-1;
      error_token.used_by_parser = true;
      stack.push(error_token);
      tos++;

      return true;
    }

  /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

  /** Lookahead Symbols used for attempting error recovery “parse aheads”. */
  protected Symbol lookahead[];

  /** Position in lookahead input buffer used for “parse ahead”. */
  protected int lookahead_pos;

  /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

  /** Read from input to establish our buffer of “parse ahead” lookahead 
   *  Symbols. 
   */
  protected void read_lookahead() throws java.lang.Exception
    {
      /* create the lookahead array */
      lookahead = new Symbol[error_sync_size()];

      /* fill in the array */
      for (int i = 0; i < error_sync_size(); i++)     {       lookahead[i] = cur_token;       cur_token = scan();     }       /* start at the beginning */       lookahead_pos = 0;     }   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/   /** Return the current lookahead in our error "parse ahead" buffer. */   protected Symbol cur_err_token() { return lookahead[lookahead_pos]; }   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/   /** Advance to next "parse ahead" input Symbol. Return true if we have     *  input to advance to, false otherwise.     */   protected boolean advance_lookahead()     {       /* advance the input location */       lookahead_pos++;       /* return true if we didn't go off the end */       return lookahead_pos < error_sync_size();     }   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/   /** Reset the parse ahead input to one Symbol past where we started error     *  recovery (this consumes one new Symbol from the real input).     */   protected void restart_lookahead() throws java.lang.Exception     {       /* move all the existing input over */       for (int i = 1; i < error_sync_size(); i++)     lookahead[i-1] = lookahead[i];       /* read a new Symbol into the last spot */       cur_token = scan();       lookahead[error_sync_size()-1] = cur_token;       /* reset our internal position marker */       lookahead_pos = 0;     }   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/   /** Do a simulated parse forward (a "parse ahead") from the current     *  stack configuration using stored lookahead input and a virtual parse    *  stack.  Return true if we make it all the way through the stored     *  lookahead input without error. This basically simulates the action of     *  parse() using only our saved "parse ahead" input, and not executing any     *  actions.    *    * @param debug should we produce debugging messages as we parse.    */   protected boolean try_parse_ahead(boolean debug)     throws java.lang.Exception     {       int act;       short lhs, rhs_size;       /* create a virtual stack from the real parse stack */       virtual_parse_stack vstack = new virtual_parse_stack(stack);       /* parse until we fail or get past the lookahead input */       for (;;)     {       /* look up the action from the current state (on top of stack) */       act = get_action(vstack.top(), cur_err_token().sym);       /* if its an error, we fail */       if (act == 0) return false;       /* > 0 encodes a shift */
      if (act > 0)
        {
          /* push the new state on the stack */
          vstack.push(act-1);

          if (debug) debug_message(“# Parse-ahead shifts Symbol #” + 
               cur_err_token().sym + ” into state #” + (act-1));

          /* advance simulated input, if we run off the end, we are done */
          if (!advance_lookahead()) return true;
        }
      /* < 0 encodes a reduce */       else         {           /* if this is a reduce with the start production we are done */           if ((-act)-1 == start_production())          {           if (debug) debug_message("# Parse-ahead accepts");           return true;         }           /* get the lhs Symbol and the rhs size */           lhs = production_tab[(-act)-1][0];           rhs_size = production_tab[(-act)-1][1];           /* pop handle off the stack */           for (int i = 0; i < rhs_size; i++)         vstack.pop();           if (debug)          debug_message("# Parse-ahead reduces: handle size = " +                rhs_size + " lhs = #" + lhs + " from state #" + vstack.top());           /* look up goto and push it onto the stack */           vstack.push(get_reduce(vstack.top(), lhs));           if (debug)          debug_message("# Goto state #" + vstack.top());         }     }     }   /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/   /** Parse forward using stored lookahead Symbols.  In this case we have    *  already verified that parsing will make it through the stored lookahead    *  Symbols and we are now getting back to the point at which we can hand    *  control back to the normal parser.  Consequently, this version of the    *  parser performs all actions and modifies the real parse configuration.      *  This returns once we have consumed all the stored input or we accept.    *    * @param debug should we produce debugging messages as we parse.    */   protected void parse_lookahead(boolean debug)     throws java.lang.Exception     {       /* the current action code */       int act;       /* the Symbol/stack element returned by a reduce */       Symbol lhs_sym = null;       /* information about production being reduced with */       short handle_size, lhs_sym_num;       /* restart the saved input at the beginning */       lookahead_pos = 0;       if (debug)      {       debug_message("# Reparsing saved input with actions");       debug_message("# Current Symbol is #" + cur_err_token().sym);       debug_message("# Current state is #" +              ((Symbol)stack.peek()).parse_state);     }       /* continue until we accept or have read all lookahead input */       while(!_done_parsing)     {       /* current state is always on the top of the stack */       /* look up action out of the current state with the current input */       act =          get_action(((Symbol)stack.peek()).parse_state, cur_err_token().sym);       /* decode the action -- > 0 encodes shift */
      if (act > 0)
        {
          /* shift to the encoded state by pushing it on the stack */
          cur_err_token().parse_state = act-1;
          cur_err_token().used_by_parser = true;
          if (debug) debug_shift(cur_err_token());
          stack.push(cur_err_token());
          tos++;

          /* advance to the next Symbol, if there is none, we are done */
          if (!advance_lookahead()) 
        {
          if (debug) debug_message(“# Completed reparse”);

          /* scan next Symbol so we can continue parse */
          // BUGFIX by Chris Harris :
          //   correct a one-off error by commenting out
          //   this next line.
          /*cur_token = scan();*/

          /* go back to normal parser */
          return;
        }
          
          if (debug) 
        debug_message(“# Current Symbol is #” + cur_err_token().sym);
        }
      /* if its less than zero, then it encodes a reduce action */
      else if (act < 0)         {           /* perform the action for the reduce */           lhs_sym = do_action((-act)-1, this, stack, tos);           /* look up information about the production */           lhs_sym_num = production_tab[(-act)-1][0];           handle_size = production_tab[(-act)-1][1];           if (debug) debug_reduce((-act)-1, lhs_sym_num, handle_size);           /* pop the handle off the stack */           for (int i = 0; i < handle_size; i++)         {           stack.pop();           tos--;         }                      /* look up the state to go to from the one popped back to */           act = get_reduce(((Symbol)stack.peek()).parse_state, lhs_sym_num);           /* shift to that state */           lhs_sym.parse_state = act;           lhs_sym.used_by_parser = true;           stack.push(lhs_sym);           tos++;                       if (debug) debug_message("# Goto state #" + act);         }       /* finally if the entry is zero, we have an error           (shouldn't happen here, but...)*/       else if (act == 0)         {           report_fatal_error("Syntax error", lhs_sym);           return;         }     }          }   /*-----------------------------------------------------------*/   /** Utility function: unpacks parse tables from strings */   protected static short[][] unpackFromStrings(String[] sa)     {       // Concatanate initialization strings.       StringBuffer sb = new StringBuffer(sa[0]);       for (int i=1; i= real_stack.size()) return;

      /* get a copy of the first Symbol we have not transfered */
      stack_sym = (Symbol)real_stack.elementAt(real_stack.size()-1-real_next);

      /* record the transfer */
      real_next++;

      /* put the state number from the Symbol onto the virtual stack */
      vstack.push(new Integer(stack_sym.parse_state));
    }

  /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

  /** Indicate whether the stack is empty. */
  public boolean empty()
    {
      /* if vstack is empty then we were unable to transfer onto it and 
     the whole thing is empty. */
      return vstack.empty();
    }

  /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
      
  /** Return value on the top of the stack (without popping it). */
  public int top() throws java.lang.Exception
    {
      if (vstack.empty())
    throw new Exception(
          “Internal parser error: top() called on empty virtual stack”);

      return ((Integer)vstack.peek()).intValue();
    }

  /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

  /** Pop the stack. */
  public void pop() throws java.lang.Exception
    {
      if (vstack.empty())
    throw new Exception(
          “Internal parser error: pop from empty virtual stack”);

      /* pop it */
      vstack.pop();

      /* if we are now empty transfer an element (if there is one) */
      if (vstack.empty())
        get_from_real();
    }

  /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/

  /** Push a state number onto the stack. */
  public void push(int state_num)
    {
      vstack.push(new Integer(state_num));
    }

  /*———————————————————–*/

}

Symbol

package java_cup.runtime;
public synchronized class Symbol {
public int sym;
public int parse_state;
boolean used_by_parser;
public int left;
public int right;
public Object value;
public void Symbol(int, int, int, Object);
public void Symbol(int, Object);
public void Symbol(int, int, int);
public void Symbol(int);
public void Symbol(int, int);
public String toString();
}

lr_parser

package java_cup.runtime;
public abstract synchronized class lr_parser {
protected static final int _error_sync_size = 3;
protected boolean _done_parsing;
protected int tos;
protected Symbol cur_token;
protected java.util.Stack stack;
protected short[][] production_tab;
protected short[][] action_tab;
protected short[][] reduce_tab;
private Scanner _scanner;
protected Symbol[] lookahead;
protected int lookahead_pos;
public void lr_parser();
public void lr_parser(Scanner);
protected int error_sync_size();
public abstract short[][] production_table();
public abstract short[][] action_table();
public abstract short[][] reduce_table();
public abstract int start_state();
public abstract int start_production();
public abstract int EOF_sym();
public abstract int error_sym();
public void done_parsing();
public void setScanner(Scanner);
public Scanner getScanner();
public abstract Symbol do_action(int, lr_parser, java.util.Stack, int) throws Exception;
public void user_init() throws Exception;
protected abstract void init_actions() throws Exception;
public Symbol scan() throws Exception;
public void report_fatal_error(String, Object) throws Exception;
public void report_error(String, Object);
public void syntax_error(Symbol);
public void unrecovered_syntax_error(Symbol) throws Exception;
protected final short get_action(int, int);
protected final short get_reduce(int, int);
public Symbol parse() throws Exception;
public void debug_message(String);
public void dump_stack();
public void debug_reduce(int, int, int);
public void debug_shift(Symbol);
public void debug_stack();
public Symbol debug_parse() throws Exception;
protected boolean error_recovery(boolean) throws Exception;
protected boolean shift_under_error();
protected boolean find_recovery_config(boolean);
protected void read_lookahead() throws Exception;
protected Symbol cur_err_token();
protected boolean advance_lookahead();
protected void restart_lookahead() throws Exception;
protected boolean try_parse_ahead(boolean) throws Exception;
protected void parse_lookahead(boolean) throws Exception;
protected static short[][] unpackFromStrings(String[]);
}

Scanner

package java_cup.runtime;
public abstract interface Scanner {
public abstract Symbol next_token() throws Exception;
}

virtual_parse_stack

package java_cup.runtime;
public synchronized class virtual_parse_stack {
protected java.util.Stack real_stack;
protected int real_next;
protected java.util.Stack vstack;
public void virtual_parse_stack(java.util.Stack) throws Exception;
protected void get_from_real();
public boolean empty();
public int top() throws Exception;
public void pop() throws Exception;
public void push(int);
}