Package com.jgraph.layout

Source Code of com.jgraph.layout.JGraphFacade

/*
* $Id: JGraphFacade.java,v 1.1 2009/09/25 15:14:15 david Exp $
* Copyright (c) 2001-2007, Gaudenz Alder
* Copyright (c) 2005-2007, David Benson
*
* All rights reserved.
*
* This file is licensed under the JGraph software license, a copy of which
* will have been provided to you in the file LICENSE at the root of your
* installation directory. If you are unable to locate this file please
* contact JGraph sales for another copy.
*/
package com.jgraph.layout;

import java.awt.Dimension;
import java.awt.geom.Dimension2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.Stack;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.jgraph.JGraph;
import org.jgraph.graph.AttributeMap;
import org.jgraph.graph.CellView;
import org.jgraph.graph.Edge;
import org.jgraph.graph.DefaultGraphModel;
import org.jgraph.graph.GraphConstants;
import org.jgraph.graph.GraphLayoutCache;
import org.jgraph.graph.GraphModel;

import com.jgraph.algebra.JGraphAlgebra;
import com.jgraph.algebra.JGraphUnionFind;
import com.jgraph.algebra.cost.JGraphCostFunction;
import com.jgraph.algebra.cost.JGraphDistanceCostFunction;

/**
* An abstract description of a graph that can be used by a layout algorithm.
* This abstracts visibility, grouping, directed edges, any root cells,
* translation and scaling functions. It also stores the actual graph to be
* acted upon by the layout and provides utility method to determine the
* characteristics of the contained cells. After the layout has been applied
* this class stores the result of that layout as a nested attribute map.
*
*/
public class JGraphFacade {

  /**
   * Stores whether or not the layout is to act on only visible cells i.e.
   * <code>true</code> means only act on visible cells, <code>false</code>
   * act on cells regardless of their visibility. Default is <code>true</code>.
   */
  protected boolean ignoresHiddenCells = true;

  /**
   * Stores whether or not the layout is to act on only cells that have at
   * least one connection. <code>true</code> means only act on connected
   * cells, <code>false</code> act on cells regardless of their connections.
   * Default is <code>true</code>.
   */
  protected boolean ignoresUnconnectedCells = true;

  /**
   * Stores whether or not the layout is to only act on root cells in the
   * model. <code>true</code> means only act on root cells,
   * <code>false</code> means act upon roots and their children. Default is
   * <code>false</code>.
   */
  protected boolean ignoresCellsInGroups = false;

  /**
   * Stores whether or not the graph is to be treated as a directed graph.
   * <code>true</code> means follow edges in target to source direction,y
   *
   * <code>false</code> means treat edges as directionless
   */
  protected boolean directed;

  /**
   * Whether or not edges connected to collapsed children are promoted to
   * their first visible parent within the facade, not the actual model
   */
  protected boolean edgePromotion = false;

  /**
   * Whether or not cells should be returned in the same order as found
   * in the model. Set to true to obtain deterministic results for things
   * such as the order of cells with a particular level of a tree layout.
   * Note that setting this variable to true can cause quadratic
   * performance, therefore it defaults to false.
   */
  protected boolean ordered = false;

  /**
   * The JGraph to have the layout applied to it. There is no accessor to the
   * graph for the layouts. If you need access to the graph, try to factor out
   * the methods into a custom facade, and pass an instance of that facade to
   * your layout's run method.
   */
  protected transient JGraph graph = null;

  /**
   * The layout cache to have the layout applied to it. There is no accessor
   * to the graph for the layouts. If you need access to the graph, try to
   * factor out the methods into a custom facade, and pass an instance of that
   * facade to your layout's run method.
   */
  protected transient GraphLayoutCache graphLayoutCache = null;

  /**
   * The model to have the layout applied to it. There is no accessor
   * to the graph for the layouts. If you need access to the graph, try to
   * factor out the methods into a custom facade, and pass an instance of that
   * facade to your layout's run method.
   */
  protected transient GraphModel model = null;

  /**
   * The map of attribute changes made be the layout. Maps from cells to maps.
   */
  protected transient Hashtable attributes = new Hashtable();

  /**
   * The default comparator to be used where ordering is required in layouts
   */
  protected transient Comparator order = new DefaultComparator();

  /**
   * The default cost function used for shortest path search.
   */
  protected transient JGraphCostFunction distanceCostFunction;

  /**
   * The default graph algebra used for basic algorithms and functions.
   */
  protected transient JGraphAlgebra algebra;

  /**
   * The root vertex to be used by tree layouts.
   */
  protected transient List roots = new ArrayList();

  /**
   * If instaniated, this set defines which vertices are to be processed in
   * any layouts. Set to null to apply no filtered set
   */
  protected transient Set verticesFilter = null;

  /**
   * A collection of groups of sibling vertices
   */
  protected transient List groupHierarchies = null;

  /**
   * The factor by which to multiple the radius of the circle layout
   */
  protected double circleRadiusFactor = 1.0;

  /** The logger for this class */
  private static Logger logger = Logger
      .getLogger("com.jgraph.layout.JGraphFacade");

  /**
   * Constructs a JGraphGraphFacade specifying the graph passed in as the
   * input graph
   *
   * @param graph
   *            the JGraph to be laid out
   */
  public JGraphFacade(JGraph graph) {
    this(graph, null);
  }

  /**
   * Constructs a JGraphGraphFacade specifying the graph passed in as the
   * input graph
   *
   * @param graph
   *            the JGraph to be laid out
   * @param roots
   *            the root vertices to be used by tree and hierarchical layouts -
   *            NOTE, any roots will be subject to the facade filters at the
   *            time of construction.
   */
  public JGraphFacade(JGraph graph, Object[] roots) {
    this(graph, roots, true, false, true, true);
  }

  /**
   * Constructs a JGraphGraphFacade
   *
   * @see #JGraphFacade(JGraph, Object[], boolean, boolean, boolean, boolean,
   *      JGraphCostFunction, JGraphAlgebra)
   */
  public JGraphFacade(JGraph graph, Object[] roots,
      boolean ignoresHiddenCells, boolean ignoresCellsInGroups,
      boolean ignoresUnconnectedCells, boolean directed) {
    this(graph, roots, ignoresHiddenCells, ignoresCellsInGroups,
        ignoresUnconnectedCells, directed,
        new JGraphDistanceCostFunction(graph.getGraphLayoutCache()),
        JGraphAlgebra.getSharedInstance());
  }

  /**
   * Creates a JGraphGraphFacade specifying the graph passed in as the input
   * graph. Also configures properties of layout, whether or not edge
   * direction is to be taken into account, whether or not invisible cells are
   * to be considered and whether or not only root cells are to be considered
   * or roots and all their children. A root is only used if the isVertex
   * method returns true.
   *
   * @see #isVertex
   *
   * @param graph
   *            The graph used as input to the layout
   * @param roots
   *            the root vertices to be used by tree and hierarchical layouts -
   *            NOTE, any roots will be subject to the facade filters at the
   *            time of construction.
   * @param ignoresHiddenCells
   * @see #ignoresHiddenCells
   * @param ignoresCellsInGroups
   * @see #ignoresCellsInGroups
   * @param ignoresUnconnectedCells
   * @see #ignoresUnconnectedCells
   * @param directed
   * @see #directed
   * @param distanceCostFunction
   *            the cost function that defines the distance metrics
   * @param algebra
   *            the algebra used for basic algorithms and functions
   */
  public JGraphFacade(JGraph graph, Object[] roots,
      boolean ignoresHiddenCells, boolean ignoresCellsInGroups,
      boolean ignoresUnconnectedCells, boolean directed,
      JGraphCostFunction distanceCostFunction, JGraphAlgebra algebra) {
    this(graph == null ? null : graph.getModel(),
        graph == null ? null : graph.getGraphLayoutCache(),
        roots, ignoresHiddenCells,
        ignoresCellsInGroups, ignoresUnconnectedCells, directed,
        distanceCostFunction, algebra);
    this.graph = graph;
  }

  /**
   * Creates a JGraphFacade specifying the graph passed in as the input
   * graph.
   *
   * @param cache
   *            The GraphLayoutCache to be used as input to the layout
   */
  public JGraphFacade(GraphLayoutCache cache) {
    this(cache, null, true, false, true, true,
        new JGraphDistanceCostFunction(cache),
        JGraphAlgebra.getSharedInstance());

  }

  /**
   * Creates a JGraphFacade specifying the graph passed in as the input
   * graph. Also configures properties of layout, whether or not edge
   * direction is to be taken into account, whether or not invisible cells are
   * to be considered and whether or not only root cells are to be considered
   * or roots and all their children. A root is only used if the isVertex
   * method returns true.
   *
   * @see #isVertex
   *
   * @param cache
   *            The GraphLayoutCache to be used as input to the layout
   * @param roots
   *            the root vertices to be used by tree and hierarchical layouts -
   *            NOTE, any roots will be subject to the facade filters at the
   *            time of construction.
   * @param ignoresHiddenCells
   * @see #ignoresHiddenCells
   * @param ignoresCellsInGroups
   * @see #ignoresCellsInGroups
   * @param ignoresUnconnectedCells
   * @see #ignoresUnconnectedCells
   * @param directed
   * @see #directed
   * @param distanceCostFunction
   *            the cost function that defines the distance metrics
   * @param algebra
   *            the algebra used for basic algorithms and functions
   */
  public JGraphFacade(GraphLayoutCache cache, Object[] roots,
      boolean ignoresHiddenCells, boolean ignoresCellsInGroups,
      boolean ignoresUnconnectedCells, boolean directed,
      JGraphCostFunction distanceCostFunction, JGraphAlgebra algebra) {
    this(cache == null ? null : cache.getModel(), cache, roots,
      ignoresHiddenCells, ignoresCellsInGroups,
      ignoresUnconnectedCells, directed,
      distanceCostFunction, algebra);
  }

  /**
   * Creates a JGraphGenericFacade specifying the graph passed in as the input
   * graph. Also configures properties of layout, whether or not edge
   * direction is to be taken into account, whether or not invisible cells are
   * to be considered and whether or not only root cells are to be considered
   * or roots and all their children. A root is only used if the isVertex
   * method returns true.
   *
   * @see #isVertex
   *
   * @param model
   *            The GraphModel to be used as input to the layout
   * @param roots
   *            the root vertices to be used by tree and hierarchical layouts -
   *            NOTE, any roots will be subject to the facade filters at the
   *            time of construction.
   * @param ignoresHiddenCells
   * @see #ignoresHiddenCells
   * @param ignoresCellsInGroups
   * @see #ignoresCellsInGroups
   * @param ignoresUnconnectedCells
   * @see #ignoresUnconnectedCells
   * @param directed
   * @see #directed
   * @param distanceCostFunction
   *            the cost function that defines the distance metrics
   * @param algebra
   *            the algebra used for basic algorithms and functions
   */
  public JGraphFacade(GraphModel model, Object[] roots,
      boolean ignoresHiddenCells, boolean ignoresCellsInGroups,
      boolean ignoresUnconnectedCells, boolean directed,
      JGraphCostFunction distanceCostFunction, JGraphAlgebra algebra) {
    this(model, null, roots,
        ignoresHiddenCells, ignoresCellsInGroups,
        ignoresUnconnectedCells, directed,
        distanceCostFunction, algebra);
  }

  /**
   * Creates a JGraphGenericFacade specifying the graph passed in as the input
   * graph. Also configures properties of layout, whether or not edge
   * direction is to be taken into account, whether or not invisible cells are
   * to be considered and whether or not only root cells are to be considered
   * or roots and all their children. A root is only used if the isVertex
   * method returns true.
   *
   * @see #isVertex
   *
   * @param model
   *            The GraphModel to be used as input to the layout
   * @param cache
   *            The GraphLayoutCache to be used as input to the layout
   * @param roots
   *            the root vertices to be used by tree and hierarchical layouts -
   *            NOTE, any roots will be subject to the facade filters at the
   *            time of construction.
   * @param ignoresHiddenCells
   * @see #ignoresHiddenCells
   * @param ignoresCellsInGroups
   * @see #ignoresCellsInGroups
   * @param ignoresUnconnectedCells
   * @see #ignoresUnconnectedCells
   * @param directed
   * @see #directed
   * @param distanceCostFunction
   *            the cost function that defines the distance metrics
   * @param algebra
   *            the algebra used for basic algorithms and functions
   */
  public JGraphFacade(GraphModel model, GraphLayoutCache cache, Object[] roots,
      boolean ignoresHiddenCells, boolean ignoresCellsInGroups,
      boolean ignoresUnconnectedCells, boolean directed,
      JGraphCostFunction distanceCostFunction, JGraphAlgebra algebra) {
    this.model = model;
    this.graphLayoutCache = cache;
    if (model == null) {
      // Cannot obtain model
      throw new RuntimeException(
      "GraphModel not available in JGraphFacade");
    }
    this.ignoresHiddenCells = ignoresHiddenCells;
    // If the graph layout cache is null, this flag cannot be
    // taken into account and must be forced to false
    if (cache == null) {
      ignoresHiddenCells = false;
    }
    this.ignoresCellsInGroups = ignoresCellsInGroups;
    this.ignoresUnconnectedCells = ignoresUnconnectedCells;
    this.directed = directed;
    this.distanceCostFunction = distanceCostFunction;
    this.algebra = algebra;
    if (roots != null) {
      for (int i = 0; i < roots.length; i++)
        if (isVertex(roots[i]))
          this.roots.add(roots[i]);
    }
    setLoggerLevel(Level.OFF);
  }
  /**
   * The main method to execute layouts
   *
   * @param layout
   *            the layout to be executed
   * @param processByGroups
   *            Whether or not to process cell only at the level of their own
   *            group When true, children are only processed with siblings and
   *            their parent only with its siblings and so on
   */
  public void run(JGraphLayout layout, boolean processByGroups) {
    if (processByGroups) {
      // Run the layout individual on each sibling group
      // then combine the result
      determineLayoutHierarchies();
      Set oldVertexFilter = verticesFilter;
      Object[] hierarchies = groupHierarchies.toArray();
      for (int i = 0; i < hierarchies.length; i++) {
        verticesFilter = (Set)hierarchies[i];
        layout.run(this);
      }

      verticesFilter = oldVertexFilter;
    } else {
      layout.run(this);
    }
  }

  /**
   * Resets the control points of all moveable edges in the graph.
   */
  public void resetControlPoints() {
    resetControlPoints(false, null);
  }

  /**
   * Resets the control points of all moveable edges in the graph.
   * Also set the routing on the edges to the specified value
   * if the parameter flag indicates to do so
   */
  public void resetControlPoints(boolean setRouting, Edge.Routing routing) {
    Iterator it = getEdges().iterator();
    while (it.hasNext()) {
      Object edge = it.next();
      if (isMoveable(edge)) {
        Map map = getAttributes(edge);

        // Resets the control points by removing all but
        // the first and the last point from the points
        // list.
        List pts = GraphConstants.getPoints(map);
        if (pts != null && pts.size() > 2) {
          List newPoints = new ArrayList();
          newPoints.add(pts.get(0));
          newPoints.add(pts.get(pts.size() - 1));
          GraphConstants.setPoints(map, newPoints);
        }
        if (setRouting) {
          GraphConstants.setRouting(map, routing);
        }
      }
    }
  }

  /**
   * Returns whether or not the specified cell is a vertex and should be taken
   * into account by the layout
   *
   * @param cell
   *            the cell that is to be classified as a vertex or not
   * @return Returns true if <code>cell</code> is a vertex
   */
  public boolean isVertex(Object cell) {
    if (verticesFilter != null) {
      if (!verticesFilter.contains(cell)) {
        return false;
      }
    }
    // If we're dealing with an edge or a port we
    // return false in all cases
    if (DefaultGraphModel.isVertex(model, cell)) {
      if (ignoresUnconnectedCells) {
        Object[] edges = getEdges(cell);
        if (edges == null || edges.length == 0)
          return false;
        else {
          if (ignoresHiddenCells && graphLayoutCache != null) {

            // Check if at least one edge is visible
            boolean connected = false;
            for (int i = 0; i < edges.length; i++) {
              connected = connected
              || graphLayoutCache.isVisible(edges[i]);
            }
            if (!connected)
              return false;
          }
        }
      }
      if (ignoresHiddenCells && graphLayoutCache != null) {

        // If only visible cells should be returned
        // we check if there is a cell view for the cell
        // and return if based on it's isLeaf property.
        CellView view = graphLayoutCache.getMapping(cell, false);
        if (view != null) {
          // Root cell views have no parent view
          if (ignoresCellsInGroups) {
            return (view.getParentView() == null);
          } else {
            return true;
          }
        }
        return false;
      }
      if (ignoresCellsInGroups && model.getParent(cell) != null) {
        return false;
      }
      return true;
    }
    return false;
  }

  /**
   * Returns whether or not the specified cell is an edge and should be taken
   * into account by the layout
   *
   * @param cell
   *            the cell that is to be classified as an edge or not
   * @return Returns true if the cell is an edge
   */
  public boolean isEdge(Object cell) {
    // Hint: "Edge groups" need special attention
    // Unconnected edges are ignored
    if (model.getSource(cell) == null || model.getTarget(cell) == null) {
      return false;
    }
    if (ignoresHiddenCells && graphLayoutCache != null) {
      if (!model.isEdge(cell)) {
        return false;
      }
      CellView view = graphLayoutCache.getMapping(cell, false);
      if (view != null) {
        if (ignoresCellsInGroups) {
          return view.isLeaf() && view.getParentView() == null;
        } else {
          return view.isLeaf();
        }
      }
      return false;
    } else {
      // Returns false if we find a child that is not a port
      if (ignoresCellsInGroups && model.getParent(cell) != null) {
        return false;
      }
      return model.isEdge(cell);
    }
  }

  /**
   * A shortcut method that calls getNeighbours with no cells to exclude.
   *
   * @see #getNeighbours(Object, Set, boolean)
   */
  public List getNeighbours(Object cell, boolean ordered) {
    return getNeighbours(cell, null, ordered);
  }

  /**
   * Returns a collection of cells that are connected to the specified cell by
   * edges. Any cells specified in the exclude set will be ignored.
   *
   * @param cell
   *            The cell from which the neighbours will be determined
   * @param exclude
   *            The set of cells to ignore when searching
   * @param ordered
   *            whether or not to order the returned value in the order of the
   *            current <code>order</code> comparator. <b>Be very careful</b>
   *            using the default comparator on the default graph model,
   *            <code>getIndexOfRoot</code> has linear performance and so
   *            sorting the entire model roots will have quadratic
   *            performance.
   * @return Returns the set of neighbours for <code>cell</code>
   */
  public List getNeighbours(Object cell, Set exclude, boolean ordered) {
    LinkedList neighbours = new LinkedList();
    if (graphLayoutCache != null && graphLayoutCache.isPartial()
        && edgePromotion) {
      Set cells = getHiddenChildren(cell);
      Iterator iter = cells.iterator();
      Set connectedCellsPromoted = new HashSet();
      while (iter.hasNext()) {
        Object currentCell = iter.next();
        List connectedCells = graphLayoutCache.getNeighbours(
            currentCell, exclude, directed, false);
        Iterator iter2 = connectedCells.iterator();
        while (iter2.hasNext()) {
          Object otherCell = iter2.next();
          if (!cells.contains(otherCell)) {
            while (model.getParent(otherCell) != null && !graphLayoutCache.isVisible(otherCell)) {
              otherCell = model.getParent(otherCell);
            }
            if (graphLayoutCache.isVisible(otherCell)) {
              connectedCellsPromoted.add(otherCell);
            }
          }
        }
      }
      neighbours.addAll(connectedCellsPromoted);
    } else {
      List connectedCells = graphLayoutCache.getNeighbours(cell, exclude,
          directed, ignoresHiddenCells);
      neighbours.addAll(connectedCells);
    }
    if (ordered && order != null)
      Collections.sort(neighbours, order);
    return neighbours;
  }

  /**
   * Obtains all hidden vertices of the specified cell
   * @param cell the cell whose children are to be determined
   * @return all the child hidden vertices
   */
  private Set getHiddenChildren(Object cell) {
    List cellChildren = DefaultGraphModel.getDescendants(model, new Object[] {cell});
    Set cells = new HashSet();
    cells.add(cell);
    Iterator iter = cellChildren.iterator();
    while (iter.hasNext()) {
      Object childCell = iter.next();
      if (DefaultGraphModel.isVertex(model, childCell)
          && !graphLayoutCache.isVisible(childCell)) {
        cells.add(childCell);
      }
    }
    return cells;
  }

  /**
   * Returns the length of the specified edge wrt
   * <code>distanceFunction</code>.
   *
   * @param edge
   *            the edge whos length is returned
   *
   * @return Returns the length of <code>edge</code>
   *
   * @see #distanceCostFunction
   * @see #getPath(Object, Object, int, JGraphCostFunction)
   */
  public double getLength(Object edge) {
    return distanceCostFunction.getCost(edge);
  }

  /**
   * Returns the length of the shortest path connecting <code>v1</code> and
   * <code>v2</code> wrt <code>distanceFunction</code>. The path has no
   * more than <code>maxHops</code> elements.
   *
   * @param v1
   *            the source vertex
   * @param v2
   *            the target vertex
   * @param maxHops
   *            the maximum number of edges the path may have
   *
   * @return Returns the length of the shortest path between v1 and v2
   *
   * @see #distanceCostFunction
   * @see #getPath(Object, Object, int, JGraphCostFunction)
   */
  public double getDistance(Object v1, Object v2, int maxHops) {
    Object[] path = getPath(v1, v2, maxHops, distanceCostFunction);
    return algebra.sum(path, distanceCostFunction);
  }

  /**
   * Returns the shortest path connecting <code>v1</code> and
   * <code>v2</code> wrt <code>cf</code> with traverses no more than
   * <code>steps</code> edges. The cost function defines the metric that is
   * used as the edges length.
   *
   * @param v1
   *            the source vertex
   * @param v2
   *            the target vertex
   * @param steps
   *            the maximum number of edges in the path
   * @param cf
   *            the cost function that defines the edge lengths
   *
   * @return Returns shortest array of edges connecting v1 and v2
   *
   * @see JGraphAlgebra#getShortestPath(GraphModel, Object, Object,
   *      JGraphCostFunction, int, boolean)
   */
  public Object[] getPath(Object v1, Object v2, int steps,
      JGraphCostFunction cf) {
    return algebra.getShortestPath(model, v1, v2, cf, steps, isDirected());
  }

  /**
   * Returns a union find structure representing the connection components of
   * G=(E,V). The union find may be used as follows to determine whether two
   * cells are connected:
   * <p>
   * Object[] v = facade.getVertices(); <br>
   * Object[] e = facade.getEdges(); <br>
   * JGraphUnionFind uf = facade.getConnectionComponents(v, e); <br>
   * boolean connected = uf.differ(vertex1, vertex2); <br>
   *
   * @param v
   *            the vertices of the graph
   * @param e
   *            the edges of the graph
   *
   * @return Returns the connection components in G=(E,V)
   *
   * @see JGraphAlgebra#getConnectionComponents(GraphModel, Object[],
   *      Object[])
   */
  public JGraphUnionFind getConnectionComponents(Object[] v, Object[] e) {
    return algebra.getConnectionComponents(model, v, e);
  }

  /**
   * Returns the minimum spanning tree (MST) for the graph defined by G=(E,V).
   * The MST is defined as the set of all vertices with minimal lengths that
   * forms no cycles in G.
   *
   * @param v
   *            the vertices of the graph
   *
   * @return Returns the MST as an array of edges
   *
   * @see JGraphAlgebra#getMinimumSpanningTree(GraphModel, Object[],
   *      JGraphCostFunction, boolean)
   */
  public Object[] getMinimumSpanningTree(Object[] v, JGraphCostFunction cf) {
    return algebra.getMinimumSpanningTree(model, v, cf, directed);
  }

  /**
   * Returns all vertices in the graph. <br>
   * Note: This returns a linked list, for frequent read operations you should
   * turn this into an array, or at least an array list.
   *
   * @return Returns all cells that the layout should take into account
   *
   * @see #isVertex(Object)
   */
  public Collection getVertices() {
    return getCells(getAll(), false, false);
  }

  /**
   * Returns all unconnected vertices in the graph. <br>
   *
   * @return Returns all the unconnected cells that the layout should take
   *         into account
   */
  public Collection getUnconnectedVertices(boolean ordered) {
    Collection vertices = getAll();
    Set result = null;
    if (ordered && order != null) {
      result = new TreeSet(order);
    } else {
      result = new LinkedHashSet();
    }
    Iterator it = vertices.iterator();
    while (it.hasNext()) {
      Object cell = it.next();
      // Check if cell is unconnected vertex
      if (DefaultGraphModel.isVertex(model, cell)) {
        Object[] edges = getEdges(cell);
        if (edges == null || edges.length == 0) {
          result.add(cell);
        }
      }
    }
    return result;
  }

  /**
   * Returns all edges in the graph. <br>
   * Note: This returns a linked list, for frequent read operations you should
   * turn this into an array, or at least an array list.
   *
   * @return Returns all edges that the layout should take into account
   *
   * @see #isEdge(Object)
   */
  public Collection getEdges() {
    return getCells(getAll(), true, false);
  }

  /**
   * Returns the connected edges for a cell. Cell should be a port or a
   * vertex.
   *
   * @param cell
   *            the cell whose edges are to be obtained
   * @return Returns the array of all connected edges
   */
  public Object[] getEdges(Object cell) {
    return DefaultGraphModel.getEdges(model, new Object[] { cell })
    .toArray();
  }

  /**
   * Returns the incoming or outgoing edges for cell. Cell should be a port or
   * a vertex.
   *
   * @param cell
   *            the graph cell whose edges are to be obtained
   * @param incoming
   *            whether or not to obtain incoming edges only
   */
  public Object[] getEdges(Object cell, boolean incoming) {
    return DefaultGraphModel.getEdges(model, cell, incoming);
  }

  /**
   * Returns the vertex that is connected to the source end of the specified
   * edge
   *
   * @param edge
   *            the reference edge
   * @return any vertex connected as the source the specified edge
   */
  public Object getSource(Object edge) {
    Object cell = null;
    cell = DefaultGraphModel.getSourceVertex(model, edge);
    if (cell != null && !isVertex(cell)) {
      // Check to see if the edge has been promoted
      if (edgePromotion) {
        while (model.getParent(cell) != null && !isVertex(cell)) {
          cell = model.getParent(cell);
        }
      } else {
        return null;
      }
      if (isVertex(cell)) {
        return cell;
      } else {
        return null;
      }
    }
    return cell;
  }

  /**
   * Returns the vertex that is connected to the target end of the specified
   * edge
   *
   * @param edge
   *            the reference edge
   * @return any vertex connected as the target the specified edge
   */
  public Object getTarget(Object edge) {
    Object cell = null;
    cell = DefaultGraphModel.getTargetVertex(model, edge);
    if (cell != null && !isVertex(cell)) {
      // Check to see if the edge has been promoted
      if (edgePromotion) {
        while (model.getParent(cell) != null && !isVertex(cell)) {
          cell = model.getParent(cell);
        }
      } else {
        return null;
      }
      if (isVertex(cell)) {
        return cell;
      } else {
        return null;
      }
    }
    return cell;
  }

  /**
   * Returns the port that is connected to the source end of the specified
   * edge
   *
   * @param edge
   *            the reference edge
   * @return any vertex connected as the source the specified edge
   */
  public Object getSourcePort(Object edge) {
    Object cell = null;
    cell = model.getSource(edge);
    return cell;
  }

  /**
   * Returns the port that is connected to the target end of the specified
   * edge
   *
   * @param edge
   *            the reference edge
   * @return any vertex connected as the target the specified edge
   */
  public Object getTargetPort(Object edge) {
    Object cell = null;
    cell = model.getTarget(edge);
    return cell;
  }

  /**
   * Returns all cells including all descendants.
   */
  protected List getAll() {
    return DefaultGraphModel.getDescendants(model, DefaultGraphModel
        .getRoots(model));
  }

  /**
   * Returns a collection of cells in the current graph. Roots are flattened
   * and returned also. It can be specified whether or not to return edges in
   * the graph using the appropriate parameter. If the <code>ordered</code>
   * flag is set to <code>true</code> the result will be ordered by the
   * current comparator set for this facade. <br>
   * Note: This returns a set, for frequent read operations you should turn
   * this into an array, or at least an array list.
   *
   * @param cells
   *            the cells to be filtered and return the correct cell types
   * @param edges
   *            whether or not to return the edges of the graph
   * @param ordered
   *            whether or not to order the returned value in the order of the
   *            current <code>order</code> comparator. <b>Be very careful</b>
   *            using the default comparator on the default graph model,
   *            <code>getIndexOfRoot</code> has linear performance and so
   *            sorting the entire model roots will have quadratic
   *            performance.
   * @return collection of cells in the graph
   */
  protected Collection getCells(Collection cells, boolean edges,
      boolean ordered) {
    Set result = null;
    if (ordered && order != null) {
      result = new TreeSet(order);
    } else {
      result = new LinkedHashSet();
    }
    Iterator it = cells.iterator();
    while (it.hasNext()) {
      Object cell = it.next();
      if ((edges && isEdge(cell)) && (getSource(cell) != null)
          && (getTarget(cell) != null))
        result.add(cell);
      if (!edges && isVertex(cell)) {
        result.add(cell);
      }
    }
    return result;
  }

  /**
   * Obtains the cell view corresponding the cell passed in
   * @param cell the cell whose view is to be obtained
   * @return the cell view, if any, assoicated with this cell
   */
  public Object getCellView(Object cell) {
    if (graphLayoutCache != null){
      Object view = graphLayoutCache.getMapping(cell, false);
      return view;
    }

    return null;
  }

  /**
   * Returns a collection of vertices found in the specified collection.
   *
   * @param cells
   *            the set of potential vertices
   * @param ordered
   *            whether or not to order the returned value in the order of the
   *            current <code>order</code> comparator. <b>Be very careful</b>
   *            using the default comparator on the default graph model,
   *            <code>getIndexOfRoot</code> has linear performance and so
   *            sorting the entire model roots will have quadratic
   *            performance.
   * @return Returns the collection of vertices on the collection
   *
   * @see #isVertex(Object)
   */
  public Collection getVertices(Collection cells, boolean ordered) {
    return getCells(cells, false, ordered);
  }

  /**
   * Returns the outgoing edges for cell. Cell should be a port or a vertex.
   *
   * @param cell
   *            The cell from which the outgoing edges will be determined
   * @param exclude
   *            The set of edges to ignore when searching
   * @param visibleCells
   *            whether or not only visible cells should be processed
   * @param selfLoops
   *            whether or not to include self loops in the returned list
   * @return Returns the list of outgoing edges for <code>cell</code>
   */
  public List getOutgoingEdges(Object cell, Set exclude,
      boolean visibleCells, boolean selfLoops) {
    return graphLayoutCache.getOutgoingEdges(cell, exclude, visibleCells,
        selfLoops);
  }

  /**
   * Returns the incoming edges for cell. Cell should be a port or a vertex.
   *
   * @param cell
   *            The cell from which the incoming edges will be determined
   * @param exclude
   *            The set of edges to ignore when searching
   * @param visibleCells
   *            whether or not only visible cells should be processed
   * @param selfLoops
   *            whether or not to include self loops in the returned list
   * @return Returns the list of incoming edges for <code>cell</code>
   */
  public List getIncomingEdges(Object cell, Set exclude,
      boolean visibleCells, boolean selfLoops) {
    LinkedList incomingEdges = new LinkedList();
    if (graphLayoutCache != null && graphLayoutCache.isPartial()
        && edgePromotion) {
      Set cells = getHiddenChildren(cell);
      Iterator iter = cells.iterator();
      Set connectedCellsPromoted = new HashSet();
      while (iter.hasNext()) {
        Object currentCell = iter.next();
        List connectedCells = graphLayoutCache.getIncomingEdges(
            currentCell, exclude, false, selfLoops);
        Iterator iter2 = connectedCells.iterator();
        while (iter2.hasNext()) {
          Object otherEdge = iter2.next();
          Object otherPort = model.getSource(otherEdge);
          Object otherCell = null;
          if (DefaultGraphModel.isVertex(model, otherPort)) {
            otherCell = otherPort;
          } else {
            otherCell = model.getParent(otherPort);
          }
          if (!cells.contains(otherCell)) {
            if (graphLayoutCache.isVisible(otherCell) && visibleCells) {
              connectedCellsPromoted.add(otherEdge);
            } else if (graphLayoutCache.isVisible(otherCell)) {
              connectedCellsPromoted.add(otherEdge);
            }
          }
        }
      }
      incomingEdges.addAll(connectedCellsPromoted);
    } else {
      List connectedCells = graphLayoutCache.getIncomingEdges(
          cell, exclude, visibleCells, selfLoops);
      incomingEdges.addAll(connectedCells);
    }

    return incomingEdges;
  }

  /**
   * Creates and returns nested attribute map specifying what changes the
   * layout made to the input graph. After a layout is run this method should
   * be queried to see what positional changes were made. This method applied
   * snapping to the graph if enabled and only fills the map with the bounds
   * values since these are the only values layout change
   *
   * @return a nested <code>Map</code> of the changes the layout made upon
   *         the input graph
   *
   * @deprecated as of version 1.1
   * @see #createNestedMap(boolean, boolean)
   * @see GraphConstants#merge(Map, Map)
   */
  public Map createNestedMap(Map nestedMap) {
    Map targetMap = createNestedMap(false, false);
    return GraphConstants.merge(nestedMap, targetMap);
  }

  /**
   * Compatibility method to invoke {@link #createNestedMap(boolean, Point2D)}
   * with an origin or null depending on <code>flushOrigin</code>.
   *
   * @param ignoreGrid
   *            whether or not the map returned is snapped to the current grid
   * @param flushOrigin
   *            whether or not the bounds of the graph should be moved to
   *            (0,0)
   *
   * @return a nested <code>Map</code> of the changes the layout made upon
   *         the input graph
   */
  public Map createNestedMap(boolean ignoreGrid, boolean flushOrigin) {
    return createNestedMap(ignoreGrid, (flushOrigin) ? new Point2D.Double(
        0, 0) : null);
  }

  /**
   * Creates and returns nested attribute map specifying what changes the
   * layout made to the input graph. After a layout is run this method should
   * be queried to see what positional changes were made. This method applied
   * snapping to the graph if enabled and only fills the map with the bounds
   * values since these are the only values layout change
   *
   * @param ignoreGrid
   *            whether or not the map returned is snapped to the current grid
   * @param origin
   *            the new origin to which the graph bounds will be flushed to
   *
   * @return a nested <code>Map</code> of the changes the layout made upon
   *         the input graph
   */
  public Map createNestedMap(boolean ignoreGrid, Point2D origin) {
    Rectangle2D rect = getCellBounds();
    if (rect == null)
      return null;

    // Makes sure the graph is not below zero and flushes to origin
    if (origin != null) {
      translateCells(getAttributes().keySet(), -rect.getX()
          + origin.getX(), -rect.getY() + origin.getY());
    } else if ((graph == null || !graph.isMoveBelowZero())
        && (rect.getX() < 0 || rect.getY() < 0)) {
      scale(getAttributes().keySet(), 1, 1, (rect.getX() < 0) ? Math
          .abs(rect.getX()) : 0, (rect.getY() < 0) ? Math.abs(rect
              .getY()) : 0);
    }

    // Contructs a graph change (nested map) by cloning all local attributes
    // for each cell, making sure the bounds are aligned to the grid if not
    // ignoreGrid is set.
    Map nested = new Hashtable();
    Iterator it = getAttributes().entrySet().iterator();
    while (it.hasNext()) {
      Map.Entry entry = (Map.Entry) it.next();
      Object cell = entry.getKey();
      Map attrs = new Hashtable((Map) entry.getValue());
      if (!ignoreGrid && graph != null) {
        graph.snap(GraphConstants.getBounds(attrs));
      }
      nested.put(cell, attrs);
    }
    return nested;
  }

  /**
   * Calculates a list of non-connected graph components for the current
   * graph.
   * @return a collection of seperate graph components
   */
  public List getComponents() {
    // Seperate out unconnected hierarchys
    List graphs = new LinkedList();
    Object[] vertices = getVertices().toArray();
    for (int i = 0; i < vertices.length; i++) {
      // First check if this vertex appears in any of the previous vertex
      // sets
      boolean newGraph = true;
      Iterator iter = graphs.iterator();
      while (newGraph && iter.hasNext()) {
        if (((Set) iter.next()).contains(vertices[i])) {
          newGraph = false;
        }
      }
      if (newGraph) {
        // Obtains set of vertices connected to this root
        Stack cellsStack = new Stack();
        cellsStack.push(vertices[i]);
        Set vertexSet = new HashSet();
        while (!cellsStack.isEmpty()) {
          Object cell = cellsStack.pop();
          if (!vertexSet.contains(cell)) {
            vertexSet.add(cell);
            boolean directed = isDirected();
            setDirected(false);
            Iterator it = getNeighbours(cell, vertexSet,
                false).iterator();
            setDirected(directed );
            while (it.hasNext()) {
                cellsStack.push(it.next());
            }
          }
        }
        graphs.add(vertexSet);
      }
    }
    return graphs;
  }

  /**
   * Calculates the euklidische Norm for the point p.
   *
   * @param p
   *            the point to calculate the norm for
   * @return the euklidische Norm for the point p
   */
  public double norm(Point2D p) {
    double x = p.getX();
    double y = p.getY();
    double norm = Math.sqrt(x * x + y * y);
    return norm;
  }

  /**
   * Returns the nested map that specifies what changes the layout has made to
   * the input graph.
   *
   * @return The map that stores all attributes.
   */
  public Hashtable getAttributes() {
    return attributes;
  }

  /**
   * Sets the map that stores all attributes that comprise the changes made by
   * the layout to the input graph
   *
   * @param attributes
   *            the new map of cell, map pairs
   */
  public void setAttributes(Hashtable attributes) {
    this.attributes = attributes;
  }

  /**
   * Returns the local attributes for the specified cell.
   */
  public Map getAttributes(Object cell) {
    AttributeMap map = (AttributeMap) getAttributes().get(cell);
    if (map == null) {

      // First tries to get a view for the cell. If no view is available
      // tries to get the attributes from the model. Then stores a local
      // clone and associate it with the cell for future reference.
      CellView view = null;
      // Treat the bounds as a special case. If available, get the bounds
      // from the view, since this will return the correct bounds for
      // group cells
      Rectangle2D bounds = null;
      if (graphLayoutCache != null) {
        view = graphLayoutCache.getMapping(cell, false);
      }
      if (view != null) {
        map = view.getAllAttributes();
        bounds = (Rectangle2D)view.getBounds().clone();
      }
      if (map == null)
        map = model.getAttributes(cell);
      if (map != null) {
        map = (AttributeMap) map.clone();
        if (bounds != null) {
          GraphConstants.setBounds(map, bounds);
        }
        getAttributes().put(cell, map);
      }

    }
    return map;
  }

  /**
   * Returns true if the cell is moveable. If this returns false then the
   * cells bounds cannot be changed via the facade. The default implementation
   * checks the <code>moveable</code> attribute. Subclassers can override
   * this eg. to check if a cell is not selected in the graph.
   */
  public boolean isMoveable(Object cell) {
    return GraphConstants.isMoveable(getAttributes(cell));
  }

  /**
   * Sets the local attributes for the specified cell.
   *
   * @param cell
   *            the cell to set the attributes for
   * @param map
   *            the new attributes for the cell
   */
  public void setAttributes(Object cell, Map map) {
    getAttributes().put(cell, map);
  }

  /**
   * Returns the minimal rectangular bounds that enclose the specified
   * vertices
   *
   * @param vertices
   *            the vertices whose collective bounds are to be determined
   * @return the collective bounds of the input vertices
   */
  public Rectangle2D getBounds(List vertices) {
    Rectangle2D ret = null;
    Iterator it = vertices.iterator();
    while (it.hasNext()) {
      Rectangle2D r = (Rectangle2D) getBounds(it.next());
      if (r != null) {
        if (ret == null)
          ret = (Rectangle2D) r.clone();
        else
          Rectangle2D.union(ret, r, ret);
      }
    }
    return ret;
  }

  /**
   * Returns the minimal rectangular bounds that enclose all the elements in
   * the <code>bounds</code> map. After a layout has completed this method
   * will return the collective bounds of the new laid out graph.
   * Note this method may return null and should be checked before using.
   *
   * @return the collective bounds of the elements in <code>bounds</code>
   */
  public Rectangle2D getGraphBounds() {
    return GraphLayoutCache.getBounds(graphLayoutCache.getCellViews());
  }

  /**
   * Returns the origin of the graph (ie the top left corner of the root
   * cells) for the original geometry.
   *
   * @return The origin of the graph.
   */
  public Point2D getGraphOrigin() {
    Object[] cells = DefaultGraphModel.getRoots(model);
    if (cells != null && cells.length > 0) {
      Rectangle2D ret = null;
      Rectangle2D r = null;
      for (int i = 0; i < cells.length; i++) {
        if (graphLayoutCache != null) {
          CellView view = graphLayoutCache.getMapping(cells[i], false);
          if (view != null) {
            r = view.getBounds();
          }
        } else if (model != null) {
          Map attributes = model.getAttributes(cells[i]);
          if (attributes != null) {
            r = GraphConstants.getBounds(attributes);
          }
        }
        if (r != null) {
          if (ret == null) {
            ret = (r != null) ? (Rectangle2D) r.clone() : null;
          } else {
            Rectangle2D.union(ret, r, ret);
          }
        }
      }
      if (ret != null) {
        return new Point2D.Double(Math.max(0, ret.getX()), Math.max(0,
            ret.getY()));
      }
    }
    return null;
  }

  /**
   * Returns the minimal rectangular bounds that enclose all the elements in
   * the <code>bounds</code> map. After a layout has completed this method
   * will return the collective bounds of the new laid out graph.
   *
   * @return the collective bounds of the elements in <code>bounds</code>
   */
  public Rectangle2D getCellBounds() {
    Rectangle2D ret = null;
    Hashtable nestedMap = getAttributes();
    // Clone the nested map to avoid to a ConcurrentModificationException
    Set nestedCopy = new HashSet(nestedMap.keySet());
    Iterator it = nestedCopy.iterator();
    while (it.hasNext()) {
      Object cell = it.next();
      Rectangle2D r = getBounds(cell);
      if (r != null) {
        if (ret == null) {
          ret = (Rectangle2D) r.clone();
        } else {
          Rectangle2D.union(ret, r, ret);
        }
      }
    }
    return ret;
  }

  /**
   * Translates the bounds of the specified cells adding <code>dx</code> and
   * <code>dy</code> to the respective location axes of the cell,
   *
   * @param dx
   *            the amount to be added to be x-axis positions of the vertices
   *            before scaling is applied
   * @param dy
   *            the amount to be added to be y-axis positions of the vertices
   *            before scaling is applied
   */
  public void translateCells(Collection cells, double dx, double dy) {
    scale(cells, 1, 1, dx, dy);
  }

  /**
   * Scales the graph bounds defined in <code>bounds</code> to fit into the
   * specified frame
   *
   * @param frame
   *            the frame the <code>bounds</code> map colective bounds is to
   *            be scaled to
   */
  public void scale(Rectangle2D frame) {
    Rectangle2D rect = getCellBounds();
    double scalex = frame.getWidth() / rect.getWidth();
    double scaley = frame.getHeight() / rect.getHeight();
    double dx = frame.getX() - rect.getX();
    double dy = frame.getY() - rect.getY();
    scale(getAttributes().keySet(), scalex, scaley, dx, dy);
  }

  /**
   * Scales the bounds of the specified cells adding <code>dx</code> and
   * <code>dy</code> to the respective location axes of the cell, then by
   * scaling them by <code>scalex</code> and <code>scaley</code>
   *
   * @param vertices
   *            the collection of vertices to be scaled
   * @param scalex
   *            the amount by which the x-axis positions of the vertices will
   *            be scaled
   * @param scaley
   *            the amount by which the y-axis positions of the vertices will
   *            be scaled
   * @param dx
   *            the amount to be added to be x-axis positions of the vertices
   *            before scaling is applied
   * @param dy
   *            the amount to be added to be y-axis positions of the vertices
   *            before scaling is applied
   */
  public void scale(Collection vertices, double scalex, double scaley,
      double dx, double dy) {
    Iterator it = vertices.iterator();
    while (it.hasNext()) {
      Object cell = it.next();
      Point2D location = getLocation(cell);
      if (location != null) {
        location.setLocation((location.getX() + dx) * scalex, (location
            .getY() + dy)
            * scaley);
        setLocation(cell, location.getX(), location.getY(), false);
      }
      if (isEdge(cell)) {
        List points = getPoints(cell);
        if (points != null) {
          Iterator it2 = points.iterator();
          while (it2.hasNext()) {
            Object obj = it2.next();
            if (obj instanceof Point2D) {
              Point2D point = (Point2D) obj;
              point.setLocation((point.getX() + dx) * scalex,
                  (point.getY() + dy) * scaley);
            }
          }
        }
      }
    }
  }

  /**
   * Moves the specified vertices to random locations in the x and y axes
   * directions between zero and a specified maximum. The maximum amounts can
   * be specified seperately for the x and y axes.
   *
   * @param vertices
   *            the collection of vertices to be moved
   * @param maxx
   *            the maximum translation that may occur in the x-axis
   * @param maxy
   *            the maximum translation that may occur in the y-axis
   */
  public void randomize(Collection vertices, int maxx, int maxy) {
    Random random = new Random();
    Iterator it = vertices.iterator();
    while (it.hasNext()) {
      if (maxx > 0 && maxy > 0) {
        int x = random.nextInt(maxx);
        int y = random.nextInt(maxy);
        setLocation(it.next(), x, y);
      }
    }
  }

  /**
   * Simulates a 'nudge' to the graph, moving the specified vertices a random
   * distance in the x and y axes directions between zero and a specified
   * maximum. The maximum amounts can be specified seperately for the x and y
   * axes.
   *
   * @param vertices
   *            the collection of vertices to be moved
   * @param maxx
   *            the maximum translation that may occur in the x-axis
   * @param maxy
   *            the maximum translation that may occur in the y-axis
   */
  public void tilt(Collection vertices, int maxx, int maxy) {
    Random random = new Random();
    Iterator it = vertices.iterator();
    while (it.hasNext()) {
      int x = random.nextInt(maxx);
      int y = random.nextInt(maxy);
      translate(it.next(), x, y);
    }
  }

  /**
   * Arrange the specified vertices into a circular shape, with a regular
   * distance between each vertex
   *
   * @param vertices
   *            the collection of vertices to be arranged
   */
  public void circle(Collection vertices) {
    Dimension d = getMaxSize(vertices);
    double max = Math.max(d.width, d.height);
    Object[] v = vertices.toArray();
    double r = v.length * max / Math.PI * circleRadiusFactor;
    double phi = 2 * Math.PI / vertices.size();
    for (int i = 0; i < v.length; i++)
      setLocation(v[i], r + r * Math.sin(i * phi), r + r
          * Math.cos(i * phi));
  }

  /**
   * Returns the current bounds for the specified cell.
   *
   * @param cell
   *            the cell whose bounds are to be determined
   * @return the bounds of the specified cell
   */
  public Rectangle2D getBounds(Object cell) {
    Map map = getAttributes(cell);
    if (isEdge(cell)) {
      Rectangle2D rect = GraphConstants.getBounds(map);
      List points = GraphConstants.getPoints(map);
      if (points != null) {
        Iterator iter = points.iterator();
        while (iter.hasNext()) {
          Object point = iter.next();
          if (point instanceof Point2D) {
            if (rect == null) {
              rect = new Rectangle2D.Double(((Point2D)point).getX(), ((Point2D)point).getY(), 1.0, 1.0);
            } else {
              rect.add((Point2D)point);
            }
          }
        }
      }
      setBounds(cell, rect);
      return rect;
    } else {
      // If this is a group cell we need to allow for the movement of any
      // child cells
      int numChildren = model.getChildCount(cell);
      Rectangle2D newChildBounds = null;
      Rectangle2D oldChildBounds = null;
      // Track whether any of the child vertices have changed. If at least one has
      // work out the collective bounds of all children again, using the new bound
      // values if available, otherwise the old.
      boolean childHasChanged = false;
      for (int i = 0; i < numChildren; i++) {
        Object child = model.getChild(cell, i);
        if (DefaultGraphModel.isVertex(model, child) && cell != child) {
          if (graphLayoutCache != null
              && !graphLayoutCache.isVisible(child)) {
            // If visiblity information is available and the child is
            // not visible do not add it to the bounds of the parent
          } else {
            AttributeMap cellAttributes = (AttributeMap) getAttributes()
                .get(child);

            if (cellAttributes != null) {
              childHasChanged = true;
              Rectangle2D cellBounds = (Rectangle2D) GraphConstants
                  .getBounds(cellAttributes).clone();
              if (newChildBounds == null) {
                newChildBounds = cellBounds;
              } else {
                newChildBounds = newChildBounds
                    .createUnion(cellBounds);
              }
            } else {
              Rectangle2D cellBounds = (Rectangle2D) getBounds(
                  child).clone();
              if (oldChildBounds == null) {
                oldChildBounds = cellBounds;
              } else {
                oldChildBounds = oldChildBounds
                    .createUnion(cellBounds);
              }
            }
          }
        }
      }
      if (childHasChanged) {
        Rectangle2D cellBounds = null;
        // Return the union of the child vertices
        if (newChildBounds != null && oldChildBounds != null) {
          Rectangle2D groupBounds = newChildBounds
          .createUnion(oldChildBounds);
          cellBounds = groupBounds;
        } else if (newChildBounds == null && oldChildBounds != null) {
          cellBounds = oldChildBounds;
        } else if (newChildBounds != null && oldChildBounds == null) {
          cellBounds = newChildBounds;
        }
        // Allow for group inset
        int inset = GraphConstants.getInset(map);
        if (inset != 0) {
          cellBounds.setFrame(cellBounds.getX() - inset, cellBounds
              .getY()
              - inset, cellBounds.getWidth() + inset * 2,
              cellBounds.getHeight() + inset * 2);
        }
        setBounds(cell, cellBounds);
        return cellBounds;
      }
      // Return the default bounds value
      return GraphConstants.getBounds(map);
    }
  }

  /**
   * Reads the bounds from the nested map for each cell and invokes setBounds
   * for that cell with a clone of the bounds.
   *
   * @param nestedMap
   *            A map of (cell, map) pairs
   *
   * @see GraphConstants#getBounds(Map)
   */
  public void setBounds(Map nestedMap) {
    if (nestedMap != null) {
      Iterator it = nestedMap.entrySet().iterator();
      while (it.hasNext()) {
        Map.Entry entry = (Map.Entry) it.next();
        Rectangle2D bounds = GraphConstants.getBounds((Map) entry
            .getValue());
        if (bounds != null) {
          setBounds(entry.getKey(), (Rectangle2D) bounds.clone());
        }
      }
    }
  }

  /**
   * Sets the current bounds for the specified cell.
   *
   * @param cell
   *            the cell whose bounds are to be set
   * @param rect
   *            the new bounds of the specified cell
   */
  public void setBounds(Object cell, Rectangle2D rect) {
    Map map = getAttributes(cell);
    GraphConstants.setBounds(map, rect);
  }

  /**
   * Returns an array of arrays (index 0 is x-coordinate, index 1 is
   * y-coordinate in the second array) that fast layouts can operate upon.
   * <p>
   * This method is normally used at the beginning of a layout to setup fast
   * internal datastructures. The layout then changes the array in-place and
   * when finished, writes the result back using the setLocations(Object[]
   * cells, double[][] locations) method:
   * <p>
   * public void run(JGraphFacade facade) { 1. vertices =
   * facade.getVertices().toArray(); 2. locations =
   * facade.getLocations(vertices); 3. perform layout on local arrays 4.
   * return result: facade.setLocations(vertices, locations); }
   *
   * @param cells
   *            The cells to return the locations for
   * @return Returns the locations of the cells as an array of arrays
   */
  public double[][] getLocations(Object[] cells) {
    double[][] locations = new double[cells.length][2];
    for (int i = 0; i < cells.length; i++) {
      Point2D location = getLocation(cells[i]);
      locations[i][0] = location.getX();
      locations[i][1] = location.getY();
    }
    return locations;
  }

  /**
   * Same as getLocations, but with width and height added at index 3 and 4
   * respectively.
   *
   * @param cells
   *            The cells to return the bounds for
   * @return Returns the bounds of the cells as an array of arrays
   *
   * @see #getLocations(Object[])
   */
  public double[][] getBounds(Object[] cells) {
    double[][] locations = new double[cells.length][4];
    for (int i = 0; i < cells.length; i++) {
      Rectangle2D bounds = getBounds(cells[i]);
      locations[i][0] = bounds.getX();
      locations[i][1] = bounds.getY();
      locations[i][2] = bounds.getWidth();
      locations[i][3] = bounds.getHeight();
    }
    return locations;
  }

  /**
   * Returns the current location of the specified cell
   *
   * @param cell
   *            the cell whose location is to be determined
   * @return Returns the current location of the specified cell
   */
  public Point2D getLocation(Object cell) {
    Rectangle2D rect = getBounds(cell);
    if (rect != null)
      return new Point2D.Double(rect.getX(), rect.getY());
    return null;
  }

  /**
   * Sets the locations of the specified cells according to the arrays
   * specified in <code>locations</code>. The cells and locations array
   * must contain the same number of elements.
   *
   * @param cells
   *            The cells to change the locations for
   * @param locations
   *            The new locations as an array of arrays
   *
   * @see #getLocations(Object[])
   */
  public void setLocations(Object[] cells, double[][] locations) {
    if (cells != null && locations != null
        && cells.length == locations.length) {
      for (int i = 0; i < cells.length; i++)
        setLocation(cells[i], locations[i][0], locations[i][1], true);
    }
  }

  /**
   * Same as setLocations, but with width and height added at index 3 and 4
   * respectively.
   *
   * @param cells
   *            The cells to change the bounds for
   * @param locations
   *            The new bounds as an array of arrays
   *
   * @see #getLocations(Object[])
   */
  public void setBounds(Object[] cells, double[][] locations) {
    if (cells != null && locations != null
        && cells.length == locations.length) {
      for (int i = 0; i < cells.length; i++)
        setBounds(cells[i], new Rectangle2D.Double(locations[i][0],
            locations[i][1], locations[i][2], locations[i][3]));
    }
  }

  /**
   * Sets the current location of the specified cell. This checks if the cell
   * is moveable.
   *
   * @param cell
   *            the cell whose location is to be set
   * @param x
   *            the new x-axs location of the cell
   * @param y
   *            the new y-axs location of the cell
   *
   * @see #isMoveable(Object)
   */
  public void setLocation(Object cell, double x, double y) {
    setLocation(cell, x, y, true);
  }

  /**
   * Sets the current location of the specified cell. This checks if the cell
   * is moveable.
   *
   * @param cell
   *            the cell whose location is to be set
   * @param x
   *            the new x-axs location of the cell
   * @param y
   *            the new y-axs location of the cell
   * @param moveGroups
   *         whether or not to move group cells
   *
   * @see #isMoveable(Object)
   */
  public void setLocation(Object cell, double x, double y, boolean moveGroups) {
    if (cell != null) {
      Rectangle2D rect = getBounds(cell); // construct the rectangle
//      System.out.println("set Location for cell " + model.getValue(cell) + " to " + x + "," + y);
//      System.out.println("set Location, old position = " + rect.getX() + "," + rect.getY());
//      System.out.println("Dimensions = " + rect.getWidth() + " , " + rect.getHeight());
      if (isMoveable(cell) && rect != null) {
        if (moveGroups) {
          // Check for child cells
          double translationX = x - rect.getX();
          double translationY = y - rect.getY();
          int numChildrenCells = model.getChildCount(cell);
          for (int i = 0; i < numChildrenCells; i++) {
            Object childCell = model.getChild(cell, i);
            // In case model only facade check for null layout cache
            // Save overriding method in JGraphModelFacade
            boolean childVisible = true;
            if (graphLayoutCache != null) {
              childVisible = graphLayoutCache.isVisible(childCell);
            }
            if (DefaultGraphModel.isVertex(model, childCell)
                && childVisible
                && cell != childCell) {
              translate(childCell, translationX, translationY);
            }
          }
          rect.setFrame(x, y, rect.getWidth(), rect.getHeight());
        } else {
          rect.setFrame(x, y, rect.getWidth(), rect.getHeight());
        }
      }
      if (rect == null) {
        rect = new Rectangle2D.Double(x, y, 0, 0);
        setBounds(cell, rect);
      }
    }
  }

  /**
   * Moved the specified cell by the specified x and y co-ordinate amounts
   *
   * @param cell
   *            the cell to be moved
   * @param dx
   *            the amount by which the cell will be translated in the x-axis
   * @param dy
   *            the amount by which the cell will be translated in the y-axis
   */
  public void translate(Object cell, double dx, double dy) {
    Rectangle2D rect = getBounds(cell);
    if (isMoveable(cell) && rect != null)
    {
      int numChildCells = model.getChildCount(cell);
      boolean hasChildren = false;
      for (int i = 0; i < numChildCells; i++) {
        Object childCell = model.getChild(cell, i);
        if (DefaultGraphModel.isVertex(model, childCell)
            && graphLayoutCache.isVisible(childCell)
            && cell != childCell) {
          translate(childCell, dx, dy);
          hasChildren = true;
        } else if (model.isEdge(childCell) && cell != childCell) {
          hasChildren = true;
        }
      }

      if (!hasChildren)
      {
        rect.setFrame(rect.getX() + dx, rect.getY() + dy, rect
            .getWidth(), rect.getHeight());
      }
    }
  }

  /**
   * Obtains the maximum width or height dimension of any of the vertices in
   * the specified collection
   *
   * @param vertices
   *            collection of vertices to be analysed
   * @return the maximum width or height of any of the vertices
   */
  public Dimension getMaxSize(Collection vertices) {
    // Maximum width or height
    Dimension d = new Dimension(0, 0);
    // Iterate over all vertices
    Iterator it = vertices.iterator();
    while (it.hasNext()) {
      Dimension2D size = getSize(it.next());
      // Update Maximum
      if (size != null)
        d.setSize(Math.max(d.getWidth(), size.getWidth()), Math.max(d
            .getHeight(), size.getHeight()));
    }
    return d;
  }

  /**
   * Sets the current size of the specified cell.
   *
   * @param cell
   *            the cell whose size is to be set
   * @param width
   *            the new width of the cell
   * @param height
   *            the new height of the cell
   */
  public void setSize(Object cell, double width, double height) {
    Rectangle2D rect = getBounds(cell);
    rect.setFrame(rect.getX(), rect.getY(), width, height);
  }

  /**
   * Return the size of the specified cell
   *
   * @param cell
   *            the cell whose size is to be returned
   * @return Returns the current size of the specified cell.
   */
  public Dimension2D getSize(Object cell) {
    Rectangle2D rect = getBounds(cell);
    return new Dimension((int) rect.getWidth(), (int) rect.getHeight());
  }

  /**
   * Returns the points of the specified edge. The list may contain PortView
   * instances. Do a typecheck when iterating through the elements of this
   * list, and use PortView.getLocation to get the position of the port.
   *
   * @param edge
   *            the cell whose points are returned
   * @return Returns the points of the specified edge
   */
  public List getPoints(Object edge) {
    Map map = getAttributes(edge);
    List points = GraphConstants.getPoints(map);
    if (points == null) {
      points = new ArrayList(4);
      points.add(new AttributeMap.SerializablePoint2D(10, 10));
      points.add(new AttributeMap.SerializablePoint2D(20, 20));
    }
    return points;
  }

  /**
   * Sets the points of the specified edge
   *
   * @param edge
   *            the edge whose points are to be set
   * @param points
   *            the new list of points for the edge
   */
  public void setPoints(Object edge, List points) {
    Map map = getAttributes(edge);
    GraphConstants.setPoints(map, points);
  }

  /**
   * Disables per-edge on the specified edge
   *
   * @param edge
   *            the edge to have per-edge routing disabled
   */
  public void disableRouting(Object edge) {
    Map map = getAttributes(edge);
    GraphConstants.setRemoveAttributes(map,
        new Object[] { GraphConstants.ROUTING });
  }

  /**
   * Returns the edges between two specified ports or two specified vertices.
   * If directed is true then <code>cell1</code> must be the source of the
   * returned edges.
   *
   * @param cell1
   *            the first of the pair of cells to find edges between
   * @param cell2
   *            the second of the pair of cells to find edges between
   * @param directed
   *            whether or not only edges going from <code>cell1</code> to
   *            <code>cell2</code> should be returned and not edges in the
   *            other direction
   */
  public Object[] getEdgesBetween(Object cell1, Object cell2, boolean directed) {
    if (graphLayoutCache != null && graphLayoutCache.isPartial()
        && edgePromotion) {
      Set cells1 = getHiddenChildren(cell1);
      Set cells2 = getHiddenChildren(cell2);

      // Optimise for the standard case of no child cells
      if (cells1.size() == 1 && cells2.size() == 1) {
        return DefaultGraphModel.getEdgesBetween(model,
            cell1, cell2, directed);
      }
      // The object array to be returned
      Object[] edgesBetween = null;
      Iterator iter1 = cells1.iterator();
      while (iter1.hasNext()) {
        Object tempCell1 = iter1.next();
        Iterator iter2 = cells2.iterator();
        while (iter2.hasNext()) {
          Object tempCell2 = iter2.next();
          Object[] edges = DefaultGraphModel.getEdgesBetween(model,
              tempCell1, tempCell2, directed);
          if (edges.length > 0) {
            if (edgesBetween == null) {
              edgesBetween = edges;
            } else {
              // need to copy everything into a new array
              Object[] newArray = new Object[edges.length
                                             + edgesBetween.length];
              System.arraycopy(edgesBetween, 0, newArray, 0,
                  edgesBetween.length);
              System.arraycopy(edges, 0, newArray,
                  edgesBetween.length, edges.length);
              edgesBetween = newArray;
            }
          }
        }
      }
      return edgesBetween;

    } else {
      return DefaultGraphModel.getEdgesBetween(model, cell1, cell2,
          directed);
    }
  }

  /**
   * Divides the graph into groups of sibling vertices, vertices that
   * share the same parent. This is mostly used for layouting of cell
   * relative to their group context.
   *
   */
  protected void determineLayoutHierarchies() {
    if (model != null) {
      groupHierarchies = new ArrayList();
      Set rootsSet = null;
      Object[] modelRoots = DefaultGraphModel.getRoots(model);
      for (int i = 0; i < modelRoots.length; i++) {
        if (DefaultGraphModel.isVertex(model, modelRoots[i])) {
          populateGroupHierarchies(modelRoots[i]);
          if (rootsSet == null) {
            rootsSet = new LinkedHashSet();
          }
          rootsSet.add(modelRoots[i]);
        }
      }
      if (rootsSet != null) {
        groupHierarchies.add(rootsSet);
      }
     
    }
  }

  /**
   * Creates a set of sibling vertices and adds them to the group
   * hierarchy collection. The list of hierarchies will naturally
   * form in an order
   * @param vertex The parent vertex to the returned vertices
   */
  protected void populateGroupHierarchies(Object vertex) {
    LinkedHashSet result = null;
    if (vertex != null) {
      for (int i = 0; i < model.getChildCount(vertex); i++) {
        Object child = model.getChild(vertex, i);
        if (DefaultGraphModel.isVertex(model, child)) {
          if (result == null) {
            result = new LinkedHashSet();
          }
          result.add(child);
          populateGroupHierarchies(child);
        }
      }
    }
    if (groupHierarchies == null) {
      groupHierarchies = new ArrayList();
    }
    if (result != null) {
      groupHierarchies.add(result);
    }
  }

  /**
   * Returns the number of root vertices to be used by tree layouts for tree
   * traversal.
   *
   * @return the number of root vertices to be used by tree layouts
   */
  public int getRootCount() {
    return roots.size();
  }

  /**
   * Returns the root at <code>index</code> to be used by tree layouts for
   * tree traversal.
   *
   * @return the root vertex to be used by tree layouts
   *
   * @see #dfs(Object, JGraphFacade.CellVisitor)
   */
  public Object getRootAt(int index) {
    return roots.get(index);
  }

  /**
   * Returns true if <code>cell</code> is a root.
   *
   * @param cell
   *            the cell to test
   *
   * @return Returns true if <code>cell</code> is a root
   */
  public boolean isRoot(Object cell) {
    return roots.contains(cell);
  }

  /**
   * Returns the list of root vertices.
   *
   * @return Returns the {@link #roots}
   */
  public List getRoots() {
    return roots;
  }

  /**
   * @param roots
   *            The roots to set.
   */
  public void setRoots(List roots) {
    this.roots = roots;
  }

  /**
   * @return Returns the directed.
   */
  public boolean isDirected() {
    return directed;
  }

  /**
   * @param directed
   *            The directed to set.
   */
  public void setDirected(boolean directed) {
    this.directed = directed;
  }

  /**
   * @return Returns the order.
   */
  public Comparator getOrder() {
    return order;
  }

  /**
   * @param order
   *            The order to set.
   */
  public void setOrder(Comparator order) {
    this.order = order;
  }

  /**
   * @return Returns the ignoresCellsInGroups.
   */
  public boolean IsIgnoresCellsInGroups() {
    return ignoresCellsInGroups;
  }

  /**
   * @param ignoresCellsInGroups
   *            Sets ignoresCellsInGroups.
   */
  public void setIgnoresCellsInGroups(boolean ignoresCellsInGroups) {
    this.ignoresCellsInGroups = ignoresCellsInGroups;
  }

  /**
   * @return Returns the ignoresHiddenCells.
   */
  public boolean isIgnoresHiddenCells() {
    return ignoresHiddenCells;
  }

  /**
   * The GraphLayoutCache instance on the JGraphFacade object must be
   * set correctly in order to change this flag. If the graphLayoutCache
   * is null, this flag will be forced to false
   * @param ignoresHiddenCells
   *            The ignoresHiddenCells to set.
   */
  public void setIgnoresHiddenCells(boolean ignoresHiddenCells) {
    if (graphLayoutCache != null) {
      this.ignoresHiddenCells = ignoresHiddenCells;
    } else {
      this.ignoresHiddenCells = false;
    }
  }

  /**
   * @return Returns the ignoresUnconnectedCells.
   */
  public boolean isIgnoresUnconnectedCells() {
    return ignoresUnconnectedCells;
  }

  /**
   * @param ignoresUnconnectedCells
   *            The ignoresUnconnectedCells to set.
   */
  public void setIgnoresUnconnectedCells(boolean ignoresUnconnectedCells) {
    this.ignoresUnconnectedCells = ignoresUnconnectedCells;
  }

  /**
   * @return Returns the edgePromotion.
   */
  public boolean isEdgePromotion() {
    return edgePromotion;
  }

  /**
   * @param edgePromotion
   *            The edgePromotion to set.
   */
  public void setEdgePromotion(boolean edgePromotion) {
    this.edgePromotion = edgePromotion;
  }

  /**
   * @return Returns the verticesFilter.
   */
  public Set getVerticesFilter() {
    return verticesFilter;
  }

  /**
   * @param verticesFilter The verticesFilter to set.
   */
  public void setVerticesFilter(Set verticesFilter) {
    this.verticesFilter = verticesFilter;
  }

  /**
   * @return the groupHierarchies
   */
  public List getGroupHierarchies() {
    return groupHierarchies;
  }

  /**
   * @param groupHierarchies the groupHierarchies to set
   */
  public void setGroupHierarchies(List groupHierarchies) {
    this.groupHierarchies = groupHierarchies;
  }

  /**
   * @return the circleRadiusFactor
   */
  public double getCircleRadiusFactor() {
    return circleRadiusFactor;
  }

  /**
   * @param circleRadiusFactor the minCircleRadius to set
   */
  public void setCircleRadiusFactor(double circleRadiusFactor) {
    this.circleRadiusFactor = circleRadiusFactor;
  }

  /**
   * Performs a depth-first search of the input graph from the specified root
   * cell using the specified visitor to extract the tree information.
   * isVertex must return true on the passed-in root cell in order to
   * continue.
   *
   * @param root
   *            the node to start the search from
   * @param visitor
   *            the visitor that defines the operations to be performed upon
   *            the graph model
   */
  public void dfs(Object root, CellVisitor visitor) {
    // DFS should return maximum depth
    if (isVertex(root)) {
      dfs(null, root, null, visitor, new HashSet(), 0, 0);
    }
  }

  /**
   * Performs a depth-first search of the input graph from the specified root
   * cell using the specified visitor to extract the tree information
   *
   * @param parent
   *            the parent of the current cell
   * @param root
   *            the node to start the search from
   * @param previousSibling
   *            the last neighbour of the current cell found
   * @param visitor
   *            the visitor that defines the operations to be performed upon
   *            the graph model
   * @param seen
   *            the set of cells that have already been seen
   * @param layer
   *            the current layer of the tree
   * @param sibling
   *            the number of siblings to the current cell
   */
  public void dfs(Object parent, Object root, Object previousSibling,
      CellVisitor visitor, Set seen, int layer, int sibling) {
    if (root != null && !seen.contains(root)) {
      seen.add(root);
      visitor.visit(parent, root, previousSibling, layer, sibling);
      // Recurse unseen neighbours
      sibling = 0;
      Object previous = null;
      Iterator it = getNeighbours(root, seen, ordered).iterator();
      while (it.hasNext()) {
        Object current = it.next();
        // Root check is O(|roots|)
        if (isVertex(current) && !isRoot(current)) {
          dfs(root, current, previous, visitor, seen, layer + 1,
              sibling);
          previous = current;
          sibling++;
        }
      }
    }
  }

  /**
   * Performs a depth-first search of the input graph from the specified root
   * cell using the specified visitor to extract the tree information
   *
   * @param parent
   *            the parent of the current cell
   * @param root
   *            the node to start the search from
   * @param previousSibling
   *            the last neighbour of the current cell found
   * @param visitor
   *            the visitor that defines the operations to be performed upon
   *            the graph model
   * @param seen
   *            the set of cells that have already been seen
   * @param layer
   *            the current layer of the tree
   * @param sibling
   *            the number of siblings to the current cell
   */
  public void dfs(Object parent, Object root, Object previousSibling,
      CellVisitor visitor, Set seen, Set ancestors, int layer, int sibling) {
    if (root != null) {
      if (parent != null) {
        ancestors.add(parent);
      }
      visitor.visit(parent, root, previousSibling, layer, sibling);
      if (!seen.contains(root)) {
        seen.add(root);
        // Recurse unseen neighbours
        sibling = 0;
        Object previous = null;
        Iterator it = getNeighbours(root, seen, true).iterator();
        while (it.hasNext()) {
          Object current = it.next();
          // Root check is O(|roots|)
          if (isVertex(current) && !isRoot(current)) {
            dfs(root, current, previous, visitor, seen,
                new HashSet(ancestors), layer + 1, sibling);
            previous = current;
            sibling++;
          }
        }
      }
    }
  }

  /**
   * Performs a breath-first search of the input graph from the specified root
   * cell using the specified visitor to extract the tree information.
   *
   * @param visitor
   *            the visitor that defines the operations to be performed upon
   *            the graph model
   */
  public void bfs(Object root, CellVisitor visitor) {
    // Track the number of cells on the next level, and the number of
    // unprocessed cells on the current level
    int numCellsCurrentLevel = 1;
    int numCellsNextLevel = 0;
    int currentLayer = 0;

    Stack cellStack = new Stack();
    cellStack.push(root);
    Set seen = new HashSet();

    while (!cellStack.isEmpty()) {
      Object current = cellStack.pop();
      if (!seen.contains(current)) {
        seen.add(root);
        visitor.visit(null, current, null, currentLayer, 0);
        // Recurse unseen neighbours
        Iterator it = getNeighbours(current, seen, true).iterator();
        while (it.hasNext()) {
          Object childCell = it.next();
          if (!seen.contains(childCell)) {
            cellStack.push(childCell);
            numCellsNextLevel++;
          }
        }
      }
      // Work out if we have finished the current level
      numCellsCurrentLevel--;
      if (--numCellsCurrentLevel <= 0) {
        numCellsCurrentLevel = numCellsNextLevel;
        numCellsNextLevel = 0;
        currentLayer++;
      }
    }
  }

  /**
   * Utility method to update the array of tree roots in a graph. This sets
   * all cells that have no incoming and one or more outgoing edges, or the
   * cell with the largest difference between outgoing and incoming edges if
   * no root cells exist.
   */
  public void findTreeRoots() {
    Object[] vertices = getCells(getAll(), false, false).toArray();

    List roots = new ArrayList();
    int maxDiff = 0;
    Object root = null;
    for (int i = 0; i < vertices.length; i++) {
      int fanin = getIncomingEdges(vertices[i], null, true, false).size();
      int fanout = getOutgoingEdges(vertices[i], null, true, false).size();
      if (fanin == 0)
        roots.add(vertices[i]);

      // Keeps a reference to the best matching cell, ie the one with the
      // greatest difference between outgoing and incoming edges in case
      // no real roots exist.
      int diff = fanout - fanin;
      if (diff >= maxDiff) {
        root = vertices[i];
        maxDiff = diff;
      }
    }

    // Returns the best match in case no real roots exist
    if (roots.isEmpty() && root != null)
      roots.add(root);
    this.roots = roots;
  }

  /**
   * Defines the interface that visitors use to perform operations upon the
   * graph information during depth first search (dfs) or other tree-traversal
   * strategies implemented by subclassers.
   */
  public interface CellVisitor {

    /**
     * The method within which the visitor will perform operations upon the
     * graph model
     *
     * @param parent
     *            the parent cell the current cell
     * @param cell
     *            the current cell visited
     * @param previousSibling
     *            the last neighbour cell found
     * @param layer
     *            the current layer of the tree
     * @param sibling
     *            the number of sibling to the current cell found
     */
    public void visit(Object parent, Object cell, Object previousSibling,
        int layer, int sibling);
  }

  /**
   * A default comparator for ordering cell views. Returns the order of the
   * cells as ordered in <code>roots</code> in the model. Enables layouts
   * with levels to be laid out deterministically. <b>Be very careful</b>
   * using the default comparator on the default graph model,
   * <code>getIndexOfRoot</code> has linear performance and so sorting the
   * entire model roots will have quadratic performance.
   */
  public class DefaultComparator implements Comparator {

    /*
     * (non-Javadoc)
     *
     * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
     */
    public int compare(Object c1, Object c2) {
      Object p1 = model.getParent(c1);
      Object p2 = model.getParent(c2);
      int index1 = (p1 == null) ? model.getIndexOfRoot(c1) : model
          .getIndexOfChild(p1, c1);
      int index2 = (p2 == null) ? model.getIndexOfRoot(c2) : model
          .getIndexOfChild(p2, c2);
      return new Integer(index1).compareTo(new Integer(index2));
    }

  }

  /**
   * @return Returns the ordered.
   */
  public boolean isOrdered() {
    return ordered;
  }

  /**
   * @param ordered The ordered to set.
   */
  public void setOrdered(boolean ordered) {
    this.ordered = ordered;
  }

  /**
   * Sets the logging level of this class
   * @param level the logging level to set
   */
  public void setLoggerLevel(Level level) {
    try {
      logger.setLevel(level);
    } catch (SecurityException e) {
      // Probably running in an applet
    }
  }
}
TOP

Related Classes of com.jgraph.layout.JGraphFacade

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.