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}