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