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}