001/* A mirror transformer for graphs.
002
003 Copyright (c) 2003-2014 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 */
025package ptolemy.graph.analysis.strategy;
026
027import java.lang.reflect.Method;
028import java.util.HashMap;
029import java.util.Iterator;
030
031import ptolemy.graph.Edge;
032import ptolemy.graph.Graph;
033import ptolemy.graph.Node;
034import ptolemy.graph.analysis.AnalysisException;
035import ptolemy.graph.analysis.analyzer.MirrorTransformer;
036
037///////////////////////////////////////////////////////////////////
038//// MirrorTransformerStrategy
039
040/**
041 A mirror transformer for graphs.
042 <p>
043 In the {@link #cloneWeight} method, users can also specify whether to clone node
044 and edge weights. For non cloneable
045 weights a {@link java.lang.CloneNotSupportedException} will be thrown by
046 the virtual machine.
047
048 @since Ptolemy II 4.0
049 @Pt.ProposedRating Red (shahrooz)
050 @Pt.AcceptedRating Red (ssb)
051 @author Shahrooz Shahparnia based on a method by Ming Yung Ko.
052 @version $Id$
053 */
054public class MirrorTransformerStrategy extends CachedStrategy
055        implements MirrorTransformer {
056    /** Construct a transformer for a given graph.
057     *  @param graph The given graph.
058     */
059    public MirrorTransformerStrategy(Graph graph) {
060        super(graph);
061    }
062
063    ///////////////////////////////////////////////////////////////////
064    ////                         public methods                    ////
065
066    /** Changes the status of the graph returned by the {@link #mirror} method.
067     *  If true, the weights will also be cloned in the next calls to the
068     *  {@link #mirror} method.
069     *
070     *  @param status If true, the weights will also be cloned.
071     */
072    @Override
073    public void cloneWeight(boolean status) {
074        _cloneWeights = status;
075    }
076
077    /** Specify if this transformation has a mapping from the transformed
078     *  version to the original version or not.
079     *
080     *  @return True If the implementation of the transformer supports backward
081     *  mapping.
082     */
083    @Override
084    public boolean hasBackwardMapping() {
085        return true;
086    }
087
088    /** Specify if this transformation has a mapping from the original
089     *  version to the transformed version or not.
090     *
091     *  @return True If the implementation of the transformer supports forward
092     *  mapping.
093     */
094    @Override
095    public boolean hasForwardMapping() {
096        return true;
097    }
098
099    /** Create a mirror of the graph associated with this analyzer with the
100     *  same runtime class.
101     *
102     *  @return The mirror graph.
103     */
104    @Override
105    public Graph mirror() {
106        return mirror(graph(), _cloneWeights);
107    }
108
109    /** Return a mirror of this graph in the form of the argument graph type
110     *  (i.e., the run-time type of the returned graph is that of the
111     *  argument graph).  The mirror and original graphs
112     *  are isomorphic (of same topology). However, nodes and edges
113     *  of the mirror are newly created and therefore not equal to
114     *  those of the original graph.
115     *  <p>
116     *  The returned mirror graph has the same ordering(integer labeling)
117     *  of nodes(edges) as the original graph. Therefore, correspondent
118     *  nodes(edges) pairs in both graphs can be gotten through same labels.
119     *  In other words, labels can also be used to relate mirror and original
120     *  nodes(edges).
121     *  <p>
122     *
123     *  @param graph The type of the graph which the graph associated with
124     *  this analyzer is being mirrored to.
125     *  @param cloneWeights If true, the weights will also be cloned.
126     *  @return The mirror graph.
127     */
128    @Override
129    public Graph mirror(Graph graph, boolean cloneWeights) {
130        if (graph.getClass() != graph().getClass()
131                || cloneWeights != _cloneWeights) {
132            reset();
133        }
134
135        _graph = graph;
136
137        boolean tempCloneWeights = _cloneWeights;
138        _cloneWeights = cloneWeights;
139
140        Graph result = (Graph) _result();
141        _cloneWeights = tempCloneWeights;
142        return result;
143    }
144
145    /** Return the original version of given object in the transformed graph.
146     *
147     *  @param transformedObject The given object in the transformed graph.
148     *  @return Return the original version the given object.
149     */
150    @Override
151    public Object originalVersionOf(Object transformedObject) {
152        return _originalVersion.get(transformedObject);
153    }
154
155    /** Return the transformed version of a given object in the original graph.
156     *
157     *  @param originalObject The given object in the original graph.
158     *  @return Return the transformed version the given object.
159     */
160    @Override
161    public Object transformedVersionOf(Object originalObject) {
162        return _transformedVersion.get(originalObject);
163    }
164
165    /** Always valid.
166     *
167     *  @return True always.
168     */
169    @Override
170    public boolean valid() {
171        return true;
172    }
173
174    ///////////////////////////////////////////////////////////////////
175    ////                         protected methods                 ////
176
177    /** The computation associated with this strategy.
178     *
179     *  @return The mirror graph as an {@link Object}.
180     */
181    @Override
182    protected Object _compute() {
183        String nameClone = "clone";
184        Graph mirrorGraph = null;
185
186        try {
187            // Kepler (jdk1.4?) requires this cast
188            mirrorGraph = _graph.getClass().newInstance();
189        } catch (Exception exception) {
190            throw new RuntimeException("Could not create an empty graph from "
191                    + "this one.\n" + exception + "\n");
192        }
193
194        // create new nodes for the mirror
195        Iterator nodes = graph().nodes().iterator();
196
197        while (nodes.hasNext()) {
198            Node node = (Node) nodes.next();
199            Node mirrorNode = null;
200
201            if (!node.hasWeight()) {
202                mirrorNode = new Node();
203            } else {
204                Object mirrorWeight = null;
205
206                try {
207                    // Clone weights of any type of object.
208                    if (_cloneWeights) {
209                        Object oldWeight = node.getWeight();
210
211                        if (oldWeight instanceof Cloneable) {
212                            /* Since clone() of Object is protected, it can't
213                             be called publicly. The class Method is used
214                             here to call public clone(). */
215                            Class[] argumentTypes = {};
216                            Method method = oldWeight.getClass()
217                                    .getMethod(nameClone, argumentTypes);
218
219                            // Cast to (Object []) so as to avoid varargs call.
220                            mirrorWeight = method.invoke(oldWeight,
221                                    (Object[]) null);
222                        } else {
223                            throw new RuntimeException();
224                        }
225                    } else {
226                        mirrorWeight = node.getWeight();
227                    }
228                } catch (Throwable throwable) {
229                    /* Exception due to non-Cloneable weights or
230                     weights without public clone(). */
231                    throw new AnalysisException(
232                            "Can not clone the node weight.\n", throwable);
233                }
234
235                mirrorNode = new Node(mirrorWeight);
236            }
237
238            mirrorGraph.addNode(mirrorNode);
239            _originalVersion.put(mirrorNode, node);
240            _transformedVersion.put(node, mirrorNode);
241        }
242
243        // create new edges for the mirror
244        Iterator edges = graph().edges().iterator();
245
246        while (edges.hasNext()) {
247            Edge edge = (Edge) edges.next();
248            Edge mirrorEdge = null;
249            Node mirrorSource = (Node) _transformedVersion.get(edge.source());
250            Node mirrorSink = (Node) _transformedVersion.get(edge.sink());
251
252            if (!edge.hasWeight()) {
253                mirrorEdge = new Edge(mirrorSource, mirrorSink);
254            } else {
255                Object mirrorWeight = null;
256
257                try {
258                    // Clone weights of any type of object.
259                    if (_cloneWeights) {
260                        Object oldWeight = edge.getWeight();
261
262                        if (oldWeight instanceof Cloneable) {
263                            /* Since clone() of Object is protected, it can't
264                             be called publicly. The class Method is used
265                             here to call public clone(). */
266                            Class[] argumentTypes = {};
267                            Method method = oldWeight.getClass()
268                                    .getMethod(nameClone, argumentTypes);
269
270                            // Cast to (Object []) so as to avoid varargs call.
271                            mirrorWeight = method.invoke(oldWeight,
272                                    (Object[]) null);
273                        } else {
274                            throw new RuntimeException();
275                        }
276                    } else {
277                        mirrorWeight = edge.getWeight();
278                    }
279                } catch (Throwable throwable) {
280                    /* Exception due to non-Cloneable weights or
281                     weights without public clone(). */
282                    throw new RuntimeException(
283                            "Can not clone the edge weight.\n", throwable);
284                }
285
286                mirrorEdge = new Edge(mirrorSource, mirrorSink, mirrorWeight);
287            }
288
289            mirrorGraph.addEdge(mirrorEdge);
290            _originalVersion.put(mirrorEdge, edge);
291            _transformedVersion.put(edge, mirrorEdge);
292        }
293
294        return mirrorGraph;
295    }
296
297    ///////////////////////////////////////////////////////////////////
298    ////                         private variables                 ////
299    private Graph _graph;
300
301    private boolean _cloneWeights = false;
302
303    private HashMap _originalVersion = new HashMap();
304
305    private HashMap _transformedVersion = new HashMap();
306}