/*
* Copyright 2010 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.drools.eclipse.reteoo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.drools.core.common.BaseNode;
import org.drools.core.impl.InternalKnowledgeBase;
import org.drools.core.reteoo.EntryPointNode;
import org.drools.core.reteoo.LeftTupleSource;
import org.drools.core.reteoo.ObjectSource;
import org.drools.core.reteoo.ObjectTypeNode;
import org.drools.core.reteoo.Rete;
import org.drools.core.util.ObjectHashMap;
import org.drools.core.util.ReflectiveVisitor;
import org.drools.eclipse.editors.rete.model.Connection;
import org.drools.eclipse.editors.rete.model.ReteGraph;
/**
* Produces a graph in GraphViz DOT format.
*
* @see http://www.research.att.com/sw/tools/graphviz/
* @see http://www.pixelglow.com/graphviz/
*/
public class ReteooVisitor extends ReflectiveVisitor {
private static final String PACKAGE_NAME = "org.drools.eclipse.reteoo.";
/**
* Keeps track of visited JoinNode DOT IDs. This mapping allows the visitor
* to recognize JoinNodes it has already visited and as a consequence link
* existing nodes back together. This is vital to the Visitor being able to
* link two JoinNodeInputs together through their common JoinNode.
*/
private final Map<String, BaseVertex> visitedNodes = new HashMap<String, BaseVertex>();
private ReteGraph graph;
private BaseVertex rootVertex;
private BaseVertex parentVertex;
/**
* Constructor.
*/
public ReteooVisitor(final ReteGraph graph) {
this.graph = graph;
}
public ReteGraph getGraph() {
return this.graph;
}
public BaseVertex getRootVertex() {
return this.rootVertex;
}
/**
* RuleBaseImpl visits its Rete.
*/
public void visitInternalKnowledgeBase(final InternalKnowledgeBase ruleBase) {
visit( (ruleBase).getRete() );
}
/**
* Rete visits each of its ObjectTypeNodes.
*/
public void visitRete(final Rete rete) {
this.rootVertex = (ReteVertex) this.visitedNodes.get( dotId( rete ) );
if ( this.rootVertex == null ) {
this.rootVertex = new ReteVertex( rete );
this.visitedNodes.put( dotId( rete ),
this.rootVertex );
}
this.graph.addChild( this.rootVertex );
this.parentVertex = this.rootVertex;
for( EntryPointNode node : rete.getEntryPointNodes().values() ) {
visit( node );
}
}
public void visitBaseNode(final BaseNode node) {
BaseVertex vertex = this.visitedNodes.get( dotId( node ) );
if ( vertex == null ) {
try {
String name = node.getClass().getName();
name = name.substring( name.lastIndexOf( '.' ) + 1 ) + "Vertex";
final Class<?> clazz = Class.forName( PACKAGE_NAME + name );
vertex = (BaseVertex) clazz.getConstructor( new Class[]{node.getClass()} ).newInstance( new Object[]{node} );
} catch ( final Exception e ) {
throw new RuntimeException( "problem visiting vertex " + node.getClass().getName(),
e );
}
this.graph.addChild( vertex );
this.visitedNodes.put( dotId( node ),
vertex );
new Connection( this.parentVertex,
vertex );
final BaseVertex oldParentVertex = this.parentVertex;
this.parentVertex = vertex;
List<?> list = null;
if ( node instanceof EntryPointNode ) {
list = new ArrayList<ObjectTypeNode>( ((EntryPointNode) node).getObjectTypeNodes().values() );
} else if ( node instanceof ObjectSource) {
list = Arrays.asList( ((ObjectSource) node).getObjectSinkPropagator().getSinks() );
} else if ( node instanceof LeftTupleSource) {
list = Arrays.asList( ((LeftTupleSource) node).getSinkPropagator().getSinks() );
}
if ( list != null ) {
for ( final java.util.Iterator<?> it = list.iterator(); it.hasNext(); ) {
final Object nextNode = it.next();
visitNode( nextNode );
}
}
this.parentVertex = oldParentVertex;
} else {
new Connection( this.parentVertex,
vertex );
}
}
/**
* Helper method to ensure nodes are not visited more than once.
*/
private void visitNode(final Object node) {
Object realNode = node;
if ( node instanceof ObjectHashMap.ObjectEntry ) {
ObjectHashMap.ObjectEntry entry = (ObjectHashMap.ObjectEntry) node;
realNode = entry.getValue();
}
visit( realNode );
}
/**
* The identity hashCode for the given object is used as its unique DOT
* identifier.
*/
private static String dotId(final Object object) {
return Integer.toHexString( System.identityHashCode( object ) ).toUpperCase();
}
}