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}