/* A mirror transformer for graphs. Copyright (c) 2003-2006 The University of Maryland. All rights reserved. Permission is hereby granted, without written agreement and without license or royalty fees, to use, copy, modify, and distribute this software and its documentation for any purpose, provided that the above copyright notice and the following two paragraphs appear in all copies of this software. IN NO EVENT SHALL THE UNIVERSITY OF MARYLAND BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF MARYLAND HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THE UNIVERSITY OF MARYLAND SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF MARYLAND HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. */ package ptolemy.graph.analysis.strategy; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Iterator; import ptolemy.graph.Edge; import ptolemy.graph.Graph; import ptolemy.graph.Node; import ptolemy.graph.analysis.AnalysisException; import ptolemy.graph.analysis.analyzer.MirrorTransformer; ////////////////////////////////////////////////////////////////////////// //// MirrorTransformerStrategy /** A mirror transformer for graphs. <p> In the {@link #cloneWeight} method, users can also specify whether to clone node and edge weights. For non cloneable weights a {@link java.lang.CloneNotSupportedException} will be thrown by the virtual machine. @since Ptolemy II 4.0 @Pt.ProposedRating Red (shahrooz) @Pt.AcceptedRating Red (ssb) @author Shahrooz Shahparnia based on a method by Ming Yung Ko. @version $Id$ */ public class MirrorTransformerStrategy extends CachedStrategy implements MirrorTransformer { /** Construct a transformer for a given graph. * @param graph The given graph. */ public MirrorTransformerStrategy(Graph graph) { super(graph); } /////////////////////////////////////////////////////////////////// //// public methods //// /** Changes the status of the graph returned by the {@link #mirror} method. * If true, the weights will also be cloned in the next calls to the * {@link #mirror} method. * * @param status If true, the weights will also be cloned. */ public void cloneWeight(boolean status) { _cloneWeights = status; } /** Specify if this transformation has a mapping from the transformed * version to the original version or not. * * @return True If the implementation of the transformer supports backward * mapping. */ public boolean hasBackwardMapping() { return true; } /** Specify if this transformation has a mapping from the original * version to the transformed version or not. * * @return True If the implementation of the transformer supports forward * mapping. */ public boolean hasForwardMapping() { return true; } /** Create a mirror of the graph associated with this analyzer with the * same runtime class. * * @return The mirror graph. */ public Graph mirror() { return mirror(graph(), _cloneWeights); } /** Return a mirror of this graph in the form of the argument graph type * (i.e., the run-time type of the returned graph is that of the * argument graph). The mirror and original graphs * are isomorphic (of same topology). However, nodes and edges * of the mirror are newly created and therefore not equal to * those of the original graph. * <p> * The returned mirror graph has the same ordering(integer labeling) * of nodes(edges) as the original graph. Therefore, correspondent * nodes(edges) pairs in both graphs can be gotten through same labels. * In other words, labels can also be used to relate mirror and original * nodes(edges). * <p> * * @param graph The type of the graph which the graph associated with * this analyzer is being mirrored to. * @param cloneWeights If true, the weights will also be cloned. * @return The mirror graph. */ public Graph mirror(Graph graph, boolean cloneWeights) { if ((graph.getClass() != graph().getClass()) || (cloneWeights != _cloneWeights)) { reset(); } _graph = graph; boolean tempCloneWeights = _cloneWeights; _cloneWeights = cloneWeights; Graph result = (Graph) _result(); _cloneWeights = tempCloneWeights; return result; } /** Return the original version of given object in the transformed graph. * * @param transformedObject The given object in the transformed graph. * @return Return the original version the given object. */ public Object originalVersionOf(Object transformedObject) { return _originalVersion.get(transformedObject); } /** Return the transformed version of a given object in the original graph. * * @param originalObject The given object in the original graph. * @return Return the transformed version the given object. */ public Object transformedVersionOf(Object originalObject) { return _transformedVersion.get(originalObject); } /** Always valid. * * @return True always. */ public boolean valid() { return true; } /////////////////////////////////////////////////////////////////// //// protected methods //// /** The computation associated with this strategy. * * @return The mirror graph as an {@link Object}. */ protected Object _compute() { String nameClone = "clone"; Graph mirrorGraph = null; try { // Kepler (jdk1.4?) requires this cast mirrorGraph = (_graph.getClass().newInstance()); } catch (Exception exception) { throw new RuntimeException("Could not create an empty graph from " + "this one.\n" + exception + "\n"); } // create new nodes for the mirror Iterator nodes = graph().nodes().iterator(); while (nodes.hasNext()) { Node node = (Node) nodes.next(); Node mirrorNode = null; if (!node.hasWeight()) { mirrorNode = new Node(); } else { Object mirrorWeight = null; try { // Clone weights of any type of object. if (_cloneWeights) { Object oldWeight = node.getWeight(); if (oldWeight instanceof Cloneable) { /* Since clone() of Object is protected, it can't be called publicly. The class Method is used here to call public clone(). */ Class[] argumentTypes = {}; Method method = oldWeight.getClass().getMethod( nameClone, argumentTypes); // Cast to (Object []) so as to avoid varargs call. mirrorWeight = method.invoke(oldWeight, (Object[]) null); } else { throw new RuntimeException(); } } else { mirrorWeight = node.getWeight(); } } catch (Throwable throwable) { /* Exception due to non-Cloneable weights or weights without public clone(). */ throw new AnalysisException( "Can not clone the node weight.\n", throwable); } mirrorNode = new Node(mirrorWeight); } mirrorGraph.addNode(mirrorNode); _originalVersion.put(mirrorNode, node); _transformedVersion.put(node, mirrorNode); } // create new edges for the mirror Iterator edges = graph().edges().iterator(); while (edges.hasNext()) { Edge edge = (Edge) edges.next(); Edge mirrorEdge = null; Node mirrorSource = (Node) _transformedVersion.get(edge.source()); Node mirrorSink = (Node) _transformedVersion.get(edge.sink()); if (!edge.hasWeight()) { mirrorEdge = new Edge(mirrorSource, mirrorSink); } else { Object mirrorWeight = null; try { // Clone weights of any type of object. if (_cloneWeights) { Object oldWeight = edge.getWeight(); if (oldWeight instanceof Cloneable) { /* Since clone() of Object is protected, it can't be called publicly. The class Method is used here to call public clone(). */ Class[] argumentTypes = {}; Method method = oldWeight.getClass().getMethod( nameClone, argumentTypes); // Cast to (Object []) so as to avoid varargs call. mirrorWeight = method.invoke(oldWeight, (Object[]) null); } else { throw new RuntimeException(); } } else { mirrorWeight = edge.getWeight(); } } catch (Throwable throwable) { /* Exception due to non-Cloneable weights or weights without public clone(). */ throw new RuntimeException( "Can not clone the edge weight.\n", throwable); } mirrorEdge = new Edge(mirrorSource, mirrorSink, mirrorWeight); } mirrorGraph.addEdge(mirrorEdge); _originalVersion.put(mirrorEdge, edge); _transformedVersion.put(edge, mirrorEdge); } return mirrorGraph; } /////////////////////////////////////////////////////////////////// //// private variables //// private Graph _graph; private boolean _cloneWeights = false; private HashMap _originalVersion = new HashMap(); private HashMap _transformedVersion = new HashMap(); }