KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > enhydra > jawe > JaWEGraphModel


1 /* PEGraphModel.java
2  *
3  * Authors:
4  * Stefanovic Nenad chupo@iis.ns.ac.yu
5  * Bojanic Sasa sasaboy@neobee.net
6  * Puskas Vladimir vpuskas@eunet.yu
7  * Pilipovic Goran zboniek@uns.ac.yu
8  *
9  */

10
11 package org.enhydra.jawe;
12
13 import org.enhydra.jawe.graph.*;
14
15 import org.jgraph.JGraph;
16 import org.jgraph.graph.*;
17 import org.jgraph.event.*;
18
19 import java.io.*;
20 import java.util.*;
21 import javax.swing.undo.*;
22 import javax.swing.tree.*;
23 import java.awt.Rectangle JavaDoc;
24 import javax.swing.event.*;
25
26 /**
27  * A process editor implementation of a graph model.
28  */

29 public class JaWEGraphModel
30    extends UndoableEditSupport
31    implements Serializable, GraphModel {
32
33    /** The list of listeners that listen to the model. */
34    protected transient EventListenerList listenerList =
35       new EventListenerList();
36
37    /** Default instance of an empty iterator. */
38    protected transient Iterator emptyIterator = new EmptyIterator();
39
40    /** Set that contains all root cells of this model. */
41    protected List roots = new ArrayList();
42
43    /** Indicates whether isLeaf is based on a node's allowsChildren value. */
44    protected boolean asksAllowsChildren = false;
45
46    /**
47     * Constructs a model that is not an attribute store.
48     */

49    public JaWEGraphModel() {
50    }
51
52    //
53
// Graph Model
54
//
55

56    /**
57     * Returns the number of roots in the model. Returns 0 if the
58     * model is empty.
59     *
60     * @return the number of roots in the model
61     */

62    public int getRootCount() {
63       return roots.size();
64    }
65
66    /**
67     * Returns the root at index <I>index</I> in the model.
68     * This should not return null if <i>index</i> is a valid
69     * index for the model (that is <i>index</i> >= 0 &&
70     * <i>index</i> < getRootCount()).
71     *
72     * @return the root of at index <I>index</I>
73     */

74    public Object JavaDoc getRootAt(int index) {
75       return roots.get(index);
76    }
77
78    /**
79     * Returns the index of <code>root</code> in the model.
80     * If root is <code>null</code>, returns -1.
81     * @param root a root in the model, obtained from this data source
82     * @return the index of the root in the model, or -1
83     * if the parent is <code>null</code>
84     */

85    public int getIndexOfRoot(Object JavaDoc root) {
86       return roots.indexOf(root);
87    }
88
89    /**
90     * Returns <code>true</code> if <code>node</code> or one of its
91     * ancestors is in the model.
92     *
93     * @return <code>true</code> if <code>node</code> is in the model
94     */

95    public boolean contains(Object JavaDoc node) {
96       Object JavaDoc parentNode = null;
97       while ((parentNode = getParent(node))
98              != null)
99          node = parentNode;
100       return roots.contains(node);
101    }
102
103    /**
104     * Returns a <code>Map</code> that represents the attributes for
105     * the specified cell. This attributes have precedence over each
106     * view's attributes, regardless of isAttributeStore.
107     *
108     * @return attributes of <code>node</code> as a <code>Map</code>
109     */

110    public Map getAttributes(Object JavaDoc node) {
111       if (node instanceof GraphCell)
112          return ((GraphCell) node).getAttributes();
113       return null;
114    }
115
116    //
117
// Graph Structure
118
//
119

120    /**
121     * Returns the source of <code>edge</code>. <I>edge</I> must be an object
122     * previously obtained from this data source.
123     *
124     * @return <code>Object</code> that represents the source of <i>edge</i>
125     */

126    public Object JavaDoc getSource(Object JavaDoc edge) {
127       if (edge instanceof Edge)
128          return ((Edge) edge).getSource();
129       return null;
130    }
131
132    /**
133     * Returns the target of <code>edge</code>. <I>edge</I> must be an object
134     * previously obtained from this data source.
135     *
136     * @return <code>Object</code> that represents the target of <i>edge</i>
137     */

138    public Object JavaDoc getTarget(Object JavaDoc edge) {
139       if (edge instanceof Edge)
140          return ((Edge) edge).getTarget();
141       return null;
142    }
143
144    /**
145     * Returns <code>true</code> if <code>port</code> is a valid source
146     * for <code>edge</code>. <I>edge</I> and <I>port</I> must be
147     * objects previously obtained from this data source.
148     *
149     * @return <code>true</code> if <code>port</code> is a valid source
150     * for <code>edge</code>.
151     */

152    public boolean acceptsSource(Object JavaDoc edge, Object JavaDoc port) {
153       return true;
154    }
155
156    /**
157     * Returns <code>true</code> if <code>port</code> is a valid target
158     * for <code>edge</code>. <I>edge</I> and <I>port</I> must be
159     * objects previously obtained from this data source.
160     *
161     * @return <code>true</code> if <code>port</code> is a valid target
162     * for <code>edge</code>.
163     */

164    public boolean acceptsTarget(Object JavaDoc edge, Object JavaDoc port) {
165       return true;
166    }
167
168    /**
169     * Returns an iterator of the edges connected to <code>port</code>.
170     * <I>port</I> must be a object previously obtained from
171     * this data source. This method never returns null.
172     *
173     * @param port a port in the graph, obtained from this data source
174     * @return <code>Iterator</code> that represents the connected edges
175     */

176    public Iterator edges(Object JavaDoc port) {
177       if (port instanceof Port)
178          return ((Port) port).edges();
179       return emptyIterator;
180    }
181
182    /**
183     * Returns <code>true</code> if <code>edge</code> is a valid edge.
184     *
185     * @return <code>true</code> if <code>edge</code> is a valid edge.
186     */

187    public boolean isEdge(Object JavaDoc edge) {
188       return edge instanceof Edge;
189    }
190
191    /**
192     * Returns <code>true</code> if <code>port</code> is a valid
193     * port, possibly supporting edge connection.
194     *
195     * @return <code>true</code> if <code>port</code> is a valid port.
196     */

197    public boolean isPort(Object JavaDoc port) {
198       return port instanceof Port;
199    }
200    //
201
// Group Structure
202
//
203

204    /**
205     * Returns a map of (cell, clone)-pairs for all <code>cells</code>
206     * and their children. Special care is taken to replace the anchor
207     * references between ports. (Iterative implementation.)
208     */

209    public Map cloneCells(Object JavaDoc[] cells) {
210       Map map = new Hashtable();
211       // Add Cells to Queue
212
ArrayList q = new ArrayList();
213       for (int i = 0; i < cells.length; i++)
214          q.add(cells[i]);
215       // Iterate Queue
216
while (!q.isEmpty()) {
217          // Remove Front Client From Queue
218
Object JavaDoc node = q.remove(0);
219          if (node instanceof DefaultGraphCell) {
220             // Enqueue Children
221
for (int i = 0; i < getChildCount(node); i++)
222                q.add(getChild(node, i));
223             // Re-Establish Parent Relation for Front Client
224
DefaultGraphCell cell = (DefaultGraphCell) node;
225             DefaultGraphCell clone = (DefaultGraphCell) cell.clone();
226             Object JavaDoc par = getParent(cell);
227             if (par != null) {
228                DefaultMutableTreeNode p =
229                   (DefaultMutableTreeNode) map.get(par);
230                if (p != null)
231                   p.add(clone);
232             }
233             // Store (cell, clone)-pair
234
map.put(cell, clone);
235          }
236       }
237       // Replace Anchors
238
Iterator it = map.values().iterator();
239       while (it.hasNext()) {
240          Object JavaDoc obj = it.next();
241          // For All Ports in Result Map do...
242
if (obj instanceof Port) {
243             Object JavaDoc anchor = ((Port) obj).getAnchor();
244             // Map Anchor to Cloned Anchor
245
if (anchor != null)
246                   ((Port) obj).setAnchor((Port) map.get(anchor));
247          }
248       }
249       return map;
250    }
251
252
253    /**
254     * Returns the parent of <I>child</I> in the model.
255     * <I>child</I> must be a node previously obtained from
256     * this data source. This returns null if <i>child</i> is
257     * a root in the model.
258     *
259     * @param child a node in the graph, obtained from this data source
260     * @return the parent of <I>child</I>
261     */

262    public Object JavaDoc getParent(Object JavaDoc child) {
263       if (child != null && child instanceof TreeNode)
264          return ((TreeNode) child).getParent();
265       return null;
266    }
267
268    /**
269     * Returns the index of child in parent.
270     * If either the parent or child is <code>null</code>, returns -1.
271     * @param parent a note in the tree, obtained from this data source
272     * @param child the node we are interested in
273     * @return the index of the child in the parent, or -1
274     * if either the parent or the child is <code>null</code>
275     */

276    public int getIndexOfChild(Object JavaDoc parent, Object JavaDoc child) {
277       if (parent == null || child == null)
278          return -1;
279       return ((TreeNode) parent).getIndex((TreeNode) child);
280    }
281
282    /**
283     * Returns the child of <I>parent</I> at index <I>index</I> in the parent's
284     * child array. <I>parent</I> must be a node previously obtained from
285     * this data source. This should not return null if <i>index</i>
286     * is a valid index for <i>parent</i> (that is <i>index</i> >= 0 &&
287     * <i>index</i> < getChildCount(<i>parent</i>)).
288     *
289     * @param parent a node in the tree, obtained from this data source
290     * @return the child of <I>parent</I> at index <I>index</I>
291     */

292    public Object JavaDoc getChild(Object JavaDoc parent, int index) {
293       if (parent instanceof TreeNode)
294          return ((TreeNode) parent).getChildAt(index);
295       return null;
296    }
297
298    /**
299     * Returns the number of children of <I>parent</I>. Returns 0 if the node
300     * is a leaf or if it has no children. <I>parent</I> must be a node
301     * previously obtained from this data source.
302     *
303     * @param parent a node in the tree, obtained from this data source
304     * @return the number of children of the node <I>parent</I>
305     */

306    public int getChildCount(Object JavaDoc parent) {
307       if (parent instanceof TreeNode)
308          return ((TreeNode) parent).getChildCount();
309       return 0;
310    }
311
312    /**
313     * Returns whether the specified node is a leaf node.
314     * The way the test is performed depends on the.
315     *
316     * @param node the node to check
317     * @return true if the node is a leaf node
318     */

319    public boolean isLeaf(Object JavaDoc node) {
320       if (asksAllowsChildren && node instanceof TreeNode)
321          return !((TreeNode) node).getAllowsChildren();
322       return ((TreeNode) node).isLeaf();
323    }
324
325
326    //
327
// Change Support
328
//
329

330    /**
331     * Inserts the <code>roots</code> and connections into the model.
332     * Notifies the model- and undo listeners of the change. The passed-in
333     * edits are executed if they implement the
334     * <code>GraphModelEvent.ExecutableGraphChange</code> interface
335     * in ascending array-order, after execution of the model change.
336     * (Note: The external order is important in a
337     * special case: After insertion on a partial view, ie. one that does not
338     * display all cells of the model, the cell is made visible after
339     * it is inserted into the model. This requires the inserting view
340     * to be able to add the cell to the visible set before it is
341     * inserted into the model.)
342     * Note: The passed-in propertyMap may contains PortViews
343     * which must be turned into Points when stored in the model.
344     */

345    public void insert(
346       Object JavaDoc[] roots,
347       Map attributes,
348       ConnectionSet cs,
349       ParentMap pm,
350       UndoableEdit[] edits) {
351       String JavaDoc undoMsg=ResourceManager.getLanguageDependentString("MessageInsertingObjects");
352       GraphModelEdit edit =
353          createInsertEdit(roots, attributes, cs, pm, edits, undoMsg);
354       if (edit != null) {
355          edit.execute();
356          if (edits != null) {
357             for (int i = 0; i < edits.length; i++)
358                if (edits[i]
359                    instanceof GraphModelEvent.ExecutableGraphChange)
360                      ((GraphModelEvent.ExecutableGraphChange) edits[i])
361                      .execute();
362          }
363          postEdit(edit);
364       }
365    }
366
367
368    /**
369     * Removes <code>cells</code> from the model.
370     * Notifies the model- and undo listeners of the change.
371     */

372    public void remove(Object JavaDoc[] roots) {
373       String JavaDoc undoMsg=ResourceManager.getLanguageDependentString("MessageRemovingObjects");
374       GraphModelEdit edit = createRemoveEdit(roots,undoMsg);
375       if (edit != null) {
376          edit.execute();
377          postEdit(edit);
378       }
379    }
380
381    /**
382     * Applies <code>attributes</code> and the connection changes to
383     * the model. The initial <code>edits</code> that triggered the call
384     * are considered to be part of this transaction. The passed-in
385     * edits are executed if they implement the
386     * <code>GraphModelEvent.ExecutableGraphChange</code> interface
387     * in ascending array-order, after execution of the model change.
388     * Notifies the model- and undo listeners of the change.
389     * <strong>Note:</strong> If only <code>edits</code> is non-null, the
390     * edits are directly passed to the UndoableEditListeners.
391     * Note: The passed-in propertyMap may contains PortViews
392     * which must be turned into Points when stored in the model.
393     */

394    public void edit(
395       Map attributes,
396       ConnectionSet cs,
397       ParentMap pm,
398       UndoableEdit[] edits) {
399       if ((attributes == null || attributes.isEmpty())
400           && (cs == null || cs.isEmpty())
401           && pm == null
402           && edits != null
403           && edits.length == 1) {
404          if (edits[0] instanceof GraphModelEvent.ExecutableGraphChange)
405                ((GraphModelEvent.ExecutableGraphChange) edits[0]).execute();
406          postEdit(edits[0]); // UndoableEdit Relay
407
} else {
408          String JavaDoc undoMsg=ResourceManager.getLanguageDependentString("MessageEditingObject");
409          GraphModelEdit edit = createCellEdit(attributes, cs, pm, edits, undoMsg);
410          //System.out.println("DefaultGraphModel_edit_attributes="+attributes);
411
if (edit != null) {
412             edit.execute();
413             if (edits != null) {
414                for (int i = 0; i < edits.length; i++)
415                   if (edits[i]
416                       instanceof GraphModelEvent.ExecutableGraphChange)
417                         ((GraphModelEvent.ExecutableGraphChange) edits[i])
418                         .execute();
419             }
420             postEdit(edit);
421          }
422       }
423    }
424
425    /**
426     * Used for editing font sizes without haveing an undo action set.
427     */

428    public void editFonts(Map attributes) {
429       GraphModelEdit edit = createCellEdit(attributes, null, null, null, null);
430       //System.out.println("DefaultGraphModel_edit_attributes="+attributes);
431
if (edit != null) {
432          edit.execute();
433       }
434    }
435
436
437    /**
438     * Used for editing font sizes without haveing an undo action set.
439     */

440    public void removeBubbles (Object JavaDoc[] bubbles,GraphModelListener gml) {
441       removeGraphModelListener(gml);
442       GraphModelEdit edit = createRemoveEdit(bubbles,null);
443       if (edit != null) {
444          edit.execute();
445       }
446       addGraphModelListener(gml);
447    }
448
449    /**
450     * Inserts the <code>roots</code> and connections into the model.
451     * Notifies the model- and undo listeners of the change. The passed-in
452     * edits are executed if they implement the
453     * <code>GraphModelEvent.ExecutableGraphChange</code> interface
454     * in ascending array-order, after execution of the model change.
455     * (Note: The external order is important in a
456     * special case: After insertion on a partial view, ie. one that does not
457     * display all cells of the model, the cell is made visible after
458     * it is inserted into the model. This requires the inserting view
459     * to be able to add the cell to the visible set before it is
460     * inserted into the model.)
461     * Note: The passed-in propertyMap may contains PortViews
462     * which must be turned into Points when stored in the model.
463     */

464    public void insertAndEdit(
465       Object JavaDoc[] roots,
466       Map attributes,
467       ConnectionSet cs,
468       ParentMap pm,
469       UndoableEdit[] edits,
470       String JavaDoc undoMsg) {
471       GraphModelEdit edit =
472          createInsertEdit(roots, attributes, cs, pm, edits, undoMsg);
473       if (edit != null) {
474          edit.execute();
475          if (edits != null) {
476             for (int i = 0; i < edits.length; i++)
477                if (edits[i]
478                    instanceof GraphModelEvent.ExecutableGraphChange)
479                      ((GraphModelEvent.ExecutableGraphChange) edits[i])
480                      .execute();
481          }
482          postEdit(edit);
483       }
484    }
485
486    /**
487     * Removes <code>cells</code> from the model and edits cells given in
488     * a <code>propertyMap</code>. If <code>removeChildren</code>
489     * is <code>true</code>, the children are also removed.
490     * Notifies the model- and undo listeners of the change.
491     */

492    public void removeAndEdit(Object JavaDoc[] roots,Map attributes,String JavaDoc name) {
493       GraphModelEdit edit = createRemoveAndCellEdit(roots,attributes,name);
494       if (edit!=null) {
495          edit.execute();
496          postEdit(edit);
497       }
498    }
499
500    /**
501     * Sends <code>cells</code> to back.
502     */

503    public void toBack(Object JavaDoc[] cells) {
504       GraphModelLayerEdit edit =
505          createLayerEdit(cells, GraphModelLayerEdit.BACK);
506       if (edit != null) {
507          edit.execute();
508          postEdit(edit);
509       }
510    }
511
512    /**
513     * Brings <code>cells</code> to front.
514     */

515    public void toFront(Object JavaDoc[] cells) {
516       GraphModelLayerEdit edit =
517          createLayerEdit(cells, GraphModelLayerEdit.FRONT);
518       if (edit != null) {
519          edit.execute();
520          postEdit(edit);
521       }
522    }
523
524    //
525
// Edit Creation
526
//
527

528    protected GraphModelLayerEdit createLayerEdit(Object JavaDoc[] cells, int layer) {
529       return new GraphModelLayerEdit(cells, layer);
530    }
531
532    /**
533     * Returns an edit that represents an insert.
534     */

535    protected GraphModelEdit createInsertEdit(
536       Object JavaDoc[] cells,
537       Map attributeMap,
538       ConnectionSet cs,
539       ParentMap pm,
540       UndoableEdit[] edits,
541       String JavaDoc name) {
542       //GraphModelEdit edit = new GraphModelEdit(cells, cs, pm, attributeMap);
543
GraphModelEdit edit =
544          createEdit(cells, null, attributeMap, cs, pm, name);
545       if (edit != null) {
546          if (edits != null)
547             for (int i = 0; i < edits.length; i++)
548                edit.addEdit(edits[i]);
549          edit.end();
550       }
551       return edit;
552    }
553
554    /**
555     * Returns an edit that represents a remove.
556     */

557    protected GraphModelEdit createRemoveEdit(Object JavaDoc[] cells,String JavaDoc name) {
558       // Remove from GraphStructure
559
ConnectionSet cs = ConnectionSet.create(this, cells, true);
560       // Remove from Group Structure
561
ParentMap pm = ParentMap.create(this, cells, true, false);
562       // Construct Edit
563
//GraphModelEdit edit = new GraphModelEdit(cells, cs, pm);
564
GraphModelEdit edit = createEdit(null, cells, null, cs, pm, name);
565       if (edit != null)
566          edit.end();
567       return edit;
568    }
569
570    /**
571     * Returns an edit that represents a change.
572     */

573    protected GraphModelEdit createCellEdit(
574       Map attributes,
575       ConnectionSet cs,
576       ParentMap pm,
577       UndoableEdit[] edits,
578       String JavaDoc name) {
579       //GraphModelEdit edit = new GraphModelEdit(cs, propertyMap, pm);
580
GraphModelEdit edit = createEdit(null, null, attributes, cs, pm, name);
581       if (edit != null) {
582          if (edits != null)
583             for (int i = 0; i < edits.length; i++)
584                edit.addEdit(edits[i]);
585          edit.end();
586       }
587       return edit;
588    }
589
590
591    protected GraphModelEdit createEdit(
592       Object JavaDoc[] inserted,
593       Object JavaDoc[] removed,
594       Map attributes,
595       ConnectionSet cs,
596       ParentMap pm,
597       String JavaDoc name) {
598       return new GraphModelEdit(
599          inserted,
600          removed,
601          attributes,
602          cs,
603          pm,
604          name);
605    }
606
607    /**
608     * Returns an edit that represents a remove and a change.
609     */

610    protected GraphModelEdit createRemoveAndCellEdit(Object JavaDoc[] cells,
611                                                     Map attributes,String JavaDoc name) {
612       // Remove from GraphStructure
613
ConnectionSet cs = ConnectionSet.create(this, cells, true);
614       // Remove from Group Structure
615
ParentMap pm = ParentMap.create(this, cells, true, false);
616       // Construct Edit
617
GraphModelEdit edit = createEdit (null,cells,attributes,cs,pm,name);
618       if (edit!=null) {
619          edit.end();
620       }
621       return edit;
622    }
623
624    //
625
// Change Handling
626
//
627

628    /**
629     * Inserts <code>cells</code> into the model. Returns
630     * the cells that were inserted (including descendants).
631     */

632    protected Object JavaDoc[] handleInsert(Object JavaDoc[] cells) {
633       Object JavaDoc[] inserted = null;
634       if (cells != null) {
635          for (int i = 0; i < cells.length; i++)
636             // Add to Roots if no parent
637
if (getParent(cells[i]) == null)
638                roots.add(cells[i]);
639          // Return *all* inserted cells
640
inserted = getDescendants(this, cells).toArray();
641       }
642       return inserted;
643    }
644
645    /**
646     * Removes <code>cells</code> from the model. Returns
647     * the cells that were removed as roots.
648     */

649    protected Object JavaDoc[] handleRemove(Object JavaDoc[] cells) {
650       List removedRoots = new ArrayList();
651       if (cells != null)
652          for (int i = 0; i < cells.length; i++)
653             if (getParent(cells[i]) == null && roots.remove(cells[i]))
654                removedRoots.add(cells[i]);
655       return removedRoots.toArray();
656    }
657
658    /**
659     * Applies <code>cells</code> to the model. Returns
660     * a parent map that may be used to undo this change.
661     */

662    protected ParentMap handleParentMap(ParentMap parentMap) {
663       if (parentMap != null) {
664          ParentMap undo = new ParentMap();
665          Iterator it = parentMap.entries();
666          while (it.hasNext()) {
667             ParentMap.Entry entry = (ParentMap.Entry) it.next();
668             Object JavaDoc child = entry.getChild();
669             Object JavaDoc parent = entry.getParent();
670             undo.addEntry(child, getParent(child));
671             if (parent == null){
672                if (child instanceof MutableTreeNode){
673                   ((MutableTreeNode)child).removeFromParent();
674                }
675             } else {
676                if (parent instanceof DefaultMutableTreeNode &&
677                    child instanceof MutableTreeNode){
678                   ((DefaultMutableTreeNode)parent).add((MutableTreeNode)child);
679                }
680             }
681
682             boolean isRoot = roots.contains(child);
683             if (parent == null && !isRoot)
684                roots.add(child);
685             else if (parent != null && isRoot)
686                roots.remove(child);
687          }
688          return undo;
689       }
690       return null;
691    }
692
693    /**
694     * Applies <code>attributes</code> to the cells specified as keys.
695     * Returns the <code>attributes</code> to undo the change.
696     */

697    protected Map handleAttributes(Map attributes) {
698       if (attributes != null) {
699          Hashtable undo = new Hashtable();
700          Iterator it = attributes.entrySet().iterator();
701          while (it.hasNext()) {
702             Map.Entry entry = (Map.Entry) it.next();
703             Object JavaDoc cell = entry.getKey();
704             Map deltaNew = (Map) entry.getValue();
705             //System.out.println("deltaNew="+deltaNew);
706
//System.out.println("stateOld="+getAttributes(cell));
707
if (cell instanceof GraphCell){
708                Map deltaOld = ((GraphCell)cell).changeAttributes(deltaNew);
709                //System.out.println("stateNew="+getAttributes(cell));
710
//System.out.println("deltaOld="+deltaOld);
711
undo.put(cell, deltaOld);
712             } else {
713                Map attr = getAttributes(cell);
714                if (attr != null){
715                   Map deltaOld = GraphConstants.applyMap(deltaNew, attr);
716                   //System.out.println("stateNew="+getAttributes(cell));
717
//System.out.println("deltaOld="+deltaOld);
718
undo.put(cell, deltaOld);
719                }
720             }
721          }
722          return undo;
723       }
724       return null;
725    }
726
727    //
728
// Connection Set Handling
729
//
730

731    /**
732     * Applies <code>connectionSet</code> to the model. Returns
733     * a connection set that may be used to undo this change.
734     */

735    protected ConnectionSet handleConnectionSet(ConnectionSet cs) {
736       if (cs != null) {
737          ConnectionSet csundo = new ConnectionSet();
738          Iterator it = cs.connections();
739          while (it.hasNext()) {
740             ConnectionSet.Connection c =
741                (ConnectionSet.Connection) it.next();
742             Object JavaDoc edge = c.getEdge();
743             if (c.isSource())
744                csundo.connect(edge, getSource(edge), true);
745             else
746                csundo.connect(edge, getTarget(edge), false);
747             handleConnection(c);
748          }
749          return csundo;
750       }
751       return null;
752    }
753
754    /**
755     * Inserts the specified connection into the model.
756     */

757    protected void handleConnection(ConnectionSet.Connection c) {
758       Object JavaDoc edge = c.getEdge();
759       Object JavaDoc old = (c.isSource()) ? getSource(edge) : getTarget(edge);
760       Object JavaDoc port = c.getPort();
761       if (port != old) {
762          connect(edge, old, c.isSource(), true);
763          if (contains(port) && contains(edge))
764             connect(edge, port, c.isSource(), false);
765       }
766    }
767
768    /**
769     * Connects or disconnects the edge and port in this model
770     * based on <code>remove</code>. Subclassers should override
771     * this to update connectivity datastructures.
772     */

773    protected void connect(
774       Object JavaDoc edge,
775       Object JavaDoc port,
776       boolean isSource,
777       boolean remove) {
778       if (port instanceof Port)
779          if (remove)
780                ((Port) port).removeEdge(edge);
781          else
782                ((Port) port).addEdge(edge);
783       if (remove)
784          port = null;
785       if (edge instanceof Edge) {
786          if (isSource)
787                ((Edge) edge).setSource(port);
788          else
789                ((Edge) edge).setTarget(port);
790       }
791    }
792
793    //
794
// GraphModelListeners
795
//
796

797    /**
798     * Adds a listener for the GraphModelEvent posted after the graph changes.
799     *
800     * @see #removeGraphModelListener
801     * @param l the listener to add
802     */

803    public void addGraphModelListener(GraphModelListener l) {
804       listenerList.add(GraphModelListener.class, l);
805    }
806
807    /**
808     * Removes a listener previously added with <B>addGraphModelListener()</B>.
809     *
810     * @see #addGraphModelListener
811     * @param l the listener to remove
812     */

813    public void removeGraphModelListener(GraphModelListener l) {
814       listenerList.remove(GraphModelListener.class, l);
815    }
816
817    /*
818     * Notify all listeners that have registered interest for
819     * notification on this event type. The event instance
820     * is lazily created using the parameters passed into
821     * the fire method.
822     * @see EventListenerList
823     */

824    protected void fireGraphChanged(
825       Object JavaDoc source,
826       GraphModelEvent.GraphModelChange edit) {
827       // Guaranteed to return a non-null array
828
Object JavaDoc[] listeners = listenerList.getListenerList();
829       GraphModelEvent e = null;
830       // Process the listeners last to first, notifying
831
// those that are interested in this event
832
for (int i = listeners.length - 2; i >= 0; i -= 2) {
833          if (listeners[i] == GraphModelListener.class) {
834             // Lazily create the event:
835
if (e == null)
836                e = new GraphModelEvent(source, edit);
837             ((GraphModelListener) listeners[i + 1]).graphChanged(e);
838          }
839       }
840    }
841
842    /**
843     * Return an array of all GraphModelListeners that were added to this model.
844     */

845    public GraphModelListener[] getGraphModelListeners() {
846       return (GraphModelListener[]) listenerList.getListeners(
847          GraphModelListener.class);
848    }
849
850    //
851
// GraphModelEdit
852
//
853

854    /**
855     * An implementation of GraphModelChange that can be added to the model
856     * event.
857     */

858    public class GraphModelEdit
859       extends CompoundEdit
860       implements
861       GraphModelEvent.GraphModelChange,
862       GraphModelEvent.ExecutableGraphChange {
863
864       /* Edit name. */
865       protected String JavaDoc name;
866
867       /* Cells that were inserted/removed/changed during the last execution. */
868       protected Object JavaDoc[] insert, changed, remove, context;
869
870       /* Cells that were inserted/removed/changed during the last execution. */
871       protected Object JavaDoc[] inserted, removed;
872
873       /* Property map for the next execution. Attribute Map is
874        passed to the views on inserts. */

875       protected Map attributes, previousAttributes;
876
877       /* Parent map for the next execution. */
878       protected ParentMap parentMap, previousParentMap;
879
880       /* ConnectionSet for the next execution. */
881       protected ConnectionSet connectionSet, previousConnectionSet;
882
883       /* Piggybacked undo from the views. */
884       protected Map cellViews = new Hashtable();
885
886       /**
887        *
888        * Constructs an edit record.
889        *
890        * @param inserted a set of roots that were inserted
891        * @param removed a set of elements that were removed
892        * @param attributes
893        * @param connectionSet
894        * @param parentMap
895        * @param name
896        */

897       public GraphModelEdit(
898          Object JavaDoc[] inserted,
899          Object JavaDoc[] removed,
900          Map attributes,
901          ConnectionSet connectionSet,
902          ParentMap parentMap,
903          String JavaDoc name) {
904          super();
905          this.insert = inserted;
906          this.remove = removed;
907          this.connectionSet = connectionSet;
908          this.attributes = attributes;
909          this.parentMap = parentMap;
910          this.name=name;
911          previousAttributes = attributes;
912          previousConnectionSet = connectionSet;
913          previousParentMap = parentMap;
914          // Remove Empty Parents
915
if (parentMap != null) {
916             // Compute Empty Group
917
/*Map childCount = new Hashtable();
918              Iterator it = parentMap.entries();
919              while (it.hasNext()) {
920              ParentMap.Entry entry = (ParentMap.Entry) it.next();
921              Object child = entry.getChild();
922              if (!isPort(child)) {
923              Object oldParent = getParent(child);
924              Object newParent = entry.getParent();
925              if (oldParent != newParent) {
926              changeChildCount(childCount, oldParent, -1);
927              changeChildCount(childCount, newParent, 1);
928              }
929              }
930              }
931              handleEmptyGroups(filterParents(childCount, 0));*/

932          }
933       }
934
935
936       public Object JavaDoc[] filterParents(Map childCount, int children) {
937          ArrayList list = new ArrayList();
938          Iterator it = childCount.entrySet().iterator();
939          while (it.hasNext()) {
940             Map.Entry entry = (Map.Entry) it.next();
941             if (entry.getValue() instanceof Integer JavaDoc) {
942                if (((Integer JavaDoc) entry.getValue()).intValue() == children)
943                   list.add(entry.getKey());
944             }
945          }
946          return list.toArray();
947       }
948
949