001/* Conversion of Ptolemy II models to generic weighted graphs. 002 003 Copyright (c) 2001-2013 The University of Maryland. All rights reserved. 004 Permission is hereby granted, without written agreement and without 005 license or royalty fees, to use, copy, modify, and distribute this 006 software and its documentation for any purpose, provided that the above 007 copyright notice and the following two paragraphs appear in all copies 008 of this software. 009 010 IN NO EVENT SHALL THE UNIVERSITY OF MARYLAND BE LIABLE TO ANY PARTY 011 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 012 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 013 THE UNIVERSITY OF MARYLAND HAS BEEN ADVISED OF THE POSSIBILITY OF 014 SUCH DAMAGE. 015 016 THE UNIVERSITY OF MARYLAND SPECIFICALLY DISCLAIMS ANY WARRANTIES, 017 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 018 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 019 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 020 MARYLAND HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 021 ENHANCEMENTS, OR MODIFICATIONS. 022 023 024 */ 025// In theory, this class could be in ptolemy.graph, but that would 026// set up a two way dependency between ptolemy.actor and ptolemy.graph. 027package ptolemy.actor; 028 029import java.util.HashMap; 030import java.util.Iterator; 031 032import ptolemy.graph.DirectedGraph; 033import ptolemy.graph.Edge; 034import ptolemy.graph.Graph; 035import ptolemy.graph.Node; 036 037/////////////////////////////////////////////////////////////////// 038//// GraphReader 039 040/** This class provides methods for converting Ptolemy II models 041 into generic graph representations. Portions of 042 this code are based on examples from [1]. 043 044 <p> 045 References<br> 046 [1] J. Davis et al., <em>Heterogeneous 047 concurrent modeling and design in Java</em>, Technical report, 048 Electronics Research Laboratory, University of California at Berkeley, 049 March 2001. 050 051 @author Shuvra S. Bhattacharyya, Chia-Jui Hsu 052 @version $Id$ 053 @since Ptolemy II 2.0 054 @Pt.ProposedRating Red (cxh) 055 @Pt.AcceptedRating Red (cxh) 056 */ 057public class GraphReader { 058 /** Construct a new graph reader. 059 */ 060 public GraphReader() { 061 _actorMap = new HashMap(); 062 } 063 064 /** Convert the model represented by a CompositeActor into a directed, 065 * weighted graph. Each node of the weighted graph will have as its 066 * weight the Ptolemy II AtomicActor that the node represents, 067 * and each edge will have as its weight the input port associated 068 * with the connection that the edge represents. These conventions 069 * for assigning node and edge weights can be changed in 070 * specialized graph reader classes by overriding the 071 * {@link ptolemy.actor.GraphReader#_computeNodeWeight(Actor)} and 072 * {@link ptolemy.actor.GraphReader#_computeEdgeWeight(IOPort sourcePort, 073 * IOPort sinkPort)} 074 * methods. 075 * This method will convert low level CompositeActor as a node. 076 * @param compositeActor The composite actor to convert. 077 * @return the directed, weighted graph. 078 * @exception RuntimeException If the deep entity list of the 079 * composite actor contains an entry that is not an AtomicActor. 080 */ 081 public Graph convert(CompositeActor compositeActor) { 082 // Instantiate an empty graph. 083 Graph graph = _initializeGraph(compositeActor); 084 085 // Add all deeply-contained actors to the graph 086 Iterator actors = compositeActor.deepEntityList().iterator(); 087 088 while (actors.hasNext()) { 089 Object entity = actors.next(); 090 091 if (entity instanceof AtomicActor 092 || entity instanceof CompositeActor) { 093 Actor actor = (Actor) entity; 094 Node newNode = graph.addNodeWeight(_computeNodeWeight(actor)); 095 _actorMap.put(actor, newNode); 096 _processNewNode(graph, newNode, actor); 097 } else { 098 throw new RuntimeException("Unsupported deep entity type: " 099 + entity.getClass().getName() + " (value = " + entity 100 + ")"); 101 } 102 } 103 104 // Convert each connection in the model to a graph edge 105 actors = compositeActor.deepEntityList().iterator(); 106 107 while (actors.hasNext()) { 108 Actor source = (Actor) actors.next(); 109 110 // Connect the current actor to each of its sinks 111 Iterator outPorts = source.outputPortList().iterator(); 112 113 while (outPorts.hasNext()) { 114 IOPort outPort = (IOPort) outPorts.next(); 115 Iterator inPorts = outPort.deepConnectedInPortList().iterator(); 116 117 while (inPorts.hasNext()) { 118 IOPort inPort = (IOPort) inPorts.next(); 119 Actor sink = (Actor) inPort.getContainer(); 120 121 if (graph.containsNode((Node) _actorMap.get(sink))) { 122 if (_debug) { 123 System.out.println("Adding edge from " + source 124 + " to " + sink); 125 } 126 127 Edge newEdge = graph.addEdge( 128 (Node) _actorMap.get(source), 129 (Node) _actorMap.get(sink), 130 _computeEdgeWeight(outPort, inPort)); 131 _processNewEdge(graph, newEdge, outPort, inPort); 132 } 133 } 134 } 135 } 136 137 // Perform global graph transformations. 138 _transformTopology(graph); 139 140 // Return the filled-in Graph. 141 return graph; 142 } 143 144 /////////////////////////////////////////////////////////////////// 145 //// protected methods //// 146 147 /** Determine the weight to be assigned to the weighted graph edge that 148 * represents a given connection in a Ptolemy II model. 149 * This method returns the input port as the edge weight. 150 * This method should be overridden by derived 151 * classes that have different edge weighting conventions. 152 * @param sourcePort the output port of the connection associated with 153 * the edge. 154 * @param sinkPort the input port of the connection associated with the 155 * edge 156 * @return the weight of the edge. 157 */ 158 protected Object _computeEdgeWeight(IOPort sourcePort, IOPort sinkPort) { 159 return sinkPort; 160 } 161 162 /** Determine the weight to be assigned to the weighted graph node that 163 * represents a given actor. This method returns the actor itself as the 164 * weight. This method should be overridden by derived 165 * classes that have different node weighting conventions. 166 * @param actor the actor whose node weight is to be determined. 167 * @return the weight of the node. 168 */ 169 protected Object _computeNodeWeight(Actor actor) { 170 return actor; 171 } 172 173 /** Instantiate and initialize a graph just prior to filling it 174 * in from a given Ptolemy II model. This is a pre-processing 175 * step to the conversion process that can be specialized 176 * based on the type of graph that is being read (e.g., 177 * the method can be overridden to allocate a specialized Graph 178 * object). In this 179 * base class, we simply instantiate an empty Graph and return it. 180 * @param compositeActor the Ptolemy II model that will be converted. 181 * @return the empty graph that is to hold the converted model. 182 */ 183 protected Graph _initializeGraph(CompositeActor compositeActor) { 184 return new DirectedGraph(); 185 } 186 187 /** Process a new edge corresponding to a given connection in a given graph. 188 * This method should be overridden to implement specialized conversion 189 * aspects that operate at the level of individual edges. 190 * @param graph The graph that contains the new edge. 191 * @param edge The new edge. 192 * @param sourcePort The source port of the connection in the model. 193 * @param sinkPort The sink port of the connection. 194 */ 195 protected void _processNewEdge(Graph graph, Edge edge, IOPort sourcePort, 196 IOPort sinkPort) { 197 return; 198 } 199 200 /** Process a new node corresponding to a given actor in a given graph. 201 * This method should be overridden to implement specialized conversion 202 * aspects that operate at the level of individual nodes. 203 * @param graph The graph that contains the new node. 204 * @param node The new node. 205 * @param actor The actor that corresponds to the new node. 206 */ 207 protected void _processNewNode(Graph graph, Node node, Actor actor) { 208 return; 209 } 210 211 /** Perform post-processing on the entire graph to complete the 212 * conversion. This method should be overridden by derived classes 213 * to implement specialized conversion aspects that operate 214 * at a global level (i.e., beyond the level of individual nodes and 215 * edges). 216 * @param graph the graph. 217 */ 218 protected void _transformTopology(Graph graph) { 219 if (_debug) { 220 System.out.println("A dump of the graph before global " 221 + "transformation:\n" + graph.toString() + "\n"); 222 } 223 224 //write transform strategy here. 225 if (_debug) { 226 System.out.println("A dump of the graph after global " 227 + "transformation:\n" + graph.toString() + "\n"); 228 } 229 } 230 231 /////////////////////////////////////////////////////////////////// 232 //// private variables //// 233 // Flag for turning local debugging output on and off. 234 private static final boolean _debug = false; 235 236 // Map from actors to the generic graph nodes that represent them. 237 // Keys are instances of AtomicActor, and values are instances of Node. 238 private HashMap _actorMap; 239}