001/* A graph model for basic ptolemy models.
002
003 Copyright (c) 2003-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.kernel;
029
030import java.util.Iterator;
031import java.util.LinkedList;
032import java.util.List;
033
034import diva.graph.modular.CompositeModel;
035import ptolemy.actor.Director;
036import ptolemy.kernel.ComponentEntity;
037import ptolemy.kernel.ComponentPort;
038import ptolemy.kernel.ComponentRelation;
039import ptolemy.kernel.CompositeEntity;
040import ptolemy.kernel.Entity;
041import ptolemy.kernel.util.Attribute;
042import ptolemy.kernel.util.InternalErrorException;
043import ptolemy.kernel.util.Locatable;
044import ptolemy.kernel.util.Location;
045import ptolemy.kernel.util.NamedObj;
046import ptolemy.moml.Vertex;
047
048///////////////////////////////////////////////////////////////////
049//// CompositePtolemyModel
050
051/**
052 A diva node model for a Ptolemy II composite entity. Each element of
053 the graph model is represented by an instance of Locatable, which is
054 an attribute contained by a Ptolemy II object.  If a Ptolemy II object
055 is found that does not contain a Locatable, then one is created if needed.
056 The graph model consists of locations for various elements in the composite.
057 In particular, one location will be included for each contained entity, port,
058 director, and visible attribute.  In each case except visible attributes,
059 if there is no location, then a default location is created.
060 Visible attributes are included in the graph only if they already
061 contain a location. In addition, for any relation that links more
062 than two ports and does not contain a Vertex, this class will
063 create a Vertex.
064
065 @author Steve Neuendorffer and Edward A. Lee
066 @version $Id$
067 @since Ptolemy II 4.0
068 @Pt.ProposedRating Yellow (neuendor)
069 @Pt.AcceptedRating Red (johnr)
070 @see ptolemy.kernel.util.Location
071 */
072public class CompositePtolemyModel implements CompositeModel {
073    ///////////////////////////////////////////////////////////////////
074    ////                         public methods                    ////
075
076    /** Return the number of nodes contained in the graph for the
077     *  specified composite.  If the argument is not an instance of
078     *  CompositeEntity, then return 0.
079     *  @param composite A composite entity.
080     *  @return The number of nodes in the graph representing the
081     *   specified composite entity.
082     */
083    @Override
084    public int getNodeCount(Object composite) {
085        if (!(composite instanceof NamedObj)) {
086            return 0;
087        }
088
089        long version = ((NamedObj) composite).workspace().getVersion();
090
091        if (_nodeList == null || composite != _composite
092                || version != _version) {
093            _nodeList = _nodeList((NamedObj) composite, true, true);
094            _composite = composite;
095            _version = version;
096        }
097
098        return _nodeList.size();
099    }
100
101    /** Return an iterator over all the nodes contained in the graph
102     *  for the specified composite. If the argument is not an
103     *  instance of CompositeEntity, then return an empty iterator.
104     *  @param composite A composite entity.
105     *  @return An iterator over nodes in the graph representing the
106     *   specified composite entity.
107     */
108    @Override
109    public Iterator nodes(Object composite) {
110        if (!(composite instanceof NamedObj)) {
111            return new LinkedList().iterator();
112        }
113
114        long version = ((NamedObj) composite).workspace().getVersion();
115
116        if (_nodeList == null || composite != _composite
117                || version != _version) {
118            _nodeList = _nodeList((NamedObj) composite, true, true);
119            _composite = composite;
120            _version = version;
121        }
122
123        return _nodeList.iterator();
124    }
125
126    /** Return an iterator over the nodes that should
127     *  be rendered prior to the edges. This iterator
128     *  does not necessarily support removal operations.
129     *  @param composite The composite.
130     *  @return An iterator over the nodes to be rendered
131     *   prior to the edges.
132     */
133    @Override
134    public Iterator nodesBeforeEdges(Object composite) {
135        if (!(composite instanceof NamedObj)) {
136            return new LinkedList().iterator();
137        }
138
139        long version = ((NamedObj) composite).workspace().getVersion();
140
141        if (_nodeListBefore == null || composite != _compositeBefore
142                || version != _versionBefore) {
143            _nodeListBefore = _nodeList((NamedObj) composite, true, false);
144            _compositeBefore = composite;
145            _versionBefore = version;
146        }
147
148        return _nodeListBefore.iterator();
149    }
150
151    /** Return an iterator over the nodes that should
152     *  be rendered after to the edges. This iterator
153     *  does not necessarily support removal operations.
154     *  @param composite The composite.
155     *  @return An iterator over the nodes to be rendered
156     *   after to the edges.
157     */
158    @Override
159    public Iterator nodesAfterEdges(Object composite) {
160        if (!(composite instanceof NamedObj)) {
161            return new LinkedList().iterator();
162        }
163
164        long version = ((NamedObj) composite).workspace().getVersion();
165
166        if (_nodeListAfter == null || composite != _compositeAfter
167                || version != _versionAfter) {
168            _nodeListAfter = _nodeList((NamedObj) composite, false, true);
169            _compositeAfter = composite;
170            _versionAfter = version;
171        }
172
173        return _nodeListAfter.iterator();
174    }
175
176    ///////////////////////////////////////////////////////////////////
177    ////                         protected methods                 ////
178
179    /** Return the location attribute contained in the given object, or
180     *  a new location contained in the given object if there was no location.
181     *  @param object The object for which a location is needed.
182     *  @return The location of the object, or a new location if none.
183     */
184    protected Locatable _getLocation(NamedObj object) {
185        List locations = object.attributeList(Locatable.class);
186
187        if (locations.size() > 0) {
188            return (Locatable) locations.get(0);
189        } else {
190            try {
191                // NOTE: We need the location right away, so we go ahead
192                // and create it. However, we also issue a MoMLChangeRequest
193                // so that the change propagates, and any models that defer
194                // to this one (e.g. subclasses) also have locations.
195                // This is necessary so that if the location later moves,
196                // then the move can be duplicated in the deferrers.
197                Location location = new Location(object, "_location");
198
199                // Since this isn't delegated to the MoML parser,
200                // we have to handle propagation here.
201                location.propagateExistence();
202
203                return location;
204            } catch (Exception e) {
205                throw new InternalErrorException(object, e, "Failed to create "
206                        + "location, even though one does not exist.");
207            }
208        }
209    }
210
211    ///////////////////////////////////////////////////////////////////
212    ////                         private variables                 ////
213
214    /** Return a list of all the nodes in the graph corresponding to
215     *  the specified Ptolemy II model.  If the <i>before</i> and
216     *  <i>after</i> arguments are both true, then all nodes are
217     *  returned. If only <i>before</i> is true, then all nodes except
218     *  attributes that contain an attribute named "_renderLast" are
219     *  returned.  If only <i>after</i> is true, then only attributes
220     *  that contain an attribute named "_renderLast" are
221     *  returned. The model can be any NamedObj, and the returned list
222     *  will include attributes that contain an attribute named
223     *  "_renderFirst", followed by entities, ports, vertexes,
224     *  followed by attributes that contain neither "_renderFirst" nor
225     *  "_renderLast", in that order.  Note that this method creates a
226     *  new list, and should therefore only be called if the object
227     *  has changed.
228     *  @param composite The composite entity.
229     *  @param before True to include nodes to be rendered before edges.
230     *  @param after True to include nodes to be rendered after edges.
231     *  @return A list of the nodes in the graph.
232     */
233    private List _nodeList(NamedObj composite, boolean before, boolean after) {
234        List nodes = new LinkedList();
235        try {
236            composite.workspace().getReadAccess();
237
238            if (before) {
239                // Add a node for visible attributes that contains
240                // an attribute named "_renderFirst".  An attribute
241                // is a visible attribute if it contains an instance
242                // of Locatable.
243                Iterator attributes = composite.attributeList().iterator();
244
245                while (attributes.hasNext()) {
246                    Attribute attribute = (Attribute) attributes.next();
247                    List locations = attribute.attributeList(Locatable.class);
248
249                    if (locations.size() > 0
250                            && attribute.getAttribute("_renderFirst") != null) {
251                        nodes.add(locations.get(0));
252                    }
253                }
254
255                if (composite instanceof CompositeEntity) {
256                    // Add a graph node for every class definition.
257                    // The node is actually the location contained by the entity.
258                    // If the entity does not contain a location, then create one.
259                    Iterator classes = ((CompositeEntity) composite)
260                            .classDefinitionList().iterator();
261
262                    while (classes.hasNext()) {
263                        ComponentEntity entity = (ComponentEntity) classes
264                                .next();
265                        nodes.add(_getLocation(entity));
266                    }
267
268                    // Add a graph node for every entity.
269                    // The node is actually the location contained by the entity.
270                    // If the entity does not contain a location, then create one.
271                    Iterator entities = ((CompositeEntity) composite)
272                            .entityList().iterator();
273
274                    while (entities.hasNext()) {
275                        ComponentEntity entity = (ComponentEntity) entities
276                                .next();
277                        nodes.add(_getLocation(entity));
278                    }
279                }
280
281                if (composite instanceof Entity) {
282                    // Add a graph node for every external port.
283                    // The node is actually the location contained by the port.
284                    // If the port does not contain a location, then create one.
285                    Iterator ports = ((Entity) composite).portList().iterator();
286
287                    while (ports.hasNext()) {
288                        ComponentPort port = (ComponentPort) ports.next();
289                        nodes.add(_getLocation(port));
290                    }
291                }
292
293                if (composite instanceof CompositeEntity) {
294                    // Add a node for every relation that has a vertex and
295                    // doesn't connect exactly two ports.
296                    // NOTE: This particular part of the graph model is irrelevant
297                    // for FSMs, but it is harmless to include it, so there is no
298                    // real need to subclass this to remove it.
299                    Iterator relations = ((CompositeEntity) composite)
300                            .relationList().iterator();
301
302                    while (relations.hasNext()) {
303                        ComponentRelation relation = (ComponentRelation) relations
304                                .next();
305                        List vertexList = relation.attributeList(Vertex.class);
306
307                        if (vertexList.size() != 0) {
308                            // Add in all the vertexes.
309                            Iterator vertexes = vertexList.iterator();
310
311                            while (vertexes.hasNext()) {
312                                Vertex v = (Vertex) vertexes.next();
313                                nodes.add(v);
314                            }
315                        } else {
316                            // See if we need to create a vertex.
317                            // Count the linked ports.
318                            int count = relation.linkedPortList().size();
319
320                            if (count != 2) {
321                                // A vertex is needed, so create one.
322                                try {
323                                    String name = relation.uniqueName("vertex");
324                                    Vertex vertex = new Vertex(relation, name);
325                                    nodes.add(vertex);
326
327                                    // Have to manually handle propagation, since
328                                    // the MoML parser is not involved.
329                                    // FIXME: Could get name collision here!
330                                    // (Unlikely though since auto naming will take
331                                    // into account subclasses).
332                                    vertex.propagateExistence();
333                                } catch (Throwable throwable) {
334                                    throw new InternalErrorException(null,
335                                            throwable,
336                                            "Failed to create a vertex!");
337                                }
338                            }
339                        }
340                    }
341                }
342
343                // Add a node for every director or visible attribute.
344                // The node is again the location.
345                // For directors, if there is no location, then create one.
346                // For visible attributes, add them only if they already
347                // create a location.
348                attributes = composite.attributeList().iterator();
349
350                while (attributes.hasNext()) {
351                    Attribute attribute = (Attribute) attributes.next();
352
353                    if (attribute.getAttribute("_renderFirst") != null
354                            || attribute.getAttribute("_renderLast") != null) {
355                        // Already rendered, or to be rendered later.
356                        continue;
357                    }
358
359                    if (attribute instanceof Director) {
360                        nodes.add(_getLocation(attribute));
361                    } else {
362                        // The object is not a director, so only give a location
363                        // if one exists already.
364                        List locations = attribute
365                                .attributeList(Locatable.class);
366
367                        if (locations.size() > 0) {
368                            nodes.add(locations.get(0));
369                        }
370                    }
371                }
372            }
373
374            if (after) {
375                // Add a node for visible attributes that contains
376                // an attribute named "_renderLast".  An attribute
377                // is a visible attribute if it contains an instance
378                // of Locatable.
379                Iterator attributes = composite.attributeList().iterator();
380
381                while (attributes.hasNext()) {
382                    Attribute attribute = (Attribute) attributes.next();
383                    List locations = attribute.attributeList(Locatable.class);
384
385                    if (locations.size() > 0
386                            && attribute.getAttribute("_renderLast") != null) {
387                        nodes.add(locations.get(0));
388                    }
389                }
390            }
391
392            // Return the final result.
393            return nodes;
394        } finally {
395            composite.workspace().doneReading();
396        }
397
398    }
399
400    ///////////////////////////////////////////////////////////////////
401    ////                         private variables                 ////
402    // The most recent object whose model was accessed.
403    private Object _composite;
404
405    // The most recent object whose model was accessed for a before list.
406    private Object _compositeBefore;
407
408    // The most recent object whose model was accessed for an after list.
409    private Object _compositeAfter;
410
411    // The cached complete list;
412    private List _nodeList;
413
414    // The cached list of nodes to be rendered before.
415    private List _nodeListBefore;
416
417    // The cached list of nodes to be rendered after.
418    private List _nodeListAfter;
419
420    // The workspace version for the cached list of nodes.
421    private long _version;
422
423    // The workspace version for the cached list of before nodes.
424    private long _versionBefore;
425
426    // The workspace version for the cached list of after nodes.
427    private long _versionAfter;
428}