0001: /*
0002: * @(#)DefaultGraphModel.java 1.0 03-JUL-04
0003: *
0004: * Copyright (c) 2001-2004 Gaudenz Alder
0005: *
0006: */
0007: package org.jgraph.graph;
0008:
0009: import java.io.IOException;
0010: import java.io.ObjectInputStream;
0011: import java.io.Serializable;
0012: import java.util.ArrayList;
0013: import java.util.Collection;
0014: import java.util.HashSet;
0015: import java.util.Hashtable;
0016: import java.util.Iterator;
0017: import java.util.LinkedHashSet;
0018: import java.util.LinkedList;
0019: import java.util.List;
0020: import java.util.Map;
0021: import java.util.Set;
0022: import java.util.Stack;
0023:
0024: import javax.swing.event.EventListenerList;
0025: import javax.swing.tree.DefaultMutableTreeNode;
0026: import javax.swing.tree.MutableTreeNode;
0027: import javax.swing.tree.TreeNode;
0028: import javax.swing.undo.AbstractUndoableEdit;
0029: import javax.swing.undo.CannotRedoException;
0030: import javax.swing.undo.CannotUndoException;
0031: import javax.swing.undo.CompoundEdit;
0032: import javax.swing.undo.UndoableEdit;
0033: import javax.swing.undo.UndoableEditSupport;
0034:
0035: import org.jgraph.event.GraphModelEvent;
0036: import org.jgraph.event.GraphModelListener;
0037:
0038: /**
0039: * A simple implementation of a graph model.
0040: *
0041: * @version 1.0 1/1/02
0042: * @author Gaudenz Alder
0043: */
0044:
0045: public class DefaultGraphModel extends UndoableEditSupport implements
0046: Serializable, GraphModel {
0047:
0048: /**
0049: * The list of listeners that listen to the model.
0050: */
0051: protected transient EventListenerList listenerList = new EventListenerList();
0052:
0053: /**
0054: * Default instance of an empty iterator.
0055: */
0056: protected transient Iterator emptyIterator = new EmptyIterator();
0057:
0058: /**
0059: * Set that contains all root cells of this model.
0060: */
0061: protected List roots = null;
0062:
0063: /**
0064: * Indicates whether isLeaf is based on a node's allowsChildren value.
0065: */
0066: protected boolean asksAllowsChildren = false;
0067:
0068: /**
0069: * Whether or not to remove group cells from the model when all of their
0070: * children are removed
0071: */
0072: protected boolean removeEmptyGroups = true;
0073:
0074: /**
0075: * The model's own attributes as a map. Defaults to an empty Hashtable.
0076: */
0077: protected AttributeMap attributes = null;
0078:
0079: /**
0080: * A collection of unexecuted updates on this model
0081: */
0082: // protected Collection currentUpdate = new ArrayList();
0083: /**
0084: * Whether or not an update is in the process of being dispatched
0085: */
0086: // private transient boolean isDispatching = false;
0087: /**
0088: * Constructs a model that is not an attribute store.
0089: */
0090: public DefaultGraphModel() {
0091: this (null, null);
0092: }
0093:
0094: /**
0095: * Constructs a model that is not an attribute store.
0096: */
0097: public DefaultGraphModel(List roots, AttributeMap attributes) {
0098: if (roots != null)
0099: this .roots = roots;
0100: else
0101: this .roots = new ArrayList();
0102: if (attributes != null)
0103: this .attributes = attributes;
0104: else
0105: this .attributes = new AttributeMap();
0106: }
0107:
0108: /**
0109: * Constructs a model using the specified information to construct the
0110: * cells, attributes and connection data.
0111: */
0112: public DefaultGraphModel(List roots, AttributeMap attributes,
0113: ConnectionSet cs) {
0114: this (roots, attributes);
0115: handleConnectionSet(cs);
0116: }
0117:
0118: public List getRoots() {
0119: return roots;
0120: }
0121:
0122: //
0123: // Graph Model
0124: //
0125:
0126: /**
0127: * Returns the number of roots in the model. Returns 0 if the model is
0128: * empty.
0129: *
0130: * @return the number of roots in the model
0131: */
0132: public int getRootCount() {
0133: return roots.size();
0134: }
0135:
0136: /**
0137: * Returns the root at index <I>index </I> in the model. This should not
0138: * return null if <i>index </i> is a valid index for the model (that is
0139: * <i>index </i>>= 0 && <i>index </i>< getRootCount()).
0140: *
0141: * @return the root of at index <I>index </I>
0142: */
0143: public Object getRootAt(int index) {
0144: return roots.get(index);
0145: }
0146:
0147: /**
0148: * Returns the index of <code>root</code> in the model. If root is
0149: * <code>null</code>, returns -1.
0150: *
0151: * @param root
0152: * a root in the model, obtained from this data source
0153: * @return the index of the root in the model, or -1 if the parent is
0154: * <code>null</code>
0155: */
0156: public int getIndexOfRoot(Object root) {
0157: return roots.indexOf(root);
0158: }
0159:
0160: /**
0161: * Returns <code>true</code> if <code>node</code> or one of its
0162: * ancestors is in the model.
0163: *
0164: * @return <code>true</code> if <code>node</code> is in the model
0165: */
0166: public boolean contains(Object node) {
0167: Object parentNode = null;
0168: while ((parentNode = getParent(node)) != null)
0169: node = parentNode;
0170: return roots.contains(node);
0171: }
0172:
0173: /**
0174: * Returns a <code>Map</code> that represents the attributes for the
0175: * specified cell. This attributes have precedence over each view's
0176: * attributes, regardless of isAttributeStore.
0177: *
0178: * @return attributes of <code>node</code> as a <code>Map</code>
0179: */
0180: public AttributeMap getAttributes(Object node) {
0181: if (node instanceof GraphCell)
0182: return ((GraphCell) node).getAttributes();
0183: else if (node == null)
0184: return attributes;
0185: return null;
0186: }
0187:
0188: /**
0189: * @return Returns the user object of the given cell. This implementation
0190: * checks if the cell is a default mutable tree node and returns
0191: * it's user object.
0192: */
0193: public Object getValue(Object cell) {
0194: if (cell instanceof DefaultMutableTreeNode)
0195: return ((DefaultMutableTreeNode) cell).getUserObject();
0196: return null;
0197: }
0198:
0199: /**
0200: * Returns the graph model's attribute. Shortcut to <code>
0201: * getAttributes(null)</code>.
0202: *
0203: * @return attributes of <code>node</code> as a <code>Map</code>
0204: */
0205: public Map getAttributes() {
0206: return getAttributes(null);
0207: }
0208:
0209: //
0210: // Graph Structure
0211: //
0212:
0213: /**
0214: * Returns the source of <code>edge</code>. <I>edge </I> must be an
0215: * object previously obtained from this data source.
0216: *
0217: * @return <code>Object</code> that represents the source of <i>edge </i>
0218: */
0219: public Object getSource(Object edge) {
0220: if (edge instanceof Edge)
0221: return ((Edge) edge).getSource();
0222: return null;
0223: }
0224:
0225: /**
0226: * Returns the target of <code>edge</code>. <I>edge </I> must be an
0227: * object previously obtained from this data source.
0228: *
0229: * @return <code>Object</code> that represents the target of <i>edge </i>
0230: */
0231: public Object getTarget(Object edge) {
0232: if (edge instanceof Edge)
0233: return ((Edge) edge).getTarget();
0234: return null;
0235: }
0236:
0237: /**
0238: * Returns <code>true</code> if <code>port</code> is a valid source for
0239: * <code>edge</code>. <I>edge </I> and <I>port </I> must be objects
0240: * previously obtained from this data source.
0241: *
0242: * @return <code>true</code> if <code>port</code> is a valid source for
0243: * <code>edge</code>.
0244: */
0245: public boolean acceptsSource(Object edge, Object port) {
0246: return true;
0247: }
0248:
0249: /**
0250: * Returns <code>true</code> if <code>port</code> is a valid target for
0251: * <code>edge</code>. <I>edge </I> and <I>port </I> must be objects
0252: * previously obtained from this data source.
0253: *
0254: * @return <code>true</code> if <code>port</code> is a valid target for
0255: * <code>edge</code>.
0256: */
0257: public boolean acceptsTarget(Object edge, Object port) {
0258: return true;
0259: }
0260:
0261: /**
0262: * Returns an iterator of the edges connected to <code>port</code>.
0263: * <I>port </I> must be a object previously obtained from this data source.
0264: * This method never returns null.
0265: *
0266: * @param port
0267: * a port in the graph, obtained from this data source
0268: * @return <code>Iterator</code> that represents the connected edges
0269: */
0270: public Iterator edges(Object port) {
0271: if (port instanceof Port)
0272: return ((Port) port).edges();
0273: return emptyIterator;
0274: }
0275:
0276: /**
0277: * Returns <code>true</code> if <code>edge</code> is a valid edge.
0278: *
0279: * @return <code>true</code> if <code>edge</code> is a valid edge.
0280: */
0281: public boolean isEdge(Object edge) {
0282: return edge instanceof Edge;
0283: }
0284:
0285: /**
0286: * Returns <code>true</code> if <code>port</code> is a valid port,
0287: * possibly supporting edge connection.
0288: *
0289: * @return <code>true</code> if <code>port</code> is a valid port.
0290: */
0291: public boolean isPort(Object port) {
0292: return port instanceof Port;
0293: }
0294:
0295: /**
0296: * A shortcut method to create a connection set that represents the
0297: * connections in this model. Useful for encoding to avoid writing redundant
0298: * connection data stored in the cells.
0299: */
0300: public ConnectionSet getConnectionSet() {
0301: return ConnectionSet.create(this , DefaultGraphModel
0302: .getAll(this ), false);
0303: }
0304:
0305: //
0306: // Group Structure
0307: //
0308:
0309: /**
0310: * Returns a map of (cell, clone)-pairs for all <code>cells</code>. In
0311: * the new array, all references are replaced with references to the cloned
0312: * cells (ie parent or anchor). This method does only include children which
0313: * are in <code>cells</code>. Use JGraph.getDescendants to get a complete
0314: * list of all children.
0315: */
0316: public Map cloneCells(Object[] cells) {
0317: Map map = new Hashtable();
0318: // Add Cells to Queue
0319: for (int i = 0; i < cells.length; i++)
0320: map.put(cells[i], cloneCell(cells[i]));
0321: // Replace Parent and Anchors
0322: Iterator it = map.entrySet().iterator();
0323: Object obj, cell, parent;
0324: while (it.hasNext()) {
0325: Map.Entry entry = (Map.Entry) it.next();
0326: obj = entry.getValue();
0327: cell = entry.getKey();
0328:
0329: // Replaces the cloned cell's parent with the parent's clone
0330: parent = getParent(cell);
0331: if (parent != null)
0332: parent = map.get(parent);
0333: if (parent != null)
0334: ((DefaultMutableTreeNode) parent)
0335: .add((DefaultMutableTreeNode) obj);
0336:
0337: // Replaces the anchors for ports
0338: if (obj instanceof Port) {
0339: Object anchor = ((Port) obj).getAnchor();
0340: if (anchor != null)
0341: ((Port) obj).setAnchor((Port) map.get(anchor));
0342: }
0343: }
0344: return map;
0345: }
0346:
0347: /**
0348: * Sets the parent of the specified cell.
0349: */
0350: protected void setParent(Object child, Object parent) {
0351: if (child instanceof DefaultMutableTreeNode
0352: && parent instanceof DefaultMutableTreeNode) {
0353: DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) parent;
0354: parentNode.add((DefaultMutableTreeNode) child);
0355: }
0356: }
0357:
0358: /**
0359: * Creates a shallow copy of the cell including a copy of the user object.
0360: * Subclassers can override the cloneUserObject to provide a custom user
0361: * object cloning mechanism.
0362: */
0363: protected Object cloneCell(Object cellObj) {
0364: if (cellObj instanceof DefaultGraphCell) {
0365: // Clones the cell
0366: DefaultGraphCell cell = (DefaultGraphCell) cellObj;
0367: DefaultGraphCell clone = (DefaultGraphCell) cell.clone();
0368: // Clones the user object
0369: clone.setUserObject(cloneUserObject(cell.getUserObject()));
0370: return clone;
0371: }
0372: return cellObj;
0373: }
0374:
0375: /**
0376: * Clones the user object. Helper method that is invoked from cloneCells.
0377: * You must use cloneCells (or cloneCell for single cells) to get a deep
0378: * copy of a clone. Subclassers must override this and valueForCellChanged
0379: * to implement custom user objects. This implementation returns
0380: * <code>object</code>.
0381: */
0382: protected Object cloneUserObject(Object userObject) {
0383: return userObject;
0384: }
0385:
0386: /**
0387: * Returns the parent of <I>child </I> in the model. <I>child </I> must be a
0388: * node previously obtained from this data source. This returns null if
0389: * <i>child </i> is a root in the model.
0390: *
0391: * @param child
0392: * a node in the graph, obtained from this data source
0393: * @return the parent of <I>child </I>
0394: */
0395: public Object getParent(Object child) {
0396: if (child != null && child instanceof TreeNode)
0397: return ((TreeNode) child).getParent();
0398: return null;
0399: }
0400:
0401: /**
0402: * Returns the index of child in parent. If either the parent or child is
0403: * <code>null</code>, returns -1.
0404: *
0405: * @param parent
0406: * a note in the tree, obtained from this data source
0407: * @param child
0408: * the node we are interested in
0409: * @return the index of the child in the parent, or -1 if either the parent
0410: * or the child is <code>null</code>
0411: */
0412: public int getIndexOfChild(Object parent, Object child) {
0413: if (parent == null || child == null)
0414: return -1;
0415: return ((TreeNode) parent).getIndex((TreeNode) child);
0416: }
0417:
0418: /**
0419: * Returns the child of <I>parent </I> at index <I>index </I> in the
0420: * parent's child array. <I>parent </I> must be a node previously obtained
0421: * from this data source. This should not return null if <i>index </i> is a
0422: * valid index for <i>parent </i> (that is <i>index </i>>= 0 && <i>index
0423: * </i>< getChildCount( <i>parent </i>)).
0424: *
0425: * @param parent
0426: * a node in the tree, obtained from this data source
0427: * @return the child of <I>parent </I> at index <I>index </I>
0428: */
0429: public Object getChild(Object parent, int index) {
0430: if (parent instanceof TreeNode)
0431: return ((TreeNode) parent).getChildAt(index);
0432: return null;
0433: }
0434:
0435: /**
0436: * Returns the number of children of <I>parent </I>. Returns 0 if the node
0437: * is a leaf or if it has no children. <I>parent </I> must be a node
0438: * previously obtained from this data source.
0439: *
0440: * @param parent
0441: * a node in the tree, obtained from this data source
0442: * @return the number of children of the node <I>parent </I>
0443: */
0444: public int getChildCount(Object parent) {
0445: if (parent instanceof TreeNode)
0446: return ((TreeNode) parent).getChildCount();
0447: return 0;
0448: }
0449:
0450: /**
0451: * Returns whether the specified node is a leaf node. The way the test is
0452: * performed depends on the.
0453: *
0454: * @param node
0455: * the node to check
0456: * @return true if the node is a leaf node
0457: */
0458: public boolean isLeaf(Object node) {
0459: if (asksAllowsChildren && node instanceof TreeNode)
0460: return !((TreeNode) node).getAllowsChildren();
0461: return ((TreeNode) node).isLeaf();
0462: }
0463:
0464: //
0465: // Change Support
0466: //
0467:
0468: /**
0469: * Inserts the <code>roots</code> and connections into the model. Notifies
0470: * the model- and undo listeners of the change. The passed-in edits are
0471: * executed if they implement the
0472: * <code>GraphModelEvent.ExecutableGraphChange</code> interface in
0473: * ascending array-order, after execution of the model change. Note: The
0474: * passed-in propertyMap may contain <code>PortView</code> s which must be
0475: * turned into <code>Point</code> s when stored in the model.
0476: */
0477: public void insert(Object[] roots, Map attributes,
0478: ConnectionSet cs, ParentMap pm, UndoableEdit[] edits) {
0479: GraphModelEdit edit = createEdit(roots, null, attributes, cs,
0480: pm, edits);
0481: if (edit != null) {
0482: edit.execute(); // fires graphChangeEvent
0483: if (edits != null) {
0484: for (int i = 0; i < edits.length; i++)
0485: if (edits[i] instanceof GraphLayoutCache.GraphLayoutCacheEdit)
0486: ((GraphLayoutCache.GraphLayoutCacheEdit) edits[i])
0487: .execute();
0488: }
0489: postEdit(edit); // fires undoableedithappened
0490: }
0491: }
0492:
0493: /**
0494: * Removes <code>cells</code> from the model. Notifies the model- and undo
0495: * listeners of the change.
0496: */
0497: public void remove(Object[] roots) {
0498: GraphModelEdit edit = createRemoveEdit(roots);
0499: if (edit != null) {
0500: edit.execute();
0501: postEdit(edit);
0502: }
0503: }
0504:
0505: /**
0506: * Shortcut to the new edit method which allows inserts and removes to go
0507: * along with an edit.
0508: */
0509: public void edit(Map attributes, ConnectionSet cs, ParentMap pm,
0510: UndoableEdit[] edits) {
0511: edit(null, null, attributes, cs, pm, edits);
0512: }
0513:
0514: /**
0515: * Applies <code>attributes</code> and the connection changes to the
0516: * model. The initial <code>edits</code> that triggered the call are
0517: * considered to be part of this transaction. The passed-in edits are
0518: * executed if they implement the
0519: * <code>GraphModelEvent.ExecutableGraphChange</code> interface in
0520: * ascending array-order, after execution of the model change. Notifies the
0521: * model- and undo listeners of the change. <strong>Note: </strong> If only
0522: * <code>edits</code> is non-null, the edits are directly passed to the
0523: * UndoableEditListeners. Note: The passed-in propertyMap may contains
0524: * PortViews which must be turned into Points when stored in the model.
0525: */
0526: public void edit(Object[] inserted, Object[] removed,
0527: Map attributes, ConnectionSet cs, ParentMap pm,
0528: UndoableEdit[] edits) {
0529: if ((inserted == null || inserted.length == 0)
0530: && (removed == null || removed.length == 0)
0531: && (attributes == null || attributes.isEmpty())
0532: && (cs == null || cs.isEmpty()) && pm == null
0533: && edits != null && edits.length == 1) {
0534: if (edits[0] instanceof GraphLayoutCache.GraphLayoutCacheEdit)
0535: ((GraphLayoutCache.GraphLayoutCacheEdit) edits[0])
0536: .execute();
0537: postEdit(edits[0]); // UndoableEdit Relay
0538: } else {
0539: GraphModelEdit edit = createEdit(inserted, removed,
0540: attributes, cs, pm, edits);
0541: if (edit != null) {
0542: edit.execute();
0543: if (edits != null) {
0544: for (int i = 0; i < edits.length; i++)
0545: if (edits[i] instanceof GraphLayoutCache.GraphLayoutCacheEdit)
0546: ((GraphLayoutCache.GraphLayoutCacheEdit) edits[i])
0547: .execute();
0548: }
0549: postEdit(edit);
0550: }
0551: }
0552: }
0553:
0554: /*
0555: * (non-Javadoc)
0556: *
0557: * @see com.jgraph.model.GraphModel#execute(com.jgraph.model.DefaultGraphModel.Change)
0558: */
0559: public synchronized void execute(ExecutableChange change) {
0560: // change.execute();
0561: // beginUpdate();
0562: // currentUpdate.add(change);
0563: // endUpdate();
0564: }
0565:
0566: /*
0567: * (non-Javadoc)
0568: *
0569: * @see com.jgraph.model.GraphModel#getUpdateLevel()
0570: */
0571: public int getUpdateLevel() {
0572: return super .getUpdateLevel();
0573: //return updateLevel;
0574: }
0575:
0576: /*
0577: * (non-Javadoc)
0578: *
0579: * @see javax.swing.undo.UndoableEditSupport#beginUpdate()
0580: */
0581: public void beginUpdate() {
0582: super .beginUpdate();
0583: //updateLevel++;
0584: }
0585:
0586: /*
0587: * (non-Javadoc)
0588: *
0589: * @see com.jgraph.model.GraphModel#endUpdate()
0590: */
0591: public void endUpdate() {
0592: super .endUpdate();
0593: // boolean dispatch = false;
0594: // synchronized (this) {
0595: // updateLevel--;
0596: // if (!isDispatching && updateLevel == 0) {
0597: // dispatch = true;
0598: // isDispatching = true;
0599: // }
0600: // }
0601: // if (dispatch) {
0602: // try {
0603: // if (!currentUpdate.isEmpty()) {
0604: // Collection changes = new ArrayList(currentUpdate);
0605: // currentUpdate.clear();
0606: // undoSupport
0607: // .postEdit(createEdit(processGraphChanges(changes)));
0608: // }
0609: // } finally {
0610: // isDispatching = false;
0611: // }
0612: // }
0613: }
0614:
0615: /**
0616: * Sends <code>cells</code> to back.
0617: */
0618: public void toBack(Object[] cells) {
0619: GraphModelLayerEdit edit = createLayerEdit(cells,
0620: GraphModelLayerEdit.BACK);
0621: if (edit != null) {
0622: edit.execute();
0623: postEdit(edit);
0624: }
0625: }
0626:
0627: /**
0628: * Brings <code>cells</code> to front.
0629: */
0630: public void toFront(Object[] cells) {
0631: GraphModelLayerEdit edit = createLayerEdit(cells,
0632: GraphModelLayerEdit.FRONT);
0633: if (edit != null) {
0634: edit.execute();
0635: postEdit(edit);
0636: }
0637: }
0638:
0639: protected GraphModelLayerEdit createLayerEdit(Object[] cells,
0640: int layer) {
0641: return new GraphModelLayerEdit(cells, layer);
0642: }
0643:
0644: //
0645: // Edit Creation
0646: //
0647:
0648: /**
0649: * Returns an edit that represents an insert.
0650: */
0651: // protected GraphModelEdit createInsertEdit(Object[] cells, Map
0652: // attributeMap,
0653: // ConnectionSet cs, ParentMap pm, UndoableEdit[] edits) {
0654: // return createEdit(cells, null, attributeMap, cs, pm, edits);
0655: // }
0656: /**
0657: * Returns an edit that represents a remove.
0658: */
0659: protected GraphModelEdit createRemoveEdit(Object[] cells) {
0660: // Remove from GraphStructure
0661: ConnectionSet cs = ConnectionSet.create(this , cells, true);
0662: // Remove from Group Structure
0663: ParentMap pm = ParentMap.create(this , cells, true, false);
0664: // Construct Edit
0665: GraphModelEdit edit = createEdit(null, cells, null, cs, pm,
0666: null);
0667: if (edit != null)
0668: edit.end();
0669: return edit;
0670: }
0671:
0672: protected GraphModelEdit createEdit(Object[] inserted,
0673: Object[] removed, Map attributes, ConnectionSet cs,
0674: ParentMap pm, UndoableEdit[] edits) {
0675: GraphModelEdit edit = new GraphModelEdit(inserted, removed,
0676: attributes, cs, pm);
0677: if (edit != null) {
0678: if (edits != null)
0679: for (int i = 0; i < edits.length; i++)
0680: edit.addEdit(edits[i]);
0681: edit.end();
0682: }
0683: return edit;
0684: }
0685:
0686: //
0687: // Change Handling
0688: //
0689:
0690: /**
0691: * Inserts <code>cells</code> into the model. Returns the cells that were
0692: * inserted (including descendants).
0693: */
0694: protected Object[] handleInsert(Object[] cells) {
0695: Object[] inserted = null;
0696: if (cells != null) {
0697: for (int i = 0; i < cells.length; i++)
0698: // Add to Roots if no parent
0699: if (getParent(cells[i]) == null)
0700: roots.add(cells[i]);
0701: // Return *all* inserted cells
0702: inserted = getDescendants(this , cells).toArray();
0703: }
0704: return inserted;
0705: }
0706:
0707: /**
0708: * Removes <code>cells</code> from the model. Returns the cells that were
0709: * removed as roots.
0710: */
0711: protected Object[] handleRemove(Object[] cells) {
0712: Set removedRoots = new HashSet();
0713: if (cells != null && cells.length > 0) {
0714: Set rootsSet = new HashSet(roots);
0715: for (int i = 0; i < cells.length; i++) {
0716: if (getParent(cells[i]) == null
0717: && rootsSet.contains(cells[i])) {
0718: removedRoots.add(cells[i]);
0719: }
0720: }
0721: if (removedRoots.size() > 0) {
0722: // If any roots have been removed, reform the roots
0723: // lists appropriately, keeping the order the same
0724: int newRootsSize = roots.size() - removedRoots.size();
0725: if (newRootsSize < 8) {
0726: newRootsSize = 8;
0727: }
0728: List newRoots = new ArrayList(newRootsSize);
0729: Iterator iter = roots.iterator();
0730: while (iter.hasNext()) {
0731: Object cell = iter.next();
0732: if (!removedRoots.contains(cell)) {
0733: newRoots.add(cell);
0734: }
0735: }
0736: roots = newRoots;
0737: }
0738: }
0739: return removedRoots.toArray();
0740: }
0741:
0742: /**
0743: * Applies <code>cells</code> to the model. Returns a parent map that may
0744: * be used to undo this change.
0745: */
0746: protected ParentMap handleParentMap(ParentMap parentMap) {
0747: if (parentMap != null) {
0748: ParentMap undo = new ParentMap();
0749: HashSet rootsSet = null;
0750: HashSet rootsToBeRemoved = null;
0751: Iterator it = parentMap.entries();
0752: while (it.hasNext()) {
0753: ParentMap.Entry entry = (ParentMap.Entry) it.next();
0754: Object child = entry.getChild();
0755: Object parent = entry.getParent();
0756: undo.addEntry(child, getParent(child));
0757: if (parent == null) {
0758: if (child instanceof MutableTreeNode) {
0759: ((MutableTreeNode) child).removeFromParent();
0760: }
0761: } else {
0762: if (parent instanceof DefaultMutableTreeNode
0763: && child instanceof MutableTreeNode) {
0764: ((DefaultMutableTreeNode) parent)
0765: .add((MutableTreeNode) child);
0766: }
0767: }
0768:
0769: if (rootsSet == null) {
0770: rootsSet = new HashSet(roots);
0771: }
0772: boolean isRoot = rootsSet.contains(child);
0773: if (parent == null && !isRoot) {
0774: rootsSet.add(child);
0775: roots.add(child);
0776: } else if (parent != null && isRoot) {
0777: if (rootsToBeRemoved == null) {
0778: rootsToBeRemoved = new HashSet();
0779: }
0780: rootsSet.remove(child);
0781: rootsToBeRemoved.add(child);
0782: }
0783: }
0784: if (rootsToBeRemoved != null && rootsToBeRemoved.size() > 0) {
0785: // If any roots have been removed, reform the roots
0786: // lists appropriately, keeping the order the same
0787: int newRootsSize = roots.size()
0788: - rootsToBeRemoved.size();
0789: if (newRootsSize < 8) {
0790: newRootsSize = 8;
0791: }
0792: List newRoots = new ArrayList(newRootsSize);
0793: Iterator iter = roots.iterator();
0794: while (iter.hasNext()) {
0795: Object cell = iter.next();
0796: if (!rootsToBeRemoved.contains(cell)) {
0797: newRoots.add(cell);
0798: }
0799: }
0800: roots = newRoots;
0801: }
0802: return undo;
0803: }
0804: return null;
0805: }
0806:
0807: /**
0808: * Applies <code>attributes</code> to the cells specified as keys. Returns
0809: * the <code>attributes</code> to undo the change.
0810: */
0811: protected Map handleAttributes(Map attributes) {
0812: if (attributes != null) {
0813: Hashtable undo = new Hashtable(attributes.size());
0814: Iterator it = attributes.entrySet().iterator();
0815: while (it.hasNext()) {
0816: Map.Entry entry = (Map.Entry) it.next();
0817: Object cell = entry.getKey();
0818: Map deltaNew = (Map) entry.getValue();
0819: // System.out.println("deltaNew="+deltaNew);
0820: // System.out.println("stateOld="+getAttributes(cell));
0821: // Handle New Values
0822: Map deltaOld = null;
0823: AttributeMap attr = getAttributes(cell);
0824: if (attr != null) {
0825: deltaOld = attr.applyMap(deltaNew);
0826: // System.out.println("stateNew="+getAttributes(cell));
0827: // System.out.println("deltaOld="+deltaOld);
0828: undo.put(cell, deltaOld);
0829: } else {
0830: // Make room for the value
0831: deltaOld = new Hashtable(2);
0832: }
0833: // Handle new values
0834: Object newValue = deltaNew.get(GraphConstants.VALUE);
0835: if (newValue != null) {
0836: Object oldValue = valueForCellChanged(cell,
0837: newValue);
0838: if (oldValue != null)
0839: GraphConstants.setValue(deltaOld, oldValue);
0840: // TODO: Userobject of null is probably invalid
0841: else
0842: GraphConstants.setRemoveAttributes(deltaOld,
0843: new Object[] { GraphConstants.VALUE });
0844: } else {
0845: // Special case to handle removal of value attribute
0846: Object[] remove = GraphConstants
0847: .getRemoveAttributes(deltaNew);
0848: if (remove != null && remove.length > 0) {
0849: for (int i = 0; i < remove.length; i++) {
0850: if (remove[i] == GraphConstants.VALUE) {
0851: Object oldValue = valueForCellChanged(
0852: cell, null);
0853: if (oldValue != null) {
0854: GraphConstants.setValue(deltaOld,
0855: oldValue);
0856: }
0857: }
0858: }
0859: }
0860: }
0861: }
0862: return undo;
0863: }
0864: return null;
0865: }
0866:
0867: /**
0868: * Applies the new value to the specified cell. Unfortunately for cloning
0869: * the user object you must still override the attribute map and provide a
0870: * custom cloneUserObject method. This is because the cloning of a cell is
0871: * local to the cell, which in turn has a reference to its attribute map.
0872: *
0873: * @param cell
0874: * @param newValue
0875: * @return the old value for the cell, if any
0876: */
0877: public Object valueForCellChanged(Object cell, Object newValue) {
0878: if (cell instanceof DefaultMutableTreeNode) {
0879: DefaultMutableTreeNode node = (DefaultMutableTreeNode) cell;
0880: Object oldValue = node.getUserObject();
0881: node.setUserObject(newValue);
0882: return oldValue;
0883: }
0884: return null;
0885: }
0886:
0887: //
0888: // Connection Set Handling
0889: //
0890:
0891: /**
0892: * Applies <code>connectionSet</code> to the model. Returns a connection
0893: * set that may be used to undo this change.
0894: */
0895: protected ConnectionSet handleConnectionSet(ConnectionSet cs) {
0896: if (cs != null) {
0897: ConnectionSet csundo = new ConnectionSet();
0898: Iterator it = cs.connections();
0899: while (it.hasNext()) {
0900: ConnectionSet.Connection c = (ConnectionSet.Connection) it
0901: .next();
0902: Object edge = c.getEdge();
0903: if (c.isSource())
0904: csundo.connect(edge, getSource(edge), true);
0905: else
0906: csundo.connect(edge, getTarget(edge), false);
0907: handleConnection(c, false);
0908: }
0909:
0910: // When removing edges it is possible that an edge is
0911: // removed in a later step which has been added in a
0912: // previous connection establishment (set semantic).
0913: // Therefore, we first need to remove all old connections
0914: // and then add all new connections in two steps.
0915: it = cs.connections();
0916: while (it.hasNext())
0917: handleConnection((ConnectionSet.Connection) it.next(),
0918: true);
0919: return csundo;
0920: }
0921: return null;
0922: }
0923:
0924: /**
0925: * Inserts the specified connection into the model.
0926: */
0927: protected void handleConnection(ConnectionSet.Connection c,
0928: boolean establish) {
0929: Object edge = c.getEdge();
0930: Object port = (establish) ? c.getPort()
0931: : (c.isSource()) ? getSource(edge) : getTarget(edge);
0932: connect(edge, port, c.isSource(), establish);
0933: }
0934:
0935: /**
0936: * Connects or disconnects the edge and port in this model based on
0937: * <code>remove</code>. Subclassers should override this to update
0938: * connectivity datastructures.
0939: */
0940: protected void connect(Object edge, Object port, boolean isSource,
0941: boolean insert) {
0942: if (port instanceof Port)
0943: if (insert)
0944: ((Port) port).addEdge(edge);
0945:
0946: // Only removes if opposite is not
0947: // connected to same port
0948: else if ((isSource) ? getTarget(edge) != port
0949: : getSource(edge) != port)
0950: ((Port) port).removeEdge(edge);
0951: if (!insert)
0952: port = null;
0953: if (edge instanceof Edge) {
0954: if (isSource)
0955: ((Edge) edge).setSource(port);
0956: else
0957: ((Edge) edge).setTarget(port);
0958: }
0959: }
0960:
0961: //
0962: // GraphModelListeners
0963: //
0964:
0965: /**
0966: * Adds a listener for the GraphModelEvent posted after the graph changes.
0967: *
0968: * @see #removeGraphModelListener
0969: * @param l
0970: * the listener to add
0971: */
0972: public void addGraphModelListener(GraphModelListener l) {
0973: listenerList.add(GraphModelListener.class, l);
0974: }
0975:
0976: /**
0977: * Removes a listener previously added with <B>addGraphModelListener() </B>.
0978: *
0979: * @see #addGraphModelListener
0980: * @param l
0981: * the listener to remove
0982: */
0983: public void removeGraphModelListener(GraphModelListener l) {
0984: listenerList.remove(GraphModelListener.class, l);
0985: }
0986:
0987: /**
0988: * Invoke this method after you've changed how the cells are to be
0989: * represented in the graph.
0990: */
0991: public void cellsChanged(final Object[] cells) {
0992: if (cells != null) {
0993: fireGraphChanged(this ,
0994: new GraphModelEvent.GraphModelChange() {
0995:
0996: public Object[] getInserted() {
0997: return null;
0998: }
0999:
1000: public Object[] getRemoved() {
1001: return null;
1002: }
1003:
1004: public Map getPreviousAttributes() {
1005: return null;
1006: }
1007:
1008: public ConnectionSet getConnectionSet() {
1009: return null;
1010: }
1011:
1012: public ConnectionSet getPreviousConnectionSet() {
1013: return null;
1014: }
1015:
1016: public ParentMap getParentMap() {
1017: return null;
1018: }
1019:
1020: public ParentMap getPreviousParentMap() {
1021: return null;
1022: }
1023:
1024: public void putViews(GraphLayoutCache view,
1025: CellView[] cellViews) {
1026: }
1027:
1028: public CellView[] getViews(GraphLayoutCache view) {
1029: return null;
1030: }
1031:
1032: public Object getSource() {
1033: return this ;
1034: }
1035:
1036: public Object[] getChanged() {
1037: return cells;
1038: }
1039:
1040: public Map getAttributes() {
1041: return null;
1042: }
1043:
1044: public Object[] getContext() {
1045: return null;
1046: }
1047:
1048: });
1049: }
1050: }
1051:
1052: /*
1053: * Notify all listeners that have registered interest for notification on
1054: * this event type. The event instance is lazily created using the
1055: * parameters passed into the fire method.
1056: *
1057: * @see EventListenerList
1058: */
1059: protected void fireGraphChanged(Object source,
1060: GraphModelEvent.GraphModelChange edit) {
1061: // Guaranteed to return a non-null array
1062: Object[] listeners = listenerList.getListenerList();
1063: GraphModelEvent e = null;
1064: // Process the listeners last to first, notifying
1065: // those that are interested in this event
1066: for (int i = listeners.length - 2; i >= 0; i -= 2) {
1067: if (listeners[i] == GraphModelListener.class) {
1068: // Lazily create the event:
1069: if (e == null)
1070: e = new GraphModelEvent(source, edit);
1071: ((GraphModelListener) listeners[i + 1]).graphChanged(e);
1072: }
1073: }
1074: }
1075:
1076: /**
1077: * Return an array of all GraphModelListeners that were added to this model.
1078: */
1079: public GraphModelListener[] getGraphModelListeners() {
1080: return (GraphModelListener[]) listenerList
1081: .getListeners(GraphModelListener.class);
1082: }
1083:
1084: //
1085: // GraphModelEdit
1086: //
1087:
1088: /**
1089: * An implementation of GraphModelChange that can be added to the model
1090: * event.
1091: */
1092: public class GraphModelEdit extends CompoundEdit implements
1093: GraphModelEvent.GraphModelChange {
1094:
1095: /* Cells that were inserted/removed/changed during the last execution. */
1096: protected Object[] insert, changed, remove, context;
1097:
1098: /* Cells that were inserted/removed/changed during the last execution. */
1099: protected Object[] inserted, removed;
1100:
1101: /*
1102: * Property map for the next execution. Attribute Map is passed to the
1103: * views on inserts.
1104: */
1105: protected Map attributes, previousAttributes;
1106:
1107: /* Parent map for the next execution. */
1108: protected ParentMap parentMap, previousParentMap;
1109:
1110: /* ConnectionSet for the next execution. */
1111: protected ConnectionSet connectionSet, previousConnectionSet;
1112:
1113: /* Piggybacked undo from the views. */
1114: protected Map cellViews = new Hashtable();
1115:
1116: /**
1117: * Constructs an edit record.
1118: *
1119: * @param inserted
1120: * a set of roots that were inserted
1121: * @param removed
1122: * a set of elements that were removed
1123: * @param attributes
1124: * the attribute changes made by the edit
1125: * @param connectionSet
1126: * the set of changed connections
1127: * @param parentMap
1128: * the map of changed parents
1129: */
1130: public GraphModelEdit(Object[] inserted, Object[] removed,
1131: Map attributes, ConnectionSet connectionSet,
1132: ParentMap parentMap) {
1133: super ();
1134: this .insert = inserted;
1135: this .remove = removed;
1136: this .connectionSet = connectionSet;
1137: this .attributes = attributes;
1138: this .parentMap = parentMap;
1139: previousAttributes = null;
1140: previousConnectionSet = connectionSet;
1141: previousParentMap = parentMap;
1142: // Remove Empty Parents
1143: if (parentMap != null) {
1144: // Compute Empty Group
1145: Map childCount = new Hashtable();
1146: Iterator it = parentMap.entries();
1147: while (it.hasNext()) {
1148: ParentMap.Entry entry = (ParentMap.Entry) it.next();
1149: Object child = entry.getChild();
1150: if (!isPort(child)) {
1151: Object oldParent = getParent(child);
1152: Object newParent = entry.getParent();
1153: if (oldParent != newParent) {
1154: changeChildCount(childCount, oldParent, -1);
1155: changeChildCount(childCount, newParent, 1);
1156: }
1157: }
1158: }
1159: handleEmptyGroups(filterParents(childCount, 0));
1160: }
1161: }
1162:
1163: public Object[] filterParents(Map childCount, int children) {
1164: ArrayList list = new ArrayList();
1165: Iterator it = childCount.entrySet().iterator();
1166: while (it.hasNext()) {
1167: Map.Entry entry = (Map.Entry) it.next();
1168: if (entry.getValue() instanceof Integer) {
1169: if (((Integer) entry.getValue()).intValue() == children)
1170: list.add(entry.getKey());
1171: }
1172: }
1173: return list.toArray();
1174: }
1175:
1176: protected void changeChildCount(Map childCount, Object parent,
1177: int change) {
1178: if (parent != null) {
1179: Integer count = (Integer) childCount.get(parent);
1180: if (count == null) {
1181: count = new Integer(getChildCount(parent));
1182: }
1183: int newValue = count.intValue() + change;
1184: childCount.put(parent, new Integer(newValue));
1185: }
1186: }
1187:
1188: /**
1189: * Adds the groups that become empty to the cells that will be removed.
1190: * (Auto remove empty cells.) Removed cells will be re-inserted on undo,
1191: * and the parent- child relations will be restored.
1192: */
1193: protected void handleEmptyGroups(Object[] groups) {
1194: if (removeEmptyGroups) {
1195: if (groups != null && groups.length > 0) {
1196: if (remove == null)
1197: remove = new Object[] {};
1198: Object[] tmp = new Object[remove.length
1199: + groups.length];
1200: System.arraycopy(remove, 0, tmp, 0, remove.length);
1201: System.arraycopy(groups, 0, tmp, remove.length,
1202: groups.length);
1203: remove = tmp;
1204: }
1205: }
1206: }
1207:
1208: public boolean isSignificant() {
1209: return true;
1210: }
1211:
1212: /**
1213: * Returns the source of this change. This can either be a view or a
1214: * model, if this change is a GraphModelChange.
1215: */
1216: public Object getSource() {
1217: return DefaultGraphModel.this ;
1218: }
1219:
1220: /**
1221: * Returns the cells that have changed. This includes the cells that
1222: * have been changed through a call to getAttributes and the edges that
1223: * have been changed with the ConnectionSet.
1224: */
1225: public Object[] getChanged() {
1226: return changed;
1227: }
1228:
1229: /**
1230: * Returns the objects that have not changed explicitly, but implicitly
1231: * because one of their dependent cells has changed.
1232: */
1233: public Object[] getContext() {
1234: return context;
1235: }
1236:
1237: /**
1238: * Returns the cells that were inserted.
1239: */
1240: public Object[] getInserted() {
1241: return inserted;
1242: }
1243:
1244: /**
1245: * Returns the cells that were inserted.
1246: */
1247: public Object[] getRemoved() {
1248: return removed;
1249: }
1250:
1251: /**
1252: * Returns a map that contains (object, map) pairs of the attributes
1253: * that have been stored in the model.
1254: */
1255: public Map getPreviousAttributes() {
1256: return previousAttributes;
1257: }
1258:
1259: /**
1260: * Returns a map of (object, view attributes). The objects are model
1261: * objects which need to be mapped to views.
1262: */
1263: public Map getAttributes() {
1264: return attributes;
1265: }
1266:
1267: /**
1268: * Returns the connectionSet.
1269: *
1270: * @return ConnectionSet
1271: */
1272: public ConnectionSet getConnectionSet() {
1273: return connectionSet;
1274: }
1275:
1276: public ConnectionSet getPreviousConnectionSet() {
1277: return previousConnectionSet;
1278: }
1279:
1280: /**
1281: * Returns the parentMap.
1282: *
1283: * @return ParentMap
1284: */
1285: public ParentMap getParentMap() {
1286: return parentMap;
1287: }
1288:
1289: public ParentMap getPreviousParentMap() {
1290: return previousParentMap;
1291: }
1292:
1293: /**
1294: * Redoes a change.
1295: *
1296: * @exception CannotRedoException
1297: * if the change cannot be redone
1298: */
1299: public void redo() throws CannotRedoException {
1300: super .redo();
1301: execute();
1302: }
1303:
1304: /**
1305: * Undoes a change.
1306: *
1307: * @exception CannotUndoException
1308: * if the change cannot be undone
1309: */
1310: public void undo() throws CannotUndoException {
1311: super .undo();
1312: execute();
1313: }
1314:
1315: /**
1316: * Execute this edit such that the next invocation to this method will
1317: * invert the last execution.
1318: */
1319: public void execute() {
1320: // Compute Changed Cells
1321: Set tmp = new HashSet();
1322: if (attributes != null)
1323: tmp.addAll(attributes.keySet());
1324: if (parentMap != null)
1325: tmp.addAll(parentMap.getChangedNodes());
1326: // Note: One must also include the previous parents!
1327: if (connectionSet != null)
1328: tmp.addAll(connectionSet.getChangedEdges());
1329: if (remove != null) {
1330: for (int i = 0; i < remove.length; i++)
1331: tmp.remove(remove[i]);
1332: }
1333: changed = tmp.toArray();
1334: // Context cells
1335: Set ctx = getEdges(DefaultGraphModel.this , changed);
1336: context = ctx.toArray();
1337: // Do Execute
1338: inserted = insert;
1339: removed = remove;
1340: remove = handleInsert(inserted);
1341: previousParentMap = parentMap;
1342: parentMap = handleParentMap(parentMap);
1343: // Adds previous parents
1344: if (parentMap != null)
1345: tmp.addAll(parentMap.getChangedNodes());
1346: previousConnectionSet = connectionSet;
1347: connectionSet = handleConnectionSet(connectionSet);
1348: insert = handleRemove(removed);
1349: previousAttributes = attributes;
1350: attributes = handleAttributes(attributes);
1351: changed = tmp.toArray();
1352: // Fire Event
1353: fireGraphChanged(DefaultGraphModel.this , this );
1354: }
1355:
1356: public void putViews(GraphLayoutCache view, CellView[] views) {
1357: if (view != null && views != null)
1358: cellViews.put(view, views);
1359: }
1360:
1361: public CellView[] getViews(GraphLayoutCache view) {
1362: return (CellView[]) cellViews.get(view);
1363: }
1364:
1365: public String toString() {
1366: String s = new String();
1367: if (inserted != null) {
1368: s += "Inserted:\n";
1369: for (int i = 0; i < inserted.length; i++)
1370: s += " " + inserted[i] + "\n";
1371: } else
1372: s += "None inserted\n";
1373: if (removed != null) {
1374: s += "Removed:\n";
1375: for (int i = 0; i < removed.length; i++)
1376: s += " " + removed[i] + "\n";
1377: } else
1378: s += "None removed\n";
1379: if (changed != null && changed.length > 0) {
1380: s += "Changed:\n";
1381: for (int i = 0; i < changed.length; i++)
1382: s += " " + changed[i] + "\n";
1383: } else
1384: s += "None changed\n";
1385: if (parentMap != null)
1386: s += parentMap.toString();
1387: else
1388: s += "No parent map\n";
1389: return s;
1390: }
1391:
1392: }
1393:
1394: /**
1395: * An implementation of GraphViewChange.
1396: */
1397: public class GraphModelLayerEdit extends AbstractUndoableEdit
1398: implements GraphModelEvent.GraphModelChange {
1399:
1400: public static final int FRONT = -1, BACK = -2;
1401:
1402: protected Object changeSource;
1403:
1404: protected transient Object[] cells;
1405:
1406: protected transient int[] next, prev;
1407:
1408: protected int layer;
1409:
1410: // The cell that change are the parents, because they need to
1411: // reload their childs for reordering!
1412: protected Object[] changed;
1413:
1414: /**
1415: * Constructs a GraphModelEdit. This modifies the order of the cells in
1416: * the model.
1417: */
1418: public GraphModelLayerEdit(Object[] cells, int layer) {
1419: this .cells = cells;
1420: this .layer = layer;
1421: next = new int[cells.length];
1422: prev = new int[cells.length];
1423: updateNext();
1424: // Compute array of changed cells (roots or parents of cells)
1425: Set par = new HashSet();
1426: for (int i = 0; i < cells.length; i++) {
1427: Object cell = DefaultGraphModel.this
1428: .getParent(cells[i]);
1429: if (cell == null)
1430: cell = cells[i];
1431: par.add(cell);
1432: }
1433: changed = par.toArray();
1434: }
1435:
1436: protected void updateNext() {
1437: for (int i = 0; i < next.length; i++)
1438: next[i] = layer;
1439: }
1440:
1441: /**
1442: * Returns the source of this change. This can either be a view or a
1443: * model, if this change is a GraphModelChange.
1444: */
1445: public Object getSource() {
1446: return DefaultGraphModel.this ;
1447: }
1448:
1449: /**
1450: * Returns the cells that have changed.
1451: */
1452: public Object[] getChanged() {
1453: return changed;
1454: }
1455:
1456: /**
1457: * Returns the cells that have changed.
1458: */
1459: public Object[] getInserted() {
1460: return null;
1461: }
1462:
1463: /**
1464: * Returns the cells that have changed.
1465: */
1466: public Object[] getRemoved() {
1467: return null;
1468: }
1469:
1470: /**
1471: * Returns null.
1472: */
1473: public Object[] getContext() {
1474: return null;
1475: }
1476:
1477: /**
1478: * Returns null.
1479: */
1480: public Map getAttributes() {
1481: return null;
1482: }
1483:
1484: /**
1485: * Returns null.
1486: */
1487: public Map getPreviousAttributes() {
1488: return null;
1489: }
1490:
1491: public ConnectionSet getConnectionSet() {
1492: return null;
1493: }
1494:
1495: public ConnectionSet getPreviousConnectionSet() {
1496: return null;
1497: }
1498:
1499: /**
1500: * Returns null.
1501: */
1502: public ParentMap getParentMap() {
1503: return null;
1504: }
1505:
1506: public ParentMap getPreviousParentMap() {
1507: return null;
1508: }
1509:
1510: /**
1511: * Allows a <code>GraphLayoutCache</code> to add and execute and
1512: * UndoableEdit in this change. This does also work if the parent edit
1513: * has already been executed, in which case the to be added edit will be
1514: * executed immediately, after addition. This is used to handle changes
1515: * to the view that are triggered by certain changes of the model. Such
1516: * implicit edits may be associated with the view so that they may be
1517: * undone and redone correctly, and are stored in the model's global
1518: * history together with the parent event as one unit.
1519: */
1520: public void addImplicitEdit(UndoableEdit edit) {
1521: // ignore
1522: }
1523:
1524: /**
1525: * Returns the views that have not changed explicitly, but implicitly
1526: * because one of their dependent cells has changed.
1527: */
1528: public CellView[] getViews(GraphLayoutCache view) {
1529: return null;
1530: }
1531:
1532: /**
1533: * Returns the views that have not changed explicitly, but implicitly
1534: * because one of their dependent cells has changed.
1535: */
1536: public void putViews(GraphLayoutCache view, CellView[] cellViews) {
1537: // ignore
1538: }
1539:
1540: /**
1541: * Redoes a change.
1542: *
1543: * @exception CannotRedoException
1544: * if the change cannot be redone
1545: */
1546: public void redo() throws CannotRedoException {
1547: super .redo();
1548: updateNext();
1549: execute();
1550: }
1551:
1552: /**
1553: * Undoes a change.
1554: *
1555: * @exception CannotUndoException
1556: * if the change cannot be undone
1557: */
1558: public void undo() throws CannotUndoException {
1559: super .undo();
1560: execute();
1561: }
1562:
1563: /**
1564: * Execute this edit such that the next invocation to this method will
1565: * invert the last execution.
1566: */
1567: public void execute() {
1568: for (int i = 0; i < cells.length; i++) {
1569: List list = getParentList(cells[i]);
1570: if (list != null) {
1571: prev[i] = list.indexOf(cells[i]);
1572: if (prev[i] >= 0) {
1573: list.remove(prev[i]);
1574: int n = next[i];
1575: if (n == FRONT)
1576: n = list.size();
1577: else if (n == BACK)
1578: n = 0;
1579: list.add(n, cells[i]);
1580: next[i] = prev[i];
1581: }
1582: }
1583: }
1584: updateListeners();
1585: }
1586:
1587: protected void updateListeners() {
1588: fireGraphChanged(DefaultGraphModel.this , this );
1589: }
1590:
1591: /**
1592: * Returns the list that exclusively contains <code>view</code>.
1593: */
1594: protected List getParentList(Object cell) {
1595: List list = null;
1596: if (cell instanceof DefaultMutableTreeNode) {
1597: Object parent = ((DefaultMutableTreeNode) cell)
1598: .getParent();
1599: if (parent instanceof DefaultGraphCell)
1600: list = ((DefaultGraphCell) parent).getChildren();
1601: else
1602: list = roots;
1603: }
1604: return list;
1605: }
1606:
1607: }
1608:
1609: //
1610: // Static Methods
1611: //
1612:
1613: /**
1614: * Returns a deep clone of the specified cell, including all children.
1615: */
1616: public static Object cloneCell(GraphModel model, Object cell) {
1617: Map clones = model.cloneCells(getDescendants(model,
1618: new Object[] { cell }).toArray());
1619: return clones.get(cell);
1620: }
1621:
1622: /**
1623: * Returns a deep clone of the specified cells, including all children.
1624: */
1625: public static Object[] cloneCell(GraphModel model, Object[] cells) {
1626: Map clones = model.cloneCells(getDescendants(model, cells)
1627: .toArray());
1628: for (int i = 0; i < cells.length; i++) {
1629: cells[i] = clones.get(cells[i]);
1630: }
1631: return cells;
1632: }
1633:
1634: /**
1635: * Helper methods that connects the source of <code>edge</code> to
1636: * <code>port</code> in <code>model</model>.
1637: */
1638: public static void setSourcePort(GraphModel model, Object edge,
1639: Object port) {
1640: model.edit(null, new ConnectionSet(edge, port, true), null,
1641: null);
1642: }
1643:
1644: /**
1645: * Helper methods that connects the source of <code>edge</code> to
1646: * <code>port</code> in <code>model</model>.
1647: */
1648: public static void setTargetPort(GraphModel model, Object edge,
1649: Object port) {
1650: model.edit(null, new ConnectionSet(edge, port, false), null,
1651: null);
1652: }
1653:
1654: /**
1655: * Returns the source vertex of the edge by calling getParent on getSource
1656: * on the specified model.
1657: */
1658: public static Object getSourceVertex(GraphModel model, Object edge) {
1659: if (model != null)
1660: return model.getParent(model.getSource(edge));
1661: return null;
1662: }
1663:
1664: /**
1665: * Returns the target vertex of the edge by calling getParent on getTarget
1666: * on the specified model.
1667: */
1668: public static Object getTargetVertex(GraphModel model, Object edge) {
1669: if (model != null)
1670: return model.getParent(model.getTarget(edge));
1671: return null;
1672: }
1673:
1674: /**
1675: * @return Returns the user object of the given cell. This implementation
1676: * checks if the cell is a default mutable tree node and returns
1677: * it's user object.
1678: *
1679: * @deprecated Use {@link GraphModel#getValue(Object)} instead.
1680: */
1681: public static Object getUserObject(Object cell) {
1682: if (cell instanceof DefaultMutableTreeNode)
1683: return ((DefaultMutableTreeNode) cell).getUserObject();
1684: return null;
1685: }
1686:
1687: /**
1688: * Checks whether the cell has at least one child which is not a port. This
1689: * implementation operates on the model, not taking into account visibility
1690: * of cells. It returns true for groups regardless of their folded state.
1691: *
1692: * @param cell
1693: * the cell to check for being a group
1694: * @return Returns true if the cell contains at least one cell which is not
1695: * a port
1696: */
1697: public static boolean isGroup(GraphModel model, Object cell) {
1698: for (int i = 0; i < model.getChildCount(cell); i++) {
1699: if (!model.isPort(model.getChild(cell, i)))
1700: return true;
1701: }
1702: return false;
1703: }
1704:
1705: /**
1706: * Returns all cells of the model in an array.
1707: *
1708: * @see #getDescendants(GraphModel, Object[])
1709: *
1710: * @return Returns all cells in the model including all descandants.
1711: */
1712: public static Object[] getAll(GraphModel model) {
1713: return getDescendants(model, getRoots(model)).toArray();
1714: }
1715:
1716: /**
1717: * Returns the roots of the specified model as an array. This implementation
1718: * uses the GraphModel interface in the general case, but if the model is a
1719: * <code>DefaultGraphModel</code> the performance can be improved to
1720: * linear time.
1721: */
1722: public static Object[] getRoots(GraphModel model) {
1723: Object[] cells = null;
1724: if (model != null) {
1725: // If model is DefaultGraphModel, we can do a linear time getRoots
1726: if (model instanceof DefaultGraphModel) {
1727: cells = ((DefaultGraphModel) model).getRoots()
1728: .toArray();
1729: } else {
1730: cells = new Object[model.getRootCount()];
1731: for (int i = 0; i < cells.length; i++) {
1732: cells[i] = model.getRootAt(i);
1733: }
1734: }
1735: }
1736: return cells;
1737: }
1738:
1739: /**
1740: * Returns the roots of the specified model as a collection. This implementation
1741: * uses the GraphModel interface in the general case, but if the model is a
1742: * <code>DefaultGraphModel</code> the performance can be improved to
1743: * linear time.
1744: */
1745: public static Collection getRootsAsCollection(GraphModel model) {
1746: Collection cells = null;
1747: if (model != null) {
1748: // If model is DefaultGraphModel, we can do a linear time getRoots
1749: if (model instanceof DefaultGraphModel) {
1750: cells = ((DefaultGraphModel) model).getRoots();
1751: } else {
1752: cells = new LinkedHashSet(model.getRootCount());
1753: for (int i = 0; i < cells.size(); i++) {
1754: cells.add(model.getRootAt(i));
1755: }
1756: }
1757: }
1758: return cells;
1759: }
1760:
1761: /**
1762: * Returns the roots in <code>cells</code> by checking if their parent is
1763: * <code>null</code>. This implementation only uses the GraphModel
1764: * interface. This method never returns null.
1765: */
1766: public static Object[] getRoots(GraphModel model, Object[] cells) {
1767: List roots = new ArrayList();
1768: if (cells != null) {
1769: for (int i = 0; i < cells.length; i++) {
1770: if (model.getParent(cells[i]) == null) {
1771: roots.add(cells[i]);
1772: }
1773: }
1774: }
1775: return roots.toArray();
1776: }
1777:
1778: /**
1779: * @return Returns the roots of cells, eg. an array that contains no cell
1780: * having an ancestor in cells.
1781: */
1782: public static Object[] getTopmostCells(GraphModel model,
1783: Object[] cells) {
1784: Set cellSet = new HashSet();
1785: for (int i = 0; i < cells.length; i++)
1786: cellSet.add(cells[i]);
1787: List parents = new ArrayList();
1788: for (int i = 0; i < cells.length; i++) {
1789: if (!hasAncestorIn(model, cellSet, cells[i]))
1790: parents.add(cells[i]);
1791: }
1792: return parents.toArray();
1793: }
1794:
1795: /**
1796: * Returns true if the specified child has an ancestor in parents.
1797: */
1798: public static boolean hasAncestorIn(GraphModel model, Set parents,
1799: Object child) {
1800: Object parent = model.getParent(child);
1801: while (parent != null) {
1802: if (parents.contains(parent))
1803: return true;
1804: parent = model.getParent(parent);
1805: }
1806: return false;
1807: }
1808:
1809: /**
1810: * Flattens the given array of root cells by adding the roots and their
1811: * descandants. The resulting set contains all cells, which means it
1812: * contains branches <strong>and </strong> leafs. Note: This is an iterative
1813: * implementation. No recursion used. <br>
1814: * Note: This returns a linked list, for frequent read operations you should
1815: * turn this into an array, or at least an array list.
1816: */
1817: public static List getDescendants(GraphModel model, Object[] cells) {
1818: if (cells != null) {
1819: Stack stack = new Stack();
1820: for (int i = cells.length - 1; i >= 0; i--)
1821: stack.add(cells[i]);
1822: LinkedList result = new LinkedList();
1823: while (!stack.isEmpty()) {
1824: Object tmp = stack.pop();
1825: for (int i = model.getChildCount(tmp) - 1; i >= 0; i--)
1826: stack.add(model.getChild(tmp, i));
1827: if (tmp != null)
1828: result.add(tmp);
1829: }
1830: return result;
1831: }
1832: return null;
1833: }
1834:
1835: /**
1836: * Orders cells so that they reflect the model order.
1837: */
1838: public static Object[] order(GraphModel model, Object[] cells) {
1839: if (cells != null) {
1840: Set cellSet = new HashSet();
1841: for (int i = 0; i < cells.length; i++)
1842: cellSet.add(cells[i]);
1843: Stack stack = new Stack();
1844: for (int i = model.getRootCount() - 1; i >= 0; i--)
1845: stack.add(model.getRootAt(i));
1846: LinkedList result = new LinkedList();
1847: while (!stack.isEmpty()) {
1848: Object tmp = stack.pop();
1849: for (int i = model.getChildCount(tmp) - 1; i >= 0; i--)
1850: stack.add(model.getChild(tmp, i));
1851: if (cellSet.remove(tmp))
1852: result.add(tmp);
1853: }
1854: return result.toArray();
1855: }
1856: return null;
1857: }
1858:
1859: /**
1860: * Returns the set of all connected edges to <code>cells</code> or their
1861: * descendants. The passed-in cells are never returned as part of the result
1862: * set. This can be used on vertices, edges and ports.
1863: */
1864: public static Set getEdges(GraphModel model, Object[] cells) {
1865: Set result = new HashSet();
1866: if (cells != null) {
1867: // We know the minimum initial capacity of this set is cells.length
1868: // We assume the cell has one port at a minimum
1869: int setSize = ((int) (cells.length * 1.33) + 1);
1870: Set allCells = new HashSet(setSize, 0.75f);
1871: for (int i = 0; i < cells.length; i++) {
1872: allCells.add(cells[i]);
1873: }
1874: // Include descendants
1875: allCells.addAll(getDescendants(model, cells));
1876: if (allCells != null) {
1877: Iterator it = allCells.iterator();
1878: while (it.hasNext()) {
1879: Iterator edges = model.edges(it.next());
1880: while (edges.hasNext())
1881: result.add(edges.next());
1882: }
1883: for (int i = 0; i < cells.length; i++)
1884: result.remove(cells[i]);
1885: }
1886: }
1887: return result;
1888: }
1889:
1890: /**
1891: * @return Returns the opposite port or vertex in <code>edge</code>.
1892: */
1893: public static Object getOpposite(GraphModel model, Object edge,
1894: Object cell) {
1895: boolean isPort = model.isPort(cell);
1896: Object source = (isPort) ? model.getSource(edge)
1897: : getSourceVertex(model, edge);
1898: if (cell == source)
1899: return (isPort) ? model.getTarget(edge) : getTargetVertex(
1900: model, edge);
1901: else
1902: return source;
1903: }
1904:
1905: /**
1906: * Returns true if the given vertices are conntected by a single edge in
1907: * this document.
1908: */
1909: public static boolean containsEdgeBetween(GraphModel model,
1910: Object v1, Object v2) {
1911: Object[] edges = getEdgesBetween(model, v1, v2, false);
1912: return (edges != null && edges.length > 0);
1913: }
1914:
1915: /**
1916: * Returns the edges between two specified ports or two specified vertices.
1917: * If directed is true then <code>cell1</code> must be the source of the
1918: * returned edges. This method never returns null. If there are no edges
1919: * between the specified cells, then an array of length 0 is returned.
1920: */
1921: public static Object[] getEdgesBetween(GraphModel model,
1922: Object cell1, Object cell2, boolean directed) {
1923: boolean isPort1 = model.isPort(cell1);
1924: boolean isPort2 = model.isPort(cell2);
1925: ArrayList result = new ArrayList();
1926: Set edges = DefaultGraphModel.getEdges(model,
1927: new Object[] { cell1 });
1928: Iterator it = edges.iterator();
1929: while (it.hasNext()) {
1930: Object edge = it.next();
1931: // TODO: Handle edge groups
1932: Object source = (isPort1) ? model.getSource(edge)
1933: : getSourceVertex(model, edge);
1934: Object target = (isPort2) ? model.getTarget(edge)
1935: : getTargetVertex(model, edge);
1936: if ((source == cell1 && target == cell2)
1937: || (!directed && source == cell2 && target == cell1))
1938: result.add(edge);
1939: }
1940: return result.toArray();
1941: }
1942:
1943: /**
1944: * Returns the outgoing edges for cell. Cell should be a port or a vertex.
1945: */
1946: public static Object[] getOutgoingEdges(GraphModel model,
1947: Object cell) {
1948: return getEdges(model, cell, false);
1949: }
1950:
1951: /**
1952: * Returns the incoming edges for cell. Cell should be a port or a vertex.
1953: */
1954: public static Object[] getIncomingEdges(GraphModel model,
1955: Object cell) {
1956: return getEdges(model, cell, true);
1957: }
1958:
1959: /**
1960: * Returns the incoming or outgoing edges for cell. Cell should be a port or
1961: * a vertex.
1962: */
1963: public static Object[] getEdges(GraphModel model, Object cell,
1964: boolean incoming) {
1965: Set edges = DefaultGraphModel.getEdges(model,
1966: new Object[] { cell });
1967: // Base initial capacity on size of set, it can't be any larger
1968: ArrayList result = new ArrayList(edges.size());
1969: Iterator it = edges.iterator();
1970:
1971: while (it.hasNext()) {
1972: Object edge = it.next();
1973: // TODO: Handle edge groups
1974: Object port = (incoming) ? model.getTarget(edge) : model
1975: .getSource(edge);
1976: Object parent = model.getParent(port);
1977: if (port == cell || parent == cell)
1978: result.add(edge);
1979: }
1980: return result.toArray();
1981: }
1982:
1983: /**
1984: * Returns <code>true</code> if <code>vertex</code> is a valid vertex.
1985: *
1986: * @return <code>true</code> if <code>vertex</code> is a valid vertex.
1987: */
1988: public static boolean isVertex(GraphModel model, Object vertex) {
1989: return (vertex != null && !model.isEdge(vertex) && !model
1990: .isPort(vertex));
1991: }
1992:
1993: // Serialization support
1994: private void readObject(ObjectInputStream s) throws IOException,
1995: ClassNotFoundException {
1996: s.defaultReadObject();
1997: listenerList = new EventListenerList();
1998: emptyIterator = new EmptyIterator();
1999: }
2000:
2001: public static class EmptyIterator implements Iterator, Serializable {
2002:
2003: public boolean hasNext() {
2004: return false;
2005: }
2006:
2007: public Object next() {
2008: return null;
2009: }
2010:
2011: public void remove() {
2012: // nop
2013: }
2014: }
2015:
2016: /**
2017: * @return the removeEmptyGroups
2018: */
2019: public boolean isRemoveEmptyGroups() {
2020: return removeEmptyGroups;
2021: }
2022:
2023: /**
2024: * @param removeEmptyGroups the removeEmptyGroups to set
2025: */
2026: public void setRemoveEmptyGroups(boolean removeEmptyGroups) {
2027: this.removeEmptyGroups = removeEmptyGroups;
2028: }
2029:
2030: }
|