Clover coverage report - Drools - 2.0-rc2
Coverage timestamp: Wed May 11 2005 07:12:26 BST
file stats: LOC: 442   Methods: 22
NCLOC: 265   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
ReteooDotDumpVisitor.java 0% 0% 0% 0%
coverage
 1    package org.drools.reteoo;
 2   
 3    /*
 4    * $Id: ReteooDotDumpVisitor.java,v 1.11 2005/02/02 00:23:22 mproctor Exp $
 5    *
 6    * Copyright 2004-2004 (C) The Werken Company. All Rights Reserved.
 7    *
 8    * Redistribution and use of this software and associated documentation
 9    * ("Software"), with or without modification, are permitted provided that the
 10    * following conditions are met:
 11    *
 12    * 1. Redistributions of source code must retain copyright statements and
 13    * notices. Redistributions must also contain a copy of this document.
 14    *
 15    * 2. Redistributions in binary form must reproduce the above copyright notice,
 16    * this list of conditions and the following disclaimer in the documentation
 17    * and/or other materials provided with the distribution.
 18    *
 19    * 3. The name "drools" must not be used to endorse or promote products derived
 20    * from this Software without prior written permission of The Werken Company.
 21    * For written permission, please contact bob@werken.com.
 22    *
 23    * 4. Products derived from this Software may not be called "drools" nor may
 24    * "drools" appear in their names without prior written permission of The Werken
 25    * Company. "drools" is a trademark of The Werken Company.
 26    *
 27    * 5. Due credit should be given to The Werken Company. (http://werken.com/)
 28    *
 29    * THIS SOFTWARE IS PROVIDED BY THE WERKEN COMPANY AND CONTRIBUTORS ``AS IS''
 30    * AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 31    * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 32    * ARE DISCLAIMED. IN NO EVENT SHALL THE WERKEN COMPANY OR ITS CONTRIBUTORS BE
 33    * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 34    * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 35    * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 36    * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 37    * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 38    * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 39    * POSSIBILITY OF SUCH DAMAGE.
 40    *
 41    */
 42   
 43    import java.io.BufferedReader;
 44    import java.io.ByteArrayInputStream;
 45    import java.io.IOException;
 46    import java.io.InputStreamReader;
 47    import java.io.PrintStream;
 48    import java.util.HashSet;
 49    import java.util.Iterator;
 50    import java.util.Set;
 51   
 52    import org.drools.rule.Declaration;
 53   
 54    /**
 55    * Produces a graph in GraphViz DOT format.
 56    *
 57    * @see http://www.research.att.com/sw/tools/graphviz/
 58    * @see http://www.pixelglow.com/graphviz/
 59    *
 60    * @author Andy Barnett
 61    */
 62    public class ReteooDotDumpVisitor extends ReflectiveVisitor
 63    {
 64    /** String displayed for Null values. */
 65    private static final String NULL_STRING = "<NULL>";
 66   
 67    /** Amount of indention for Node and Edge lines. */
 68    private static final String INDENT = " ";
 69   
 70    /** The PrintStream where the DOT output will be written. */
 71    private final PrintStream out;
 72   
 73    /**
 74    * Keeps track of visited JoinNode DOT IDs. This mapping allows the visitor
 75    * to recognize JoinNodes it has already visited and as a consequence link
 76    * existing nodes back together. This is vital to the Visitor being able to
 77    * link two JoinNodeInputs together through their common JoinNode.
 78    */
 79    private Set visitedNodes = new HashSet( );
 80   
 81    /** Counter used to produce distinct DOT IDs for Null Nodes. */
 82    private int nullDotId;
 83   
 84    /**
 85    * Constructor.
 86    */
 87  0 public ReteooDotDumpVisitor(PrintStream outPrintStream)
 88    {
 89  0 out = outPrintStream;
 90    }
 91   
 92    /**
 93    * Default visitor if an unknown object is visited.
 94    */
 95  0 public void visitObject(Object object)
 96    {
 97  0 makeNode( object,
 98    "Unknown Object",
 99    "object: " + object + newline + "class: " + object.getClass( ) );
 100    }
 101   
 102    /**
 103    * Null visitor if a NULL object gets visited. Unique String objects are
 104    * generated to ensure every NULL object is distinct.
 105    */
 106  0 public void visitNull()
 107    {
 108  0 makeNode( "NULL" + nullDotId++,
 109    NULL_STRING );
 110    }
 111   
 112    /**
 113    * RuleBaseImpl visits its Rete.
 114    */
 115  0 public void visitRuleBaseImpl(RuleBaseImpl ruleBase)
 116    {
 117  0 visit( ruleBase.getRete( ) );
 118    }
 119   
 120    /**
 121    * Rete visits each of its ObjectTypeNodes.
 122    */
 123  0 public void visitRete(Rete rete)
 124    {
 125  0 makeNode( rete,
 126    "RETE-OO" );
 127  0 for ( Iterator i = rete.getObjectTypeNodeIterator( ); i.hasNext( ); )
 128    {
 129  0 Object nextNode = i.next( );
 130  0 makeEdge( rete,
 131    nextNode );
 132  0 visitNode( nextNode );
 133    }
 134    }
 135   
 136    /**
 137    * ObjectTypeNode displays its objectType and then visits each of its
 138    * ParameterNodes.
 139    */
 140  0 public void visitObjectTypeNode(ObjectTypeNode node)
 141    {
 142  0 makeNode( node,
 143    "ObjectTypeNode",
 144    "objectType: " + node.getObjectType( ) );
 145  0 for ( Iterator i = node.getParameterNodeIterator( ); i.hasNext( ); )
 146    {
 147  0 Object nextNode = i.next( );
 148  0 makeEdge( node,
 149    nextNode );
 150  0 visitNode( nextNode );
 151    }
 152    }
 153   
 154    /**
 155    * ParameterNode displays its declaration and then visits its TupleSink.
 156    */
 157  0 public void visitParameterNode(ParameterNode node)
 158    {
 159  0 makeTupleSourceNode( node, "ParameterNode", "TupleSource", "decl: " + format( node.getDeclaration( ) ) );
 160    }
 161   
 162  0 private void makeTupleSourceNode( TupleSource node, String nodeType, String tupleType, String label )
 163    {
 164  0 makeNode( node,
 165    nodeType,
 166    tupleType,
 167    label );
 168  0 for ( Iterator i = node.getTupleSinks( ).iterator(); i.hasNext(); )
 169    {
 170  0 Object nextNode = i.next();
 171  0 makeEdge( node,
 172    nextNode );
 173  0 visitNode( nextNode );
 174    }
 175    }
 176   
 177    /**
 178    * ConditionNode displays its condition and tuple Declarations and then
 179    * visits its TupleSink.
 180    */
 181  0 public void visitConditionNode(ConditionNode node)
 182    {
 183  0 makeTupleSourceNode( node,
 184    "ConditionNode",
 185    "TupleSource/TupleSink",
 186    "condition: " + node.getCondition( ) + newline + format( node.getTupleDeclarations( ),
 187    "tuple" ) );
 188    }
 189   
 190    /**
 191    * JoinNodeInput displays its side (LEFT/RIGHT) and then visits its
 192    * JoinNode.
 193    */
 194  0 public void visitJoinNodeInput(JoinNodeInput node)
 195    {
 196  0 makeNode( node,
 197    "JoinNodeInput",
 198    "TupleSink",
 199  0 node.getSide( ) == JoinNodeInput.LEFT ? "LEFT" : "RIGHT" );
 200  0 Object nextNode = node.getJoinNode( );
 201  0 makeEdge( node,
 202    nextNode );
 203  0 visitNode( nextNode );
 204    }
 205   
 206    /**
 207    * JoinNode displays its common Declarations and tuple Declarations and then
 208    * visits its TupleSink.
 209    */
 210  0 public void visitJoinNode(JoinNode node)
 211    {
 212  0 makeTupleSourceNode( node,
 213    "JoinNode",
 214    "TupleSource",
 215    format( node.getCommonDeclarations( ),
 216    "common" ) + newline + format( node.getTupleDeclarations( ),
 217    "tuple" ) );
 218    }
 219   
 220    /**
 221    * TerminalNode displays its rule.
 222    */
 223  0 public void visitTerminalNode(TerminalNode node)
 224    {
 225  0 makeNode( node,
 226    "TerminalNode",
 227    "TupleSink",
 228    "rule: " + node.getRule( ).getName( ) );
 229    }
 230   
 231    /**
 232    * Helper method to ensure nodes are not visited more than once.
 233    */
 234  0 private void visitNode(Object node)
 235    {
 236  0 if ( !visitedNodes.contains( dotId( node ) ) )
 237    {
 238  0 visitedNodes.add( dotId( node ) );
 239  0 visit( node );
 240    }
 241    }
 242   
 243    /**
 244    * Helper method for makeNode().
 245    */
 246  0 private void makeNode(Object object,
 247    String type,
 248    String label)
 249    {
 250  0 makeNode( object,
 251    type,
 252    null,
 253    label );
 254    }
 255   
 256    /**
 257    * Helper method for makeNode().
 258    */
 259  0 private void makeNode(Object object,
 260    String nodeType,
 261    String tupleType,
 262    String label)
 263    {
 264  0 makeNode( object,
 265  0 nodeType + "@" + dotId( object ) + newline + (null == tupleType ? "" : "(" + tupleType + ")" + newline ) + label );
 266    }
 267   
 268    /**
 269    * Outputs a DOT node line: "ID" [label="..."];
 270    */
 271  0 private void makeNode(Object object,
 272    String label)
 273    {
 274  0 out.println( INDENT + "\"" + dotId( object ) + "\" " +
 275    "[" + getStyle( object ) + ", label=\"" + format( label ) + "\"];" );
 276    }
 277   
 278    /**
 279    * Outputs a DOT edge line: "FROM_ID" -> "TO_ID";
 280    */
 281  0 private void makeEdge(Object fromNode,
 282    Object toNode)
 283    {
 284  0 out.println( INDENT + "\"" + dotId( fromNode ) + "\" -> \"" + dotId( toNode ) + "\";" );
 285    }
 286   
 287    /**
 288    * The identity hashCode for the given object is used as its unique DOT
 289    * identifier.
 290    */
 291  0 private static String dotId(Object object)
 292    {
 293  0 return Integer.toHexString( System.identityHashCode( object ) ).toUpperCase( );
 294    }
 295   
 296    /**
 297    * Formats a Set of Declarations for display.
 298    */
 299  0 private String format(Set declarationSet,
 300    String declString)
 301    {
 302  0 if ( declarationSet.isEmpty( ) )
 303    {
 304  0 return "No " + declString + " declarations";
 305    }
 306   
 307  0 Declaration[] declarations = (Declaration[]) declarationSet.toArray( new Declaration[]{} );
 308   
 309  0 StringBuffer label = new StringBuffer( );
 310  0 int i = 0;
 311  0 for ( int max = declarations.length - 1; i < max; i++ )
 312    {
 313  0 label.append( declString + "Decl: " + format( declarations[i] ) + newline );
 314    }
 315  0 label.append( declString + "Decl: " + format( declarations[i] ) );
 316   
 317  0 return label.toString( );
 318    }
 319   
 320    /**
 321    * Formats a single Declaration for display.
 322    */
 323  0 private static String format(Declaration declaration)
 324    {
 325  0 return null == declaration ? NULL_STRING : declaration.getIdentifier( ) + " (" + declaration.getObjectType( ) + ")";
 326    }
 327   
 328    /**
 329    * Formats text for compatibility with GraphViz DOT.
 330    * Converting line-breaks into '\' + 'n'.
 331    * Escapes double-quotes.
 332    */
 333  0 private static String format(String label)
 334    {
 335  0 if ( null == label )
 336    {
 337  0 return NULL_STRING;
 338    }
 339   
 340  0 BufferedReader br = new BufferedReader( new InputStreamReader( new ByteArrayInputStream( label.getBytes( ) ) ) );
 341   
 342  0 StringBuffer buffer = new StringBuffer( );
 343  0 try
 344    {
 345  0 boolean firstLine = true;
 346  0 for ( String line = br.readLine( ); null != line; line = br.readLine( ) )
 347    {
 348  0 if ( line.trim( ).length( ) == 0 )
 349    {
 350  0 continue;
 351    }
 352  0 if ( firstLine )
 353    {
 354  0 firstLine = false;
 355    }
 356    else
 357    {
 358  0 buffer.append( "\\n" );
 359    }
 360   
 361  0 if ( -1 == line.indexOf( '"' ) )
 362    {
 363  0 buffer.append( line );
 364    }
 365    else
 366    {
 367  0 for ( int i = 0, max = line.length( ); i < max; ++i )
 368    {
 369  0 char c = line.charAt( i );
 370  0 if ( '"' == c )
 371    {
 372  0 buffer.append( '\\' + c );
 373    }
 374    else
 375    {
 376  0 buffer.append( c );
 377    }
 378    }
 379    }
 380    }
 381    }
 382    catch ( IOException e )
 383    {
 384  0 throw new RuntimeException( "Error formatting '" + label + "': " + e.getMessage( ) );
 385    }
 386   
 387  0 return buffer.toString( );
 388    }
 389   
 390    /**
 391    * Returns the desired Node shape for the given object.
 392    *
 393    * @param node The Rete graph Node to be formatted
 394    *
 395    * @return a DOT style-string for the given Node
 396    */
 397  0 private String getStyle( Object node )
 398    {
 399  0 String style;
 400   
 401  0 if ( node instanceof Rete )
 402    {
 403    // Preferably this would be a "rectangle" with rounded sides
 404    // but GraphViz doesn't support "rounded" & "filled" on one node.
 405  0 style = "style=\"filled\", shape=\"ellipse\"";
 406    }
 407  0 else if ( node instanceof ObjectTypeNode )
 408    {
 409  0 style = "style=\"filled\", fillcolor=\"cyan4\", shape=\"rectangle\"";
 410    }
 411  0 else if ( node instanceof ParameterNode )
 412    {
 413  0 style = "style=\"filled\", fillcolor=\"cyan3\", shape=\"rectangle\"";
 414    }
 415  0 else if ( node instanceof ConditionNode )
 416    {
 417  0 style = "style=\"filled\", fillcolor=\"yellow3\", shape=\"diamond\"";
 418    }
 419  0 else if ( node instanceof JoinNodeInput )
 420    {
 421  0 style = "style=\"filled\", fillcolor=\"chartreuse\", shape=\"invtriangle\"";
 422    }
 423  0 else if ( node instanceof JoinNode )
 424    {
 425  0 style = "style=\"filled\", fillcolor=\"green\", shape=\"house\"";
 426    }
 427  0 else if ( node instanceof TerminalNode )
 428    {
 429    // Preferably this would be a "rectangle" with rounded sides
 430    // but GraphViz doesn't support "rounded" & "filled" on one node.
 431  0 style = "style=\"filled\", shape=\"ellipse\"";
 432    }
 433    else
 434    {
 435    // Preferably this would be a "rectangle" with rounded sides
 436    // but GraphViz doesn't support "rounded" & "filled" on one node.
 437  0 style = "style=\"filled\", fillcolor=\"yellow3\", shape=\"ellipse\"";
 438    }
 439   
 440  0 return style;
 441    }
 442    }