001/* A graph model for basic ptolemy models.
002
003 Copyright (c) 1999-2016 The Regents of the University of California.
004 All rights reserved.
005 Permission is hereby granted, without written agreement and without
006 license or royalty fees, to use, copy, modify, and distribute this
007 software and its documentation for any purpose, provided that the above
008 copyright notice and the following two paragraphs appear in all copies
009 of this software.
010
011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
015 SUCH DAMAGE.
016
017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
022 ENHANCEMENTS, OR MODIFICATIONS.
023
024 PT_COPYRIGHT_VERSION_2
025 COPYRIGHTENDKEY
026
027 */
028package ptolemy.vergil.actor;
029
030import java.util.Collections;
031import java.util.HashSet;
032import java.util.Iterator;
033import java.util.LinkedList;
034import java.util.List;
035import java.util.Set;
036
037import diva.graph.GraphEvent;
038import diva.graph.GraphUtilities;
039import diva.graph.modular.CompositeModel;
040import diva.graph.modular.CompositeNodeModel;
041import diva.graph.modular.EdgeModel;
042import diva.graph.modular.MutableEdgeModel;
043import diva.graph.modular.NodeModel;
044import diva.util.NullIterator;
045import ptolemy.actor.IOPort;
046import ptolemy.actor.IORelation;
047import ptolemy.data.BooleanToken;
048import ptolemy.data.IntToken;
049import ptolemy.data.Token;
050import ptolemy.data.expr.Parameter;
051import ptolemy.kernel.ComponentEntity;
052import ptolemy.kernel.ComponentPort;
053import ptolemy.kernel.ComponentRelation;
054import ptolemy.kernel.CompositeEntity;
055import ptolemy.kernel.Entity;
056import ptolemy.kernel.Port;
057import ptolemy.kernel.Relation;
058import ptolemy.kernel.util.Attribute;
059import ptolemy.kernel.util.ChangeListener;
060import ptolemy.kernel.util.ChangeRequest;
061import ptolemy.kernel.util.IllegalActionException;
062import ptolemy.kernel.util.InternalErrorException;
063import ptolemy.kernel.util.Locatable;
064import ptolemy.kernel.util.Location;
065import ptolemy.kernel.util.Nameable;
066import ptolemy.kernel.util.NamedObj;
067import ptolemy.moml.MoMLChangeRequest;
068import ptolemy.moml.Vertex;
069import ptolemy.vergil.basic.AbstractBasicGraphModel;
070import ptolemy.vergil.basic.NamedObjNodeModel;
071import ptolemy.vergil.kernel.Link;
072import ptolemy.vergil.toolbox.SnapConstraint;
073
074// NOTE: The inner classes here should be factored out as independent
075// classes, and the resulting NodeModel hierarchy should be carefully
076// thought out.  This work has been started, with the NamedObjNodeModel
077// base class and the AttributeNodeModel derived class.  The remaining
078// node models here and in the FSMGraphModel remain to be done.  EAL.
079///////////////////////////////////////////////////////////////////
080//// ActorGraphModel
081
082/**
083 This class represents one level of hierarchy of a Ptolemy II model.
084 The graph model represents attributes, ports, entities and relations
085 as nodes.  Entities and attributes are represented in the model by the
086 icon that is used to visually depict them.  Relations are represented
087 in the model by its vertices (which are visual elements that generally
088 exist in multiple places in a visual rendition).  Ports represent
089 themselves in the model.
090 <p>
091 In the terminology of diva, the graph elements are "nodes" (icons,
092 vertices, and ports), and the "edges" link them.  Edges
093 are represented in the model by instances of the Link class.
094 Edges may link a port and a vertex, or a port and another port.
095 For visual simplicity, both types of edges are represented by
096 an instance of the Link class.  If an edge is placed between a port
097 and a vertex then the Link represents a Ptolemy II link between
098 the port and the vertex's Relation.  However, if an edge is placed between
099 two ports, then it represents a relation (with no vertex) and links from
100 the relation to each port (in Ptolemy II, this is called a "connection").
101 <p>
102 This model uses a ptolemy change listener to detect changes to the model
103 that do not originate from this model.  These changes are propagated
104 as structure changed graph events to all graphListeners registered with this
105 model.  This mechanism allows a graph visualization of a ptolemy model to
106 remain synchronized with the state of a mutating model.
107
108 @author Steve Neuendorffer, Contributor: Edward A. Lee, Bert Rodiers
109 @version $Id$
110 @since Ptolemy II 2.0
111 @Pt.ProposedRating Yellow (neuendor)
112 @Pt.AcceptedRating Red (johnr)
113 */
114public class ActorGraphModel extends AbstractBasicGraphModel {
115    /** Construct a new graph model whose root is the given composite entity.
116     *  @param composite The top-level composite entity for the model.
117     */
118    public ActorGraphModel(NamedObj composite) {
119        super(composite);
120        _linkSet = new HashSet<Link>();
121        _update();
122    }
123
124    ///////////////////////////////////////////////////////////////////
125    ////                         public methods                    ////
126
127    /** This implementation will delegate to the implementation in the parent
128     *  class and will additionally update the model in case it is necessary.
129     *  This is typically the case when a link is created to another link.
130     *  If this existing link has a vertex as head or tail,
131     *  we will connect with the vertex, otherwise we will
132     *  remove the old link, create a new vertex, link the
133     *  head and tail of the existing link with the
134     *  vertex and link the new link with the vertex.
135     *  It is possible to link with an existing link.
136     *  If this existing link has a vertex as head or tail,
137     *  we will connect with the vertex, otherwise we will
138     *  remove the old link, create a new vertex, link the
139     *  head and tail of the existing link with the
140     *  vertex and link the new link with the vertex.
141     *  In the latter case the parent class won't call _update
142     *  as a result of an optimization, and hence we do it here.
143     *
144     *  @param change The change that has been executed.
145     */
146    @Override
147    public void changeExecuted(ChangeRequest change) {
148        super.changeExecuted(change);
149
150        // update the graph model if necessary.
151        if (_forceUpdate && _update()) {
152            _forceUpdate = false;
153            // Notify any graph listeners
154            // that the graph might have
155            // completely changed.
156            dispatchGraphEvent(new GraphEvent(this,
157                    GraphEvent.STRUCTURE_CHANGED, getRoot()));
158        }
159    }
160
161    /** Disconnect an edge from its two endpoints and notify graph
162     *  listeners with an EDGE_HEAD_CHANGED and an EDGE_TAIL_CHANGED
163     *  event whose source is the given source.
164     *  @param eventSource The source of the event that will be dispatched,
165     *   e.g. the view that made this call.
166     *  @param edge The edge.
167     */
168    @Override
169    public void disconnectEdge(Object eventSource, Object edge) {
170        if (!(getEdgeModel(edge) instanceof MutableEdgeModel)) {
171            return;
172        }
173
174        MutableEdgeModel model = (MutableEdgeModel) getEdgeModel(edge);
175        Object head = model.getHead(edge);
176        Object tail = model.getTail(edge);
177        model.setTail(edge, null);
178        model.setHead(edge, null);
179
180        if (head != null) {
181            GraphEvent e = new GraphEvent(eventSource,
182                    GraphEvent.EDGE_HEAD_CHANGED, edge, head);
183            dispatchGraphEvent(e);
184        }
185
186        if (tail != null) {
187            GraphEvent e = new GraphEvent(eventSource,
188                    GraphEvent.EDGE_TAIL_CHANGED, edge, tail);
189            dispatchGraphEvent(e);
190        }
191    }
192
193    /** Return the model for the given composite object.
194     *  In this class, return an instance of CompositePtolemyModel
195     *  if the object is the root object of this graph model, and return
196     *  an instance of IconModel if the object is a location contained
197     *  by an entity.  Otherwise return null.
198     *  @param composite A composite object.
199     *  @return A model of a composite node.
200     */
201    @Override
202    public CompositeModel getCompositeModel(Object composite) {
203        CompositeModel result = super.getCompositeModel(composite);
204
205        if (result == null && composite instanceof Locatable
206                && ((Locatable) composite).getContainer() instanceof Entity) {
207            return _iconModel;
208        }
209
210        return result;
211    }
212
213    /** Return a MoML String that will delete the given edge from the
214     *  Ptolemy model.
215     *  @param edge The edge.
216     *  @return A valid MoML string.
217     */
218    @Override
219    public String getDeleteEdgeMoML(Object edge) {
220        // Note: the abstraction here is rather broken.  Ideally this
221        // should look like getDeleteNodeMoML()
222        if (!(getEdgeModel(edge) instanceof LinkModel)) {
223            return "";
224        }
225
226        LinkModel model = (LinkModel) getEdgeModel(edge);
227        return model.getDeleteEdgeMoML(edge);
228    }
229
230    /** Return a MoML String that will delete the given node from the
231     *  Ptolemy model.
232     *  @param node The node.
233     *  @return A valid MoML string.
234     */
235    @Override
236    public String getDeleteNodeMoML(Object node) {
237        if (!(getNodeModel(node) instanceof NamedObjNodeModel)) {
238            return "";
239        }
240
241        NamedObjNodeModel model = (NamedObjNodeModel) getNodeModel(node);
242        return model.getDeleteNodeMoML(node);
243    }
244
245    /** Return the model for the given edge object.  If the object is not
246     *  an edge, then return null.
247     *  @param edge An object which is assumed to be in this graph model.
248     *  @return An instance of LinkModel if the object is a Link.
249     *   Otherwise return null.
250     */
251    @Override
252    public EdgeModel getEdgeModel(Object edge) {
253        if (edge instanceof Link) {
254            return _linkModel;
255        } else {
256            return null;
257        }
258    }
259
260    /** Return the model for edge objects that are instance of Link.
261     *  This will return the same object as getEdgeModel() when the
262     *  argument is a link.
263     *  @return The model for links.
264     */
265    public LinkModel getLinkModel() {
266        // FIXME: This design makes it impossible to have different
267        // models for different types of links.  This method should
268        // be removed, and getEdgeModel() should be used instead.
269        return _linkModel;
270    }
271
272    /** Return the node model for the given object.  If the object is not
273     *  a node, then return null.  The argument should be either an instance
274     *  of Port or Vertex, or it implements Locatable.
275     *  @param node An object which is assumed to be in this graph model.
276     *  @return The node model for the specified node, or null if there
277     *   is none.
278     */
279    @Override
280    public NodeModel getNodeModel(Object node) {
281        if (node instanceof Port) {
282            return _portModel;
283        } else if (node instanceof Vertex) {
284            return _vertexModel;
285        } else if (node instanceof Locatable) {
286            Object container = ((Locatable) node).getContainer();
287
288            if (container instanceof Port) {
289                return _externalPortModel;
290            } else if (container instanceof Entity) {
291                return _iconModel;
292            }
293        }
294
295        return super.getNodeModel(node);
296    }
297
298    /** Return the semantic object corresponding to the given node, edge,
299     *  or composite.  A "semantic object" is an object associated with
300     *  a node in the graph.  In this case, if the node is icon, the
301     *  semantic object is an entity.  If it is a vertex or a link, the
302     *  semantic object is a relation.  If it is a port, then the
303     *  semantic object is the port itself.
304     *  @param element A graph element.
305     *  @return The semantic object associated with this element, or null
306     *   if the object is not recognized.
307     */
308    @Override
309    public Object getSemanticObject(Object element) {
310        if (element instanceof Vertex) {
311            return ((Vertex) element).getContainer();
312        } else if (element instanceof Link) {
313            return ((Link) element).getRelation();
314        }
315
316        return super.getSemanticObject(element);
317    }
318
319    /** Delete a node from its parent graph and notify
320     *  graph listeners with a NODE_REMOVED event.
321     *  @param eventSource The source of the event that will be dispatched,
322     *   e.g. the view that made this call.
323     *  @param node The node.
324     */
325    @Override
326    public void removeNode(Object eventSource, Object node) {
327        if (!(getNodeModel(node) instanceof NamedObjNodeModel)) {
328            return;
329        }
330
331        NamedObjNodeModel model = (NamedObjNodeModel) getNodeModel(node);
332
333        model.removeNode(eventSource, node);
334    }
335
336    // FIXME: The following methods are probably inappropriate.
337    // They make it impossible to have customized models for
338    // particular links or icons. getLinkModel() and
339    // getNodeModel() should be sufficient.
340    // Big changes needed, however to make this work.
341    // The huge inner classes below should be factored out as
342    // separate classes.  EAL
343    /** Get the icon model.
344     *  @return The icon model.
345     */
346    public IconModel getIconModel() {
347        return _iconModel;
348    }
349
350    /** Get the port model.
351     *  @return The port model.
352     */
353    public PortModel getPortModel() {
354        return _portModel;
355    }
356
357    /** Get the external port model.
358     *  @return The external port model.
359     */
360    public ExternalPortModel getExternalPortModel() {
361        return _externalPortModel;
362    }
363
364    /** Get the vertex model.
365     *  @return The vertex model.
366     */
367    public VertexModel getVertexModel() {
368        return _vertexModel;
369    }
370
371    // End of FIXME.
372    ///////////////////////////////////////////////////////////////////
373    ////                         protected methods                 ////
374
375    /** Get an unmodifiable copy of the link set.
376     *
377     *  @return The link set.
378     */
379    protected Set<?> _getLinkSet() {
380        return Collections.unmodifiableSet(_linkSet);
381    }
382
383    /** Remove a link from the link set. This function is not made synchronized.
384     *  Concurrent modification on the link set should be avoided.
385     *
386     *  @param link The link to be removed.
387     */
388    protected void _removeLink(Link link) {
389        _linkSet.remove(link);
390    }
391
392    /** Update the graph model.  This is called whenever a change request is
393     *  executed.  In this class the internal set of link objects is created
394     *  to represent each of the links in the graph, and to remove any
395     *  link objects that are incorrect (e.g., do not have both ends
396     *  in the model).
397     *  This method is usually called just before
398     *  issuing a graph event.  If this method returns false, then the graph
399     *  event should not be issued, because further changes are necessary.
400     *  @return True if the model was successfully updated, or false if
401     *  further change requests were queued.
402     */
403    @Override
404    protected boolean _update() {
405        // Go through all the links that currently exist, and remove
406        // any that don't have both ends in the model.
407
408        Iterator<Link> links = _linkSet.iterator();
409
410        while (links.hasNext()) {
411            Link link = links.next();
412            Relation relation = link.getRelation();
413
414            // Undo needs this: Check that the relation hasn't been removed
415            if (relation == null || relation.getContainer() == null
416                    || _isHidden(relation)) {
417                // NOTE: We used to not do the next three lines when
418                // relation == null, but this seems better.
419                // EAL 6/26/05.
420                link.setHead(null);
421                link.setTail(null);
422                links.remove();
423                continue;
424            }
425
426            boolean headOK = GraphUtilities.isContainedNode(link.getHead(),
427                    getRoot(), this);
428            boolean tailOK = GraphUtilities.isContainedNode(link.getTail(),
429                    getRoot(), this);
430
431            // If the head or tail has been removed, then remove this link.
432            if (!(headOK && tailOK)) {
433                Object headObj = getSemanticObject(link.getHead());
434                Object tailObj = getSemanticObject(link.getTail());
435                link.setHead(null);
436                link.setTail(null);
437                links.remove();
438
439                if (headObj instanceof Port && tailObj instanceof Port
440                        && relation.getContainer() != null
441                        && relation.linkedPortList().size() < 2) {
442                    NamedObj container = getPtolemyModel();
443
444                    // remove the relation  This should trigger removing the
445                    // other link.  This avoids turning a direct connection
446                    // into a half connection with a diamond.
447                    // Note that the source is NOT the graphmodel, so this
448                    // will trigger the changerequest listener to
449                    // redraw the graph again.
450                    MoMLChangeRequest request = new MoMLChangeRequest(container,
451                            container, "<deleteRelation name=\""
452                                    + relation.getName(container) + "\"/>\n");
453                    request.setUndoable(true);
454
455                    // Need to merge the undo for this request in with one that
456                    // triggered it
457                    request.setMergeWithPreviousUndo(true);
458                    container.requestChange(request);
459
460                    // If updating requires further updates to the model
461                    // i.e. the above change request, then return false.
462                    // this is so that rerendering doesn't happen until the
463                    // graph model has reached a stable point.
464                    // Note that there is a bit of a performance tradeoff
465                    // here as to whether we queue a bunch of mutations in
466                    // parallel (which may be redundant) or queue them
467                    // serially (which may be slow).
468                    return false;
469                }
470            }
471        }
472
473        // Now create Links for links that may be new
474        NamedObj ptolemyModel = getPtolemyModel();
475
476        if (ptolemyModel instanceof CompositeEntity) {
477            Iterator<?> relations = ((CompositeEntity) ptolemyModel)
478                    .relationList().iterator();
479
480            while (relations.hasNext()) {
481                _updateLinks((ComponentRelation) relations.next());
482            }
483        }
484
485        return super._update();
486    }
487
488    ///////////////////////////////////////////////////////////////////
489    ////                         private methods                   ////
490
491    /** Return true if the relation has a _hide attribute indicating
492     *  that it is hidden.
493     *  @return True if the relation is hidden.
494     */
495    private boolean _isHidden(Relation relation) {
496        Attribute hide = relation.getAttribute("_hide");
497        if (hide != null) {
498            if (hide instanceof Parameter) {
499                Token token;
500                try {
501                    token = ((Parameter) hide).getToken();
502                    if (token instanceof BooleanToken) {
503                        if (((BooleanToken) token).booleanValue()) {
504                            return true;
505                        }
506                    }
507                } catch (IllegalActionException e) {
508                    throw new InternalErrorException(e);
509                }
510            } else {
511                // The mere presence of the attribute will hide
512                // the relation.
513                return true;
514            }
515        }
516        return false;
517    }
518
519    /** Make sure that there is a Link object representing every
520     *  link connected to the given relation.  Create links if necessary.
521     */
522    private void _updateLinks(ComponentRelation relation) {
523        // If the relation is hidden, then skip it.
524        if (_isHidden(relation)) {
525            return;
526        }
527        // FIXME: This method is expensive for large graphs.
528        // It is called for each relation, it creates a new list
529        // of links for each relation, it then goes through the full
530        // list of all existing links, looking only at the ones
531        // associated with this relation.  Ugh.
532        // Create a list of linked objects.
533        // We will remove objects from this list as we discover
534        // existing links to them, and then create links to any
535        // remaining objects in the list.
536
537        List<?> linkedObjects = relation.linkedObjectsList();
538        int linkedObjectsCount = linkedObjects.size();
539
540        for (Link link : new LinkedList<Link>(_linkSet)) {
541
542            // If this link matches a link in the linkedObjects list,
543            // then we remove that link from that list, since we don't
544            // have to manufacture that link.
545            Object tail = link.getTail();
546            Object tailObj = getSemanticObject(tail);
547            Object head = link.getHead();
548            Object headObj = getSemanticObject(head);
549
550            if (tailObj != relation && headObj != relation
551                    && link.getRelation() != relation) {
552                // The link does not involve this relation. Skip it.
553                // NOTE: Used to skip it if the relation field of the link
554                // didn't match this relation. But we need to ignore
555                // that field for links between relations, since that
556                // field will be arbitrarily one of the relations,
557                // and we'll end up creating two links where there
558                // should be one.
559                // EAL 6/26/05
560                continue;
561            }
562
563            if (tailObj != relation && headObj != relation
564                    && linkedObjectsCount > 2) {
565                // When the link is a direct link between two ports but the
566                // relation has more than 2 ends, the link is corrupted and
567                // should be deleted. This could happen as a result of model
568                // transformation of the model in the frame.
569                // tfeng (03/10/2009)
570                link.setHead(null);
571                link.setTail(null);
572                _linkSet.remove(link);
573                continue;
574            }
575
576            if (tailObj != null && linkedObjects.contains(tailObj)) {
577                // The tail is an object in the list.
578                linkedObjects.remove(tailObj);
579            } else if (tailObj != relation) {
580                // Unless the tail object is this relation, the link
581                // must be spurious. Remove the link.
582                link.setHead(null);
583                link.setTail(null);
584                _linkSet.remove(link);
585            }
586
587            if (headObj != null && linkedObjects.contains(headObj)) {
588                // The head is an object in the list.
589                linkedObjects.remove(headObj);
590            } else if (headObj != relation) {
591                // Unless the head object is this relation, the link
592                // must be spurious. Remove the link.
593                link.setHead(null);
594                link.setTail(null);
595                _linkSet.remove(link);
596            }
597        }
598
599        // Count the remaining linked objects, which are those
600        // for which there is no Link object.
601        int unlinkedPortCount = linkedObjects.size();
602
603        // If there are no links left to create, then just return.
604        if (unlinkedPortCount == 0) {
605            return;
606        }
607
608        // Get the Root vertex.  This is where we will manufacture links.
609        // The root vertex is the one with no linked vertices.
610        Vertex rootVertex = null;
611        Iterator<?> vertexes = relation.attributeList(Vertex.class).iterator();
612
613        while (vertexes.hasNext()) {
614            Vertex v = (Vertex) vertexes.next();
615
616            if (v.getLinkedVertex() == null) {
617                rootVertex = v;
618            }
619        }
620
621        // If there are no verticies, and the relation has exactly
622        // two connections, neither of which has been made yet, then
623        // create a link without a vertex for the relation.
624        if (rootVertex == null && linkedObjectsCount == 2
625                && unlinkedPortCount == 2
626                && linkedObjects.get(0) instanceof Port
627                && linkedObjects.get(1) instanceof Port) {
628            Port port1 = (Port) linkedObjects.get(0);
629            Port port2 = (Port) linkedObjects.get(1);
630            Object head = null;
631            Object tail = null;
632
633            if (port1.getContainer().equals(getRoot())) {
634                head = _getLocation(port1);
635            } else {
636                head = port1;
637            }
638
639            if (port2.getContainer().equals(getRoot())) {
640                tail = _getLocation(port2);
641            } else {
642                tail = port2;
643            }
644
645            Link link;
646
647            try {
648                link = new Link();
649                _linkSet.add(link);
650            } catch (Exception e) {
651                throw new InternalErrorException("Failed to create "
652                        + "new link, even though one does not "
653                        + "already exist:" + e.getMessage());
654            }
655
656            link.setRelation(relation);
657            link.setHead(head);
658            link.setTail(tail);
659        } else {
660            // A regular relation with a diamond.
661            // Create a vertex if one is not found.
662            if (rootVertex == null) {
663                try {
664                    String name = relation.uniqueName("vertex");
665                    rootVertex = new Vertex(relation, name);
666
667                    // Have to manually handle propagation, since
668                    // the MoML parser is not involved.
669                    // FIXME: This could cause a name collision!
670                    // (Unlikely though since auto naming will take
671                    // into account subclasses).
672                    rootVertex.propagateExistence();
673                } catch (Throwable throwable) {
674                    throw new InternalErrorException(null, throwable,
675                            "Failed to create "
676                                    + "new vertex, even though one does not "
677                                    + "already exist:"
678                                    + throwable.getMessage());
679                }
680            }
681
682            // Create any required links for this relation.
683            Iterator<?> linkedObjectsIterator = linkedObjects.iterator();
684
685            while (linkedObjectsIterator.hasNext()) {
686                Object portOrRelation = linkedObjectsIterator.next();
687
688                // Set the head to the port or relation. More precisely:
689                //   If it is a port belonging to the composite, then
690                //   set the head to a Location contained by the port.
691                //   If is a port belonging to an actor, then set
692                //   the head to the port.
693                //   If it is a relation, then set the head to the
694                //   root vertex of the relation.
695                Object head = null;
696
697                if (portOrRelation instanceof Port) {
698                    Port port = (Port) portOrRelation;
699
700                    if (port.getContainer().equals(getRoot())) {
701                        head = _getLocation(port);
702                    } else {
703                        head = port;
704                    }
705                } else {
706                    // Get the Root vertex of the other relation.
707                    // The root vertex is the one with no linked vertices.
708                    vertexes = ((Relation) portOrRelation)
709                            .attributeList(Vertex.class).iterator();
710
711                    while (vertexes.hasNext()) {
712                        Vertex v = (Vertex) vertexes.next();
713
714                        if (v.getLinkedVertex() == null) {
715                            head = v;
716                        }
717                    }
718                }
719
720                Link link;
721
722                try {
723                    link = new Link();
724                    _linkSet.add(link);
725                } catch (Exception e) {
726                    throw new InternalErrorException("Failed to create "
727                            + "new link, even though one does not "
728                            + "already exist:" + e.getMessage());
729                }
730
731                link.setRelation(relation);
732                link.setHead(head);
733                link.setTail(rootVertex);
734            }
735        }
736    }
737
738    ///////////////////////////////////////////////////////////////////
739    ////                         private variables                 ////
740
741    /** The models of the different types of nodes and edges. */
742    private ExternalPortModel _externalPortModel = new ExternalPortModel();
743
744    /** A flag to force calling update when a ChangeRequest has been executed. */
745    private boolean _forceUpdate = false;
746
747    private IconModel _iconModel = new IconModel();
748
749    private LinkModel _linkModel = new LinkModel();
750
751    /** The set of all links in the model. */
752    private Set<Link> _linkSet;
753
754    private PortModel _portModel = new PortModel();
755
756    private VertexModel _vertexModel = new VertexModel();
757
758    ///////////////////////////////////////////////////////////////////
759    ////                         inner classes                     ////
760
761    /** The model for ports that make external connections to this graph.
762     *  These ports are always contained by the root of this graph model.
763     */
764    public class ExternalPortModel extends NamedObjNodeModel {
765        /** Return a MoML String that will delete the given node from the
766         *  Ptolemy model. The MoML assumes a context that is the container
767         *  of the port.
768         *  @param node The node.
769         *  @return A valid MoML string.
770         */
771        @Override
772        public String getDeleteNodeMoML(Object node) {
773            Locatable location = (Locatable) node;
774            ComponentPort port = (ComponentPort) location.getContainer();
775            StringBuffer moml = new StringBuffer();
776            moml.append("<deletePort name=\"" + port.getName() + "\"/>\n");
777            return moml.toString();
778        }
779
780        /** Return the graph parent of the given node.
781         *  @param node The node, which is assumed to be a port contained in
782         *   the root of this graph model.
783         *  @return The root of this graph model.
784         */
785        @Override
786        public Object getParent(Object node) {
787            return ((Locatable) node).getContainer().getContainer();
788        }
789
790        /** Return an iterator over the edges coming into the given node.
791         *  This method first ensures that there is a link
792         *  object for every link.
793         *  Then the iterator is constructed by
794         *  removing any links that do not have the given node as head.
795         *  @param node The node, which is assumed to be a port contained in
796         *   the root of this graph model.
797         *  @return An iterator of Link objects, all of which have
798         *   the given node as their head.
799         */
800        @Override
801        public Iterator inEdges(Object node) {
802            Locatable location = (Locatable) node;
803            //ComponentPort port = (ComponentPort) location.getContainer();
804
805            // make sure that the links to relations that we are connected to
806            // are up to date.
807            // Go through all the links, creating a list of
808            // those we are connected to.
809            List<Link> portLinkList = new LinkedList<Link>();
810
811            for (Link link : _linkSet) {
812                Object head = link.getHead();
813
814                if (head != null && head.equals(location)) {
815                    portLinkList.add(link);
816                }
817            }
818
819            return portLinkList.iterator();
820        }
821
822        /** Return an iterator over the edges coming out of the given node.
823         *  This iterator is constructed by looping over all the relations
824         *  that the port is connected to, and ensuring that there is a link
825         *  object for every link.  Then the iterator is constructed by
826         *  removing any links that do not have the given node as tail.
827         *  @param node The node, which is assumed to be a port contained in
828         *   the root of this graph model.
829         *  @return An iterator of Link objects, all of which have their
830         *   tail as the given node.
831         */
832        @Override
833        public Iterator outEdges(Object node) {
834            Locatable location = (Locatable) node;
835            //ComponentPort port = (ComponentPort) location.getContainer();
836
837            // make sure that the links to relations that we are connected to
838            // are up to date.
839            // Go through all the links, creating a list of
840            // those we are connected to.
841            List<Link> portLinkList = new LinkedList<Link>();
842
843            for (Link link : _linkSet) {
844                Object tail = link.getTail();
845
846                if (tail != null && tail.equals(location)) {
847                    portLinkList.add(link);
848                }
849            }
850
851            return portLinkList.iterator();
852        }
853
854        /** Remove the given edge from the model.
855         *  @param eventSource The source of the event that will be dispatched,
856         *   e.g. the view that made this call.
857         *  @param node The node.
858         */
859        @Override
860        public void removeNode(final Object eventSource, Object node) {
861            Locatable location = (Locatable) node;
862            ComponentPort port = (ComponentPort) location.getContainer();
863            NamedObj container = port.getContainer();
864            ;
865
866            StringBuffer moml = new StringBuffer();
867            moml.append(
868                    "<deletePort name=\"" + port.getName(container) + "\"/>\n");
869
870            // Note: The source is NOT the graph model.
871            MoMLChangeRequest request = new MoMLChangeRequest(this, container,
872                    moml.toString());
873            request.setUndoable(true);
874            container.requestChange(request);
875        }
876    }
877
878    /** The model for an icon that contains ports.
879     */
880    public static class IconModel extends NamedObjNodeModel
881            implements CompositeNodeModel {
882        // FindBugs suggests making this class static so as to decrease
883        // the size of instances and avoid dangling references.
884
885        /** Return a MoML String that will delete the given node from the
886         *  Ptolemy model. The returned string assumes that the context is
887         *  the container of the object with an icon.
888         *  @param node The node.
889         *  @return A valid MoML string.
890         */
891        @Override
892        public String getDeleteNodeMoML(Object node) {
893            NamedObj deleteObj = ((Locatable) node).getContainer();
894            String moml = "<deleteEntity name=\"" + deleteObj.getName()
895                    + "\"/>\n";
896            return moml;
897        }
898
899        /** Return the number of nodes contained in
900         *  this graph or composite node.
901         *  @param composite The composite, which is assumed to be an icon.
902         *  @return The number of ports contained in the container of the icon.
903         */
904        @Override
905        public int getNodeCount(Object composite) {
906            Locatable location = (Locatable) composite;
907            return ((ComponentEntity) location.getContainer()).portList()
908                    .size();
909        }
910
911        /** Return the graph parent of the given node.
912         *  @param node The node, which is assumed to be an icon.
913         *  @return The container of the Icon's container, which should be
914         *   the root of the graph.
915         */
916        @Override
917        public Object getParent(Object node) {
918            return ((Locatable) node).getContainer().getContainer();
919        }
920
921        /** Return an iterator over the edges coming into the given node.
922         *  @param node The node, which is assumed to be an icon.
923         *  @return A NullIterator, since no edges are attached to icons.
924         */
925        @Override
926        public Iterator inEdges(Object node) {
927            return new NullIterator();
928        }
929
930        /** Provide an iterator over the nodes in the
931         *  given graph or composite node. The nodes are ports, so if the
932         *  container of the node is not an entity, then an empty iterator
933         *  is returned.  This iterator
934         *  does not necessarily support removal operations.
935         *  @param composite The composite, which is assumed to be an icon.
936         *  @return An iterator over the ports contained in the container
937         *   of the icon.
938         */
939        @Override
940        public Iterator nodes(Object composite) {
941            Locatable location = (Locatable) composite;
942            Nameable container = location.getContainer();
943
944            if (container instanceof ComponentEntity) {
945                ComponentEntity entity = (ComponentEntity) container;
946                return entity.portList().iterator();
947            } else {
948                return new NullIterator();
949            }
950        }
951
952        /**
953         * Provide an iterator over the nodes that should
954         * be rendered prior to the edges. This iterator
955         * does not necessarily support removal operations.
956         * In this base class, this returns the same iterator
957         * as the nodes(Object) method.
958         * @param composite The composite, which is assumed to be an icon.
959         * @return An iterator of nodes that should be rendered before
960         * the edges.
961         */
962        @Override
963        public Iterator nodesBeforeEdges(Object composite) {
964            return nodes(composite);
965        }
966
967        /**
968         * Provide an iterator over the nodes that should
969         * be rendered after to the edges. This iterator
970         * does not necessarily support removal operations.
971         * In this base class, this returns an iterator over
972         * nothing.
973         * @param composite The composite, which is assumed to be an icon.
974         * @return An iterator of nodes that should be rendered after
975         * the edges.
976         */
977        @Override
978        public Iterator nodesAfterEdges(Object composite) {
979            return new NullIterator();
980        }
981
982        /** Return an iterator over the edges coming out of the given node.
983         *  @param node The node, which is assumed to be an icon.
984         *  @return A NullIterator, since no edges are attached to icons.
985         */
986        @Override
987        public Iterator outEdges(Object node) {
988            return new NullIterator();
989        }
990
991        /** Remove the given node from the model.  The node is assumed
992         *  to be an icon.
993         *  @param eventSource The source of the event that will be dispatched,
994         *   e.g. the view that made this call.
995         *  @param node The node.
996         */
997        @Override
998        public void removeNode(final Object eventSource, Object node) {
999            NamedObj deleteObj = ((Locatable) node).getContainer();
1000
1001            if (!(deleteObj instanceof ComponentEntity)) {
1002                throw new InternalErrorException(
1003                        "Attempt to remove a node that is not an Entity. "
1004                                + "node = " + node);
1005            }
1006
1007            // Make the request in the context of the container.
1008            NamedObj container = deleteObj.getContainer();
1009            ;
1010
1011            String moml = "<deleteEntity name=\"" + deleteObj.getName()
1012                    + "\"/>\n";
1013
1014            // Note: The source is NOT the graph model.
1015            MoMLChangeRequest request = new MoMLChangeRequest(this, container,
1016                    moml);
1017            request.setUndoable(true);
1018            container.requestChange(request);
1019        }
1020    }
1021
1022    /** The model for links that connect two ports, or a port and a vertex.
1023     */
1024    public class LinkModel implements MutableEdgeModel {
1025        /** Return true if the head of the given edge can be attached to the
1026         *  given node.
1027         *  @param edge The edge to attach, which is assumed to be a link.
1028         *  @param node The node to attach to.
1029         *  @return True if the node is a port or a vertex, or a location
1030         *  representing a port.
1031         */
1032        @Override
1033        public boolean acceptHead(Object edge, Object node) {
1034            if (node instanceof Port || node instanceof Vertex
1035                    || node instanceof Locatable && ((Locatable) node)
1036                            .getContainer() instanceof Port) {
1037                return true;
1038            } else {
1039                return false;
1040            }
1041        }
1042
1043        /** Return true if the tail of the given edge can be attached to the
1044         *  given node.
1045         *  @param edge The edge to attach, which is assumed to be a link.
1046         *  @param node The node to attach to.
1047         *  @return True if the node is a port or a vertex, or a location
1048         *  representing a port.
1049         */
1050        @Override
1051        public boolean acceptTail(Object edge, Object node) {
1052            if (node instanceof Port || node instanceof Vertex
1053                    || node instanceof Locatable && ((Locatable) node)
1054                            .getContainer() instanceof Port) {
1055                return true;
1056            } else {
1057                return false;
1058            }
1059        }
1060
1061        /** Generate the moml to add a vertex to an exist link.
1062         *  @param moml The moml to add the vertex to the link.
1063         *  @param failmoml The moml to undo these changed when
1064         *                      something goes wrong.
1065         *  @param container The container.
1066         *  @param oldLink The link that will be replace by two new once
1067         *      and a vertex in between.
1068         *  @param newRelationName The name of the new relation.
1069         *  @param x The x coordinate of the location of the vertex.
1070         *  @param y The y coordinate of the location of the vertex.
1071         */
1072        public void addNewVertexToLink(final StringBuffer moml,
1073                final StringBuffer failmoml, final CompositeEntity container,
1074                Link oldLink, String newRelationName, double x, double y) {
1075
1076            final String vertexName = "vertex1";
1077            ComponentRelation relation = oldLink.getRelation();
1078            int width = IORelation.WIDTH_TO_INFER;
1079            if (relation instanceof IORelation) {
1080                Parameter widthPar = ((IORelation) relation).width;
1081                try {
1082                    IntToken t = (IntToken) widthPar.getToken();
1083                    if (t != null) {
1084                        width = t.intValue();
1085                    }
1086                } catch (IllegalActionException e) {
1087                    // ignore the exception. If we can't request the
1088                    // width, we'll use WIDTH_TO_INFER
1089                }
1090            }
1091
1092            // Create the relation.
1093            moml.append("<relation name=\"" + newRelationName + "\">\n");
1094            moml.append(
1095                    "<property name=\"width\" class=\"ptolemy.data.expr.Parameter\""
1096                            + " value=\"" + width + "\"></property>");
1097            moml.append("<vertex name=\"" + vertexName + "\" value=\"{");
1098            moml.append(x + ", " + y);
1099            moml.append("}\"/>\n");
1100
1101            moml.append("</relation>");
1102
1103            // We will remove the existing link, but before doing that
1104            // we need to retrieve the index to reconnect at the correct index
1105
1106            boolean headIsActorPort = oldLink.getHead() instanceof IOPort;
1107            boolean tailIsActorPort = oldLink.getTail() instanceof IOPort;
1108
1109            NamedObj oldHead = (NamedObj) oldLink.getHead();
1110            NamedObj oldTail = (NamedObj) oldLink.getTail();
1111
1112            _unlinkMoML(container, moml, oldHead, oldTail, relation);
1113
1114            NamedObj oldHeadSemantic = (NamedObj) getSemanticObject(oldHead);
1115
1116            if (oldHeadSemantic != null) {
1117                int headRelationIndex = oldHeadSemantic instanceof IOPort
1118                        ? IOPort.getRelationIndex((IOPort) oldHeadSemantic,
1119                                relation, headIsActorPort)
1120                        : -1;
1121                _linkWithRelation(moml, failmoml, container, oldHeadSemantic,
1122                        headRelationIndex, newRelationName);
1123            }
1124
1125            NamedObj oldTailSemantic = (NamedObj) getSemanticObject(oldTail);
1126
1127            if (oldTailSemantic != null) {
1128                int tailRelationIndex = oldTailSemantic instanceof IOPort
1129                        ? IOPort.getRelationIndex((IOPort) oldTailSemantic,
1130                                relation, tailIsActorPort)
1131                        : -1;
1132
1133                _linkWithRelation(moml, failmoml, container, oldTailSemantic,
1134                        tailRelationIndex, newRelationName);
1135            }
1136        }
1137
1138        /** Return a MoML String that will delete the given edge from the
1139         *  Ptolemy model.
1140         *  @param edge The edge.
1141         *  @return A valid MoML string.
1142         */
1143        public String getDeleteEdgeMoML(Object edge) {
1144            final Link link = (Link) edge;
1145            NamedObj linkHead = (NamedObj) link.getHead();
1146            NamedObj linkTail = (NamedObj) link.getTail();
1147            Relation linkRelation = link.getRelation();
1148
1149            // This moml is parsed to execute the change
1150            StringBuffer moml = new StringBuffer();
1151
1152            // Make the request in the context of the container.
1153            // JDK1.2.2 fails to compile the next line.
1154            NamedObj container = getPtolemyModel();
1155
1156            // create moml to unlink any existing.
1157            try {
1158                _unlinkMoML(container, moml, linkHead, linkTail, linkRelation);
1159            } catch (Exception ex) {
1160                throw new RuntimeException(ex.getMessage());
1161            }
1162
1163            return moml.toString();
1164        }
1165
1166        /** Return the head node of the given edge.
1167         *  @param edge The edge, which is assumed to be a link.
1168         *  @return The node that is the head of the specified edge.
1169         *  @see #setHead(Object, Object)
1170         */
1171        @Override
1172        public Object getHead(Object edge) {
1173            return ((Link) edge).getHead();
1174        }
1175
1176        /** Return the tail node of the specified edge.
1177         *  @param edge The edge, which is assumed to be a link.
1178         *  @return The node that is the tail of the specified edge.
1179         *  @see #setTail(Object, Object)
1180         */
1181        @Override
1182        public Object getTail(Object edge) {
1183            return ((Link) edge).getTail();
1184        }
1185
1186        /** Return true if this edge is directed.
1187         *  In this model, none of edges
1188         *  are directed, so this always returns false.
1189         *  @param edge The edge, which is assumed to be a link.
1190         *  @return False.
1191         */
1192        @Override
1193        public boolean isDirected(Object edge) {
1194            return false;
1195        }
1196
1197        /** Connect the given edge to the given head node.
1198         *  This class queues a new change request with the ptolemy model
1199         *  to make this modification.
1200         *  @param edge The edge, which is assumed to be a link.
1201         *  @param newLinkHead The new head for the edge, which is assumed to
1202         *  be a location representing a port, a port or a vertex.
1203         *  @see #getHead(Object)
1204         */
1205        @Override
1206        public void setHead(final Object edge, final Object newLinkHead) {
1207            _setHeadOrTail(edge, newLinkHead, true);
1208        }
1209
1210        /** Connect the given edge to the given tail node.
1211         *  This class queues a new change request with the ptolemy model
1212         *  to make this modification.
1213         *  @param edge The edge, which is assumed to be a link.
1214         *  @param newLinkTail The new tail for the edge, which is
1215         *  assumed to be a location representing a port, a port or a
1216         *  vertex.
1217         *  @see #getTail(Object)
1218         */
1219        @Override
1220        public void setTail(final Object edge, final Object newLinkTail) {
1221            _setHeadOrTail(edge, newLinkTail, false);
1222        }
1223
1224        ///////////////////////////////////////////////////////////////////
1225        ////                         private methods                   ////
1226
1227        /** Get a location for a new relations between the ports denoted by
1228         * semanticHead and semanticTail.
1229         * @param semanticHead The head for the new relation.
1230         * @param semanticTail The tail for the new relation.
1231         * @param headIsActorPort A flag that specifies whether this is a
1232         *      actor port of a actor or a stand-alone port.
1233         * @param tailIsActorPort A flag that specifies whether this is a
1234         *      actor port of a actor or a stand-alone port.
1235         * @return The new location.
1236         */
1237        private double[] _getNewLocation(NamedObj semanticHead,
1238                NamedObj semanticTail, boolean headIsActorPort,
1239                boolean tailIsActorPort) {
1240            double[] headLocation = _getLocation(
1241                    headIsActorPort ? semanticHead.getContainer()
1242                            : semanticHead).getLocation();
1243            double[] tailLocation = _getLocation(
1244                    tailIsActorPort ? semanticTail.getContainer()
1245                            : semanticTail).getLocation();
1246            double[] newLocation = new double[2];
1247            newLocation[0] = (headLocation[0] + tailLocation[0]) / 2.0;
1248            newLocation[1] = (headLocation[1] + tailLocation[1]) / 2.0;
1249            newLocation = SnapConstraint.constrainPoint(newLocation);
1250            return newLocation;
1251        }
1252
1253        /** Append moml to the given buffer that connects a link with the
1254         *  given head and tail.  Names in the moml that is written will be
1255         *  relative to the given container.  This may require adding a
1256         *  vertex to the ptolemy model.
1257         *  If no vertex is added, then return null.
1258         *  @param container The container composite actor.
1259         *  @param moml The string buffer to write the MoML to.
1260         *  @param failmoml The string buffer to write alternative
1261         *   MoML to, to be used if the first MoML fails.
1262         *  @param linkHead The head vertex or port.
1263         *  @param linkTail The tail vertex or port.
1264         *  @return The MoML to establish a link, or null if no vertex is added.
1265         */
1266        private String _linkMoML(NamedObj container, StringBuffer moml,
1267                StringBuffer failmoml, NamedObj linkHead, NamedObj linkTail)
1268                throws Exception {
1269            if (linkHead != null && linkTail != null) {
1270                NamedObj head = (NamedObj) getSemanticObject(linkHead);
1271                NamedObj tail = (NamedObj) getSemanticObject(linkTail);
1272
1273                if (head instanceof ComponentPort
1274                        && tail instanceof ComponentPort) {
1275                    ComponentPort headPort = (ComponentPort) head;
1276                    ComponentPort tailPort = (ComponentPort) tail;
1277                    NamedObj ptolemyModel = getPtolemyModel();
1278
1279                    // Linking two ports with a new relation.
1280                    String relationName = ptolemyModel.uniqueName("relation");
1281
1282                    // If the context is not the entity that we're editing,
1283                    // then we need to set the context correctly.
1284                    if (ptolemyModel != container) {
1285                        String contextString = "<entity name=\""
1286                                + ptolemyModel.getName(container) + "\">\n";
1287                        moml.append(contextString);
1288                        failmoml.append(contextString);
1289                    }
1290
1291                    // Note that we use no class so that we use the container's
1292                    // factory method when this gets parsed
1293                    moml.append("<relation name=\"" + relationName + "\"/>\n");
1294                    moml.append("<link port=\"" + headPort.getName(ptolemyModel)
1295                            + "\" relation=\"" + relationName + "\"/>\n");
1296                    moml.append("<link port=\"" + tailPort.getName(ptolemyModel)
1297                            + "\" relation=\"" + relationName + "\"/>\n");
1298
1299                    // Record moml so that we can blow away these
1300                    // links in case we can't create them
1301                    failmoml.append("<unlink port=\""
1302                            + headPort.getName(ptolemyModel) + "\" relation=\""
1303                            + relationName + "\"/>\n");
1304                    failmoml.append("<unlink port=\""
1305                            + tailPort.getName(ptolemyModel) + "\" relation=\""
1306                            + relationName + "\"/>\n");
1307                    failmoml.append("<deleteRelation name=\"" + relationName
1308                            + "\"/>\n");
1309
1310                    // close the context
1311                    if (ptolemyModel != container) {
1312                        moml.append("</entity>");
1313                        failmoml.append("</entity>");
1314                    }
1315
1316                    // Ugh this is ugly.
1317                    if (ptolemyModel != container) {
1318                        return ptolemyModel.getName(container) + "."
1319                                + relationName;
1320                    } else {
1321                        return relationName;
1322                    }
1323                } else if (head instanceof ComponentPort
1324                        && linkTail instanceof Vertex) {
1325                    // Linking a port to an existing relation.
1326                    moml.append("<link port=\"" + head.getName(container)
1327                            + "\" relation=\"" + tail.getName(container)
1328                            + "\"/>\n");
1329                    return tail.getName(container);
1330                } else if (tail instanceof ComponentPort
1331                        && linkHead instanceof Vertex) {
1332                    // Linking a port to an existing relation.
1333                    moml.append("<link port=\"" + tail.getName(container)
1334                            + "\" relation=\"" + head.getName(container)
1335                            + "\"/>\n");
1336                    return head.getName(container);
1337                } else if (linkHead instanceof Vertex
1338                        && linkTail instanceof Vertex) {
1339                    moml.append("<link relation1=\"" + tail.getName(container)
1340                            + "\" relation2=\"" + head.getName(container)
1341                            + "\"/>\n");
1342                    return head.getName(container);
1343                } else {
1344                    throw new RuntimeException("Link failed: " + "Head = "
1345                            + head + ", Tail = " + tail);
1346                }
1347            } else {
1348                // No Linking to do.
1349                return null;
1350            }
1351        }
1352
1353        /** Append moml to the given buffer that connects a relation with the
1354         *  given semanticObject. If relationIndex equals -1 it will
1355         *  be ignored, otherwise the relation will be connected at index relationIndex at
1356         *  the semanticObject, in case it represents a port.
1357         *  @param moml The string buffer to write the MoML to.
1358         *  @param failmoml The string buffer to write alternative
1359         *   MoML to, to be used if the first MoML fails.
1360         *  @param container The container composite actor.
1361         *  @param semanticObject The semantic object (relation or port).
1362         *  @param relationIndex The index of the relation at the port.
1363         *  @param relationName The name of the relation.
1364         */
1365        private void _linkWithRelation(final StringBuffer moml,
1366                final StringBuffer failmoml, final CompositeEntity container,
1367                NamedObj semanticObject, int relationIndex,
1368                String relationName) {
1369
1370            if (semanticObject instanceof ComponentPort) {
1371                moml.append("<link port=\"" + semanticObject.getName(container)
1372                        + "\" relation=\"" + relationName);
1373                if (relationIndex != -1) {
1374                    moml.append("\" insertAt=\"" + relationIndex);
1375                }
1376                moml.append("\"/>\n");
1377
1378                // Record moml so that we can blow away these
1379                // links in case we can't create them
1380                failmoml.append(
1381                        "<unlink port=\"" + semanticObject.getName(container)
1382                                + "\" relation=\"" + relationName + "\"/>\n");
1383            } else if (semanticObject instanceof Relation) {
1384                moml.append(
1385                        "<link relation1=\"" + semanticObject.getName(container)
1386                                + "\" relation2=\"" + relationName + "\"/>\n");
1387                failmoml.append("<unlink relation1=\""
1388                        + semanticObject.getName(container) + "\" relation2=\""
1389                        + relationName + "\"/>\n");
1390            } else {
1391                throw new RuntimeException("Link failed: " + "Object = "
1392                        + semanticObject + ", Relation = " + relationName);
1393            }
1394        }
1395
1396        /** Connect the given edge to the given head or tail node.
1397         *  This class queues a new change request with the ptolemy model
1398         *  to make this modification.
1399         *  @param edge The edge, which is assumed to be a link.
1400         *  @param newLinkHeadOrTail The new head or tail for the edge,
1401         *  which is assumed to be a location representing a port,
1402         *  a port or a vertex.
1403         *  @param isHead True when newLinkHeadOrTail represents the head
1404         *  @see #setHead(Object, Object)
1405         *  @see #setTail(Object, Object)
1406         */
1407        private void _setHeadOrTail(final Object edge,
1408                final Object newLinkHeadOrTail, final boolean isHead) {
1409            final Link link = (Link) edge;
1410            final NamedObj linkHead = (NamedObj) link.getHead();
1411            final NamedObj linkTail = (NamedObj) link.getTail();
1412            Relation linkRelation = link.getRelation();
1413
1414            // This moml is parsed to execute the change
1415            final StringBuffer moml = new StringBuffer();
1416
1417            // This moml is parsed in case the change fails.
1418            final StringBuffer failmoml = new StringBuffer();
1419            moml.append("<group>\n");
1420            failmoml.append("<group>\n");
1421
1422            // Make the request in the context of the container.
1423            final CompositeEntity container = (CompositeEntity) getPtolemyModel();
1424
1425            String relationName = "";
1426
1427            // Flag specifying whether we have actually created any MoML.
1428            boolean appendedMoML = false;
1429
1430            try {
1431                // create moml to unlink any existing.
1432                appendedMoML = _unlinkMoML(container, moml, linkHead, linkTail,
1433                        linkRelation);
1434
1435                // It is possible to link with an existing link.
1436                // If this existing link has a vertex as head or tail,
1437                // we will connect with the vertex, otherwise we will
1438                // remove the old link, create a new vertex, link the
1439                // head and tail of the existing link with the
1440                // vertex and link the new link with the vertex.
1441                if (newLinkHeadOrTail instanceof Link) {
1442
1443                    Link oldLink = (Link) newLinkHeadOrTail;
1444
1445                    NamedObj oldHead = (NamedObj) oldLink.getHead();
1446                    NamedObj oldTail = (NamedObj) oldLink.getTail();
1447
1448                    if (oldHead instanceof Vertex) {
1449                        // Link the new link with oldHead
1450                        // create moml to make the new links.
1451                        if (isHead) {
1452                            relationName = _linkMoML(container, moml, failmoml,
1453                                    oldHead, linkTail);
1454                        } else {
1455                            relationName = _linkMoML(container, moml, failmoml,
1456                                    linkHead, oldHead);
1457                        }
1458                    } else if (oldTail instanceof Vertex) {
1459                        // Link the new link with oldTail
1460                        // create moml to make the new links.
1461                        if (isHead) {
1462                            relationName = _linkMoML(container, moml, failmoml,
1463                                    oldTail, linkTail);
1464                        } else {
1465                            relationName = _linkMoML(container, moml, failmoml,
1466                                    linkHead, oldTail);
1467                        }
1468                    } else {
1469                        // Remove the old link, create a new vertex, link the
1470                        // head and tail of the existing link with the
1471                        // vertex and link the new link with the vertex.
1472
1473                        NamedObj oldHeadSemantic = (NamedObj) getSemanticObject(
1474                                oldHead);
1475                        NamedObj oldTailSemantic = (NamedObj) getSemanticObject(
1476                                oldTail);
1477
1478                        // In case the head is a port of an actor in the current composite
1479                        // actor the head will be an IOPort, if it is a port of the current
1480                        // composite actor it will be a Locatable
1481                        boolean headIsActorPort = oldHeadSemantic != null
1482                                ? oldLink.getHead() instanceof IOPort
1483                                : linkTail instanceof IOPort;
1484                        boolean tailIsActorPort = oldLink
1485                                .getTail() instanceof IOPort;
1486
1487                        final NamedObj toplevel = getPtolemyModel();
1488                        String newRelationName = toplevel
1489                                .uniqueName("relation");
1490
1491                        double[] newLocation = _getNewLocation(
1492                                oldHeadSemantic != null ? oldHeadSemantic
1493                                        : (NamedObj) getSemanticObject(
1494                                                linkTail),
1495                                oldTailSemantic, headIsActorPort,
1496                                tailIsActorPort);
1497
1498                        relationName = newRelationName;
1499
1500                        addNewVertexToLink(moml, failmoml, container, oldLink,
1501                                newRelationName, newLocation[0],
1502                                newLocation[1]);
1503
1504                        if (isHead) {
1505                            _linkWithRelation(moml, failmoml, container,
1506                                    (NamedObj) getSemanticObject(linkTail), -1,
1507                                    newRelationName);
1508                        } else {
1509                            _linkWithRelation(moml, failmoml, container,
1510                                    (NamedObj) getSemanticObject(linkHead), -1,
1511                                    newRelationName);
1512                        }
1513
1514                        failmoml.append("<deleteRelation name=\""
1515                                + newRelationName + "\"/>\n");
1516
1517                        appendedMoML = true;
1518                    }
1519                } else {
1520                    // create moml to make the new links.
1521                    if (isHead) {
1522                        relationName = _linkMoML(container, moml, failmoml,
1523                                (NamedObj) newLinkHeadOrTail, linkTail);
1524                    } else {
1525                        relationName = _linkMoML(container, moml, failmoml,
1526                                linkHead, (NamedObj) newLinkHeadOrTail);
1527                    }
1528                }
1529
1530                // FIXME: Above can return an empty name, so the following
1531                // test is not quite right.
1532                appendedMoML = appendedMoML || relationName != null;
1533            } catch (Exception ex) {
1534                // The link is bad... remove it.
1535                _linkSet.remove(link);
1536                link.setHead(null);
1537                link.setTail(null);
1538                dispatchGraphEvent(new GraphEvent(ActorGraphModel.this,
1539                        GraphEvent.STRUCTURE_CHANGED, getRoot()));
1540            }
1541
1542            moml.append("</group>\n");
1543            failmoml.append("</group>\n");
1544
1545            final String relationNameToAdd = relationName;
1546            final boolean nonEmptyMoML = appendedMoML;
1547
1548            // Here the source IS the graph model, because we need to
1549            // handle the event dispatch specially:  An event is only
1550            // dispatched if both the head and the tail are attached.
1551            // This rather obnoxious hack is here because edge creation
1552            // is tricky and we can't rerender the edge while we are dragging
1553            // it.
1554            MoMLChangeRequest request = new MoMLChangeRequest(
1555                    ActorGraphModel.this, container, moml.toString()) {
1556                @Override
1557                protected void _execute() throws Exception {
1558                    // If nonEmptyMoML is false, then the MoML code is empty.
1559                    // Do not execute it, as this will put spurious empty
1560                    // junk on the undo stack.
1561                    if (nonEmptyMoML) {
1562                        super._execute();
1563                    }
1564
1565                    // It is possible to link with an existing link.
1566                    // If this existing link has a vertex as head or tail,
1567                    // we will connect with the vertex, otherwise we will
1568                    // remove the old link, create a new vertex, link the
1569                    // head and tail of the existing link with the
1570                    // vertex and link the new link with the vertex.
1571
1572                    if (!(newLinkHeadOrTail instanceof Link)) {
1573                        if (isHead) {
1574                            link.setHead(newLinkHeadOrTail);
1575                        } else {
1576                            link.setTail(newLinkHeadOrTail);
1577                        }
1578                    } else {
1579                        // Make sure that the model is updated. We made structural
1580                        // changes that impose an update of this ActorGraphModel.
1581                        _forceUpdate = true;
1582
1583                        // Set head/tail equal to newly created vertex.
1584                        if (isHead) {
1585                            link.setHead(_getLocation(
1586                                    container.getRelation(relationNameToAdd)));
1587                        } else {
1588                            Object relation = container
1589                                    .getRelation(relationNameToAdd);
1590                            if (relation == null) {
1591                                throw new NullPointerException(
1592                                        "Getting the relation \""
1593                                                + relationNameToAdd + "\" in "
1594                                                + container.getFullName()
1595                                                + " returned null?");
1596                            }
1597                        }
1598                    }
1599
1600                    if (relationNameToAdd != null) {
1601                        ComponentRelation relation = container
1602                                .getRelation(relationNameToAdd);
1603
1604                        if (relation == null) {
1605                            throw new InternalErrorException(
1606                                    "Tried to find relation with name "
1607                                            + relationNameToAdd + " in context "
1608                                            + container);
1609                        }
1610
1611                        link.setRelation(relation);
1612                    } else {
1613                        link.setRelation(null);
1614                    }
1615                }
1616            };
1617
1618            // Handle what happens if the mutation fails.
1619            request.addChangeListener(
1620                    new LinkChangeListener(link, container, failmoml));
1621
1622            request.setUndoable(true);
1623            container.requestChange(request);
1624        }
1625
1626        /** Append moml to the given buffer that disconnects a link with the
1627         *  given head, tail, and relation. Names in the returned moml will be
1628         *  relative to the given container. If either linkHead or linkTail
1629         *  is null, then nothing will be appended to the moml buffer.
1630         *  @return True if any MoML is appended to the moml argument.
1631         */
1632        private boolean _unlinkMoML(NamedObj container, StringBuffer moml,
1633                NamedObj linkHead, NamedObj linkTail, Relation relation) {
1634            // If the link is already connected, then create a bit of MoML
1635            // to unlink the link.
1636            if (linkHead != null && linkTail != null) {
1637                NamedObj head = (NamedObj) getSemanticObject(linkHead);
1638                NamedObj tail = (NamedObj) getSemanticObject(linkTail);
1639
1640                if (head instanceof ComponentPort
1641                        && tail instanceof ComponentPort) {
1642                    ComponentPort headPort = (ComponentPort) head;
1643                    ComponentPort tailPort = (ComponentPort) tail;
1644
1645                    // Unlinking two ports with an anonymous relation.
1646                    moml.append("<unlink port=\"" + headPort.getName(container)
1647                            + "\" relation=\"" + relation.getName(container)
1648                            + "\"/>\n");
1649                    moml.append("<unlink port=\"" + tailPort.getName(container)
1650                            + "\" relation=\"" + relation.getName(container)
1651                            + "\"/>\n");
1652                    moml.append("<deleteRelation name=\""
1653                            + relation.getName(container) + "\"/>\n");
1654                } else if (head instanceof ComponentPort
1655                        && linkTail instanceof Vertex) {
1656                    // Unlinking a port from an existing relation.
1657                    moml.append("<unlink port=\"" + head.getName(container)
1658                            + "\" relation=\"" + tail.getName(container)
1659                            + "\"/>\n");
1660                } else if (tail instanceof ComponentPort
1661                        && linkHead instanceof Vertex) {
1662                    // Unlinking a port from an existing relation.
1663                    moml.append("<unlink port=\"" + tail.getName(container)
1664                            + "\" relation=\"" + head.getName(container)
1665                            + "\"/>\n");
1666                } else if (linkHead instanceof Vertex
1667                        && linkTail instanceof Vertex) {
1668                    moml.append("<unlink relation1=\"" + tail.getName(container)
1669                            + "\" relation2=\"" + head.getName(container)
1670                            + "\"/>\n");
1671                } else {
1672                    throw new RuntimeException("Unlink failed: " + "Head = "
1673                            + head + ", Tail = " + tail);
1674                }
1675
1676                return true;
1677            } else {
1678                // No unlinking to do.
1679                return false;
1680            }
1681        }
1682
1683        /** This change listener is responsible for dispatching graph events
1684         *  when an edge is moved.  It works the same for heads and tails.
1685         */
1686        public class LinkChangeListener implements ChangeListener {
1687            /** Construct a link change listener.
1688             *  @param link The link.
1689             *  @param container The container.
1690             *  @param failMoML MoML that cleans up the model if the
1691             *  change request fails.
1692             */
1693            public LinkChangeListener(Link link, CompositeEntity container,
1694                    StringBuffer failMoML) {
1695                _link = link;
1696                _container = container;
1697                _failMoML = failMoML;
1698            }
1699
1700            /** Handled a failed change request.
1701             *  @param change  The change request.
1702             *  @param exception The exception.
1703             */
1704            @Override
1705            public void changeFailed(ChangeRequest change,
1706                    Exception exception) {
1707                // If we fail here, then we remove the link entirely.
1708                _linkSet.remove(_link);
1709                _link.setHead(null);
1710                _link.setTail(null);
1711                _link.setRelation(null);
1712
1713                // and queue a new change request to clean up the model
1714                // Note: JDK1.2.2 requires that this variable not be
1715                // called request or we get a compile error.
1716                // Note the source is NOT the graph model
1717                MoMLChangeRequest changeRequest = new MoMLChangeRequest(this,
1718                        _container, _failMoML.toString());
1719
1720                // fail moml not undoable
1721                _container.requestChange(changeRequest);
1722            }
1723
1724            /** Called after the change has been executed.
1725             *  @param change The change request.
1726             */
1727            @Override
1728            public void changeExecuted(ChangeRequest change) {
1729                // modification to the linkset HAS to occur in the swing
1730                // thread.
1731                if (GraphUtilities.isPartiallyContainedEdge(_link, getRoot(),
1732                        ActorGraphModel.this)) {
1733                    _linkSet.add(_link);
1734                } else {
1735                    _linkSet.remove(_link);
1736                }
1737
1738                // Note that there is no GraphEvent dispatched here
1739                // if the edge is not fully connected.  This is to
1740                // prevent rerendering while
1741                // an edge is being created.
1742                if (_link.getHead() != null && _link.getTail() != null) {
1743                    dispatchGraphEvent(new GraphEvent(ActorGraphModel.this,
1744                            GraphEvent.STRUCTURE_CHANGED, getRoot()));
1745                }
1746            }
1747
1748            private Link _link;
1749
1750            private CompositeEntity _container;
1751
1752            private StringBuffer _failMoML;
1753        }
1754    }
1755
1756    /** The model for ports that are contained in icons in this graph.
1757     */
1758    public class PortModel extends NamedObjNodeModel {
1759        /** Return a MoML String that will delete the given node from the
1760         *  Ptolemy model. This assumes that the context is the container
1761         *  of the port.
1762         *  @param node The node.
1763         *  @return A valid MoML string.
1764         */
1765        @Override
1766        public String getDeleteNodeMoML(Object node) {
1767            NamedObj deleteObj = ((Locatable) node).getContainer();
1768            NamedObj container = deleteObj.getContainer();
1769            ;
1770
1771            String moml = "<deletePort name=\"" + deleteObj.getName(container)
1772                    + "\"/>\n";
1773            return moml;
1774        }
1775
1776        /** Return the graph parent of the given node.
1777         *  @param node The node, which is assumed to be a port.
1778         *  @return The (presumably unique) icon contained in the port's
1779         *   container.
1780         */
1781        @Override
1782        public Object getParent(Object node) {
1783            ComponentPort port = (ComponentPort) node;
1784            Entity entity = (Entity) port.getContainer();
1785
1786            if (entity == null) {
1787                return null;
1788            }
1789
1790            List<?> locationList = entity.attributeList(Locatable.class);
1791
1792            if (locationList.size() > 0) {
1793                return locationList.get(0);
1794            } else {
1795                try {
1796                    // NOTE: We need the location right away, so we go ahead
1797                    // and create it and handle the propagation locally.
1798                    Location location = new Location(entity, "_location");
1799                    location.propagateExistence();
1800                    return location;
1801                } catch (Exception e) {
1802                    throw new InternalErrorException("Failed to create "
1803                            + "location, even though one does not exist:"
1804                            + e.getMessage());
1805                }
1806            }
1807        }
1808
1809        /** Return an iterator over the edges coming into the given node.
1810         *  This method first ensures that there is a link
1811         *  object for every link.  Then the iterator is constructed by
1812         *  removing any links that do not have the given node as head.
1813         *  @param node The node, which is assumed to be a port contained in
1814         *   the root of this graph model.
1815         *  @return An iterator of Link objects, all of which have their
1816         *   head as the given node.
1817         */
1818        @Override
1819        public Iterator inEdges(Object node) {
1820            ComponentPort port = (ComponentPort) node;
1821
1822            // Go through all the links, creating a list of
1823            // those we are connected to.
1824            List<Link> portLinkList = new LinkedList<Link>();
1825
1826            for (Link link : _linkSet) {
1827                Object head = link.getHead();
1828
1829                if (head != null && head.equals(port)) {
1830                    portLinkList.add(link);
1831                }
1832            }
1833
1834            return portLinkList.iterator();
1835        }
1836
1837        /** Return an iterator over the edges coming out of the given node.
1838         *  This iterator is constructed by looping over all the relations
1839         *  that the port is connected to, and ensuring that there is a link
1840         *  object for every link.  Then the iterator is constructed by
1841         *  removing any links that do not have the given node as tail.
1842         *  @param node The node, which is assumed to be a port contained in
1843         *   the root of this graph model.
1844         *  @return An iterator of Link objects, all of which have their
1845         *   tail as the given node.
1846         */
1847        @Override
1848        public Iterator outEdges(Object node) {
1849            ComponentPort port = (ComponentPort) node;
1850
1851            // Go through all the links, creating a list of
1852            // those we are connected to.
1853            List<Link> portLinkList = new LinkedList<Link>();
1854            for (Link link : _linkSet) {
1855                Object tail = link.getTail();
1856
1857                if (tail != null && tail.equals(port)) {
1858                    portLinkList.add(link);
1859                }
1860            }
1861
1862            return portLinkList.iterator();
1863        }
1864
1865        /** Remove the given node from the model.  The node is assumed
1866         *  to be a port.
1867         *  This class queues a new change request with the ptolemy model
1868         *  to make this modification.
1869         *  @param eventSource The source of the event that will be dispatched,
1870         *   e.g. the view that made this call.
1871         *  @param node The node.
1872         */
1873        @Override
1874        public void removeNode(final Object eventSource, Object node) {
1875            ComponentPort port = (ComponentPort) node;
1876            NamedObj container = port.getContainer();
1877
1878            // Delete the port.
1879            String moml = "<deletePort name=\"" + port.getName() + "\"/>\n";
1880
1881            // Note: The source is NOT the graph model.
1882            MoMLChangeRequest request = new MoMLChangeRequest(this, container,
1883                    moml);
1884            request.setUndoable(true);
1885            container.requestChange(request);
1886        }
1887    }
1888
1889    /** The model for vertexes that are contained within the relations of the
1890     *  ptolemy model.
1891     */
1892    public class VertexModel extends NamedObjNodeModel {
1893        /** Return a MoML String that will delete the given node from the
1894         *  Ptolemy model. This assumes that the context is the container
1895         *  of the vertex.
1896         *  @param node The node.
1897         *  @return A valid MoML string.
1898         */
1899        @Override
1900        public String getDeleteNodeMoML(Object node) {
1901            ComponentRelation deleteObj = (ComponentRelation) ((Vertex) node)
1902                    .getContainer();
1903            String moml = "<deleteRelation name=\"" + deleteObj.getName()
1904                    + "\"/>\n";
1905            return moml;
1906        }
1907
1908        /** Return the graph parent of the given node.
1909         *  @param node The node, which is assumed to be a Vertex.
1910         *  @return The container of the vertex's container, which is
1911         *   presumably the root of the graph model.
1912         */
1913        @Override
1914        public Object getParent(Object node) {
1915            // Undo: If we use automatic layout, then we need to check to
1916            // see if the container is null here.
1917            if (((Vertex) node).getContainer() == null) {
1918                return null;
1919            }
1920
1921            return ((Vertex) node).getContainer().getContainer();
1922        }
1923
1924        /** Return an iterator over the edges coming into the given node.
1925         *  This method ensures that there is a link object for
1926         *  every link to the relation contained by the vertex.
1927         *  Then the iterator is constructed by
1928         *  removing any links that do not have the given node as head.
1929         *  @param node The node, which is assumed to be a vertex contained in
1930         *   a relation.
1931         *  @return An iterator of Link objects, all of which have their
1932         *   head as the given node.
1933         */
1934        @Override
1935        public Iterator inEdges(Object node) {
1936            Vertex vertex = (Vertex) node;
1937
1938            // Go through all the links, creating a list of
1939            // those we are connected to.
1940            List<Link> vertexLinkList = new LinkedList<Link>();
1941
1942            for (Link link : _linkSet) {
1943                Object head = link.getHead();
1944
1945                if (head != null && head.equals(vertex)) {
1946                    vertexLinkList.add(link);
1947                }
1948            }
1949
1950            return vertexLinkList.iterator();
1951        }
1952
1953        /** Return an iterator over the edges coming into the given node.
1954         *  This method ensures that there is a link object for
1955         *  every link to the relation contained by the vertex.
1956         *  Then the iterator is constructed by
1957         *  removing any links that do not have the given node as head.
1958         *  @param node The node, which is assumed to be a vertex contained in
1959         *   a relation.
1960         *  @return An iterator of Link objects, all of which have their
1961         *   tail as the given node.
1962         */
1963        @Override
1964        public Iterator outEdges(Object node) {
1965            Vertex vertex = (Vertex) node;
1966
1967            // Go through all the links, creating a list of
1968            // those we are connected to.
1969            List<Link> vertexLinkList = new LinkedList<Link>();
1970
1971            for (Link link : _linkSet) {
1972                Object tail = link.getTail();
1973
1974                if (tail != null && tail.equals(vertex)) {
1975                    vertexLinkList.add(link);
1976                }
1977            }
1978
1979            return vertexLinkList.iterator();
1980        }
1981
1982        /** Remove the given node from the model.  The node is assumed
1983         *  to be a vertex contained by a relation.
1984         *  This class queues a new change request with the ptolemy model
1985         *  to make this modification.
1986         *  @param eventSource The source of the event that will be dispatched,
1987         *   e.g. the view that made this call.
1988         *  @param node The node.
1989         */
1990        @Override
1991        public void removeNode(final Object eventSource, Object node) {
1992            ComponentRelation relation = (ComponentRelation) ((Vertex) node)
1993                    .getContainer();
1994            NamedObj container = relation.getContainer();
1995
1996            // Delete the relation.
1997            String moml = "<deleteRelation name=\"" + relation.getName()
1998                    + "\"/>\n";
1999
2000            // Note: The source is NOT the graph mode.
2001            MoMLChangeRequest request = new MoMLChangeRequest(this, container,
2002                    moml);
2003            request.setUndoable(true);
2004            container.requestChange(request);
2005        }
2006    }
2007}