package org.drools.semantics.util.editor;
import com.clarkparsia.empire.annotation.RdfProperty;
import org.drools.semantics.Thing;
import prefuse.data.Edge;
import prefuse.data.Graph;
import prefuse.data.Node;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;
import java.util.List;
public class FactGraphAnalyzer {
private int counter = 0;
private static Map<Class, Map<String,RelationDescriptor>> relCache = new HashMap<Class, Map<String, RelationDescriptor>>();
public Graph analyzeObject( Thing thing ) {
Map<Object, Node> cache = new IdentityHashMap<Object, Node>();
Graph graph = new Graph();
graph.getNodeTable().addColumn( "id", int.class );
graph.getNodeTable().addColumn( "rdfId", String.class );
graph.getNodeTable().addColumn( "aggId", Integer.class );
graph.getNodeTable().addColumn( "node", Node.class );
graph.getNodeTable().addColumn( "label", String.class );
graph.getNodeTable().addColumn( "descr", ObjectDescriptor.class );
graph.getNodeTable().addColumn( "type", ObjectDescriptor.NODETYPE.class );
graph.getEdgeTable().addColumn( "label", String.class );
graph.getEdgeTable().addColumn( "iri", String.class );
graph.getEdgeTable().addColumn( "edge", Edge.class );
graph.getEdgeTable().addColumn( "descr", RelationDescriptor.class );
Node root = graph.addNode();
cache.put( thing, root );
ObjectDescriptor x = new ObjectDescriptor();
x.setObject(thing);
x.setType( ObjectDescriptor.NODETYPE.OBJECT );
x.setRelations( extractRelations(thing) );
root.set( "aggId", counter++ );
root.set( "rdfId", thing.getRdfId().toString() );
root.set( "label", thing.getClass().getSimpleName() );
root.set( "descr", x );
root.set( "type", ObjectDescriptor.NODETYPE.ROOT );
root.set( "node", root );
process(thing, root, graph, cache);
return graph;
}
private void process( Object thing, Node root, Graph graph, Map<Object, Node> cache ) {
Map<String, Method> properties = new HashMap<String, Method>();
Method[] methods = thing.getClass().getMethods();
for ( Method m : methods ) {
if ( m.getName().startsWith( "get" ) || m.getName().startsWith( "is" ) ) {
RdfProperty ann = m.getAnnotation( RdfProperty.class );
if ( ann != null ) {
properties.put( ann.value(), m );
}
}
}
for ( String key : properties.keySet() ) {
Method m = properties.get( key );
Object val;
try {
val = m.invoke( thing );
} catch ( IllegalAccessException e ) {
val = e.getMessage();
} catch ( InvocationTargetException e ) {
val = e.getMessage();
}
boolean singleValue = false;
if ( val != null ) {
List list;
if ( val instanceof List ) {
list = (List) val;
} else {
list = Arrays.asList( val );
singleValue = true;
}
for ( Object x : list ) {
Node tgt;
if ( isDataType( x.getClass() ) ) {
tgt = addDataRelation( root, key, x, m, graph );
} else {
tgt = exploreObjectRelation( root, key, x, m, graph, cache );
}
}
}
}
}
private Node exploreObjectRelation(Node root, String key, Object val, Method getter, Graph graph, Map<Object, Node> cache) {
RelationDescriptor descr = new RelationDescriptor();
ObjectDescriptor source = (ObjectDescriptor) root.get( "descr" );
descr.setSubject( source.getObject() );
descr.setProperty( key );
descr.setObject( val );
descr.setType( RelationDescriptor.RELTYPE.OBJECT );
descr.setRange( val.getClass() );
extractMethods( getter, source.getObject().getClass(), descr );
source.addOutRelation( descr );
Node target;
if ( ! cache.containsKey( val ) ) {
target = graph.addNode();
ObjectDescriptor od = new ObjectDescriptor();
od.setObject( val );
od.setType( ObjectDescriptor.NODETYPE.OBJECT );
od.setLabel( val.toString() );
od.setRelations( extractRelations( val ) );
target.set( "label", val.toString() );
if ( val instanceof Thing ) {
target.set( "rdfId", ((Thing) val).getRdfId().toString() );
}
int id = counter++;
target.set( "id", id );
target.set( "aggId", id );
target.set( "descr", od );
target.set( "node", target );
target.set( "type", ObjectDescriptor.NODETYPE.OBJECT );
cache.put( val, target );
process( val, target, graph, cache );
} else {
target = cache.get( val );
}
((ObjectDescriptor) target.get( "descr" ) ).addInRelation( descr );
Edge arc = graph.addEdge( root, target );
arc.set( "label", key );
arc.set( "iri", key );
arc.set( "edge", arc );
arc.set( "descr", descr );
return target;
}
public static Map<String, RelationDescriptor> extractRelations( Object val ) {
if ( ! relCache.containsKey( val.getClass() ) ) {
Map<String,RelationDescriptor> props = new HashMap<String, RelationDescriptor>();
String className = val.getClass().getName();
// remove "Impl"
String interfaceName = className.substring(0, className.length() - 4);
Class[] ifx = val.getClass().getInterfaces();
Map<String,Method> methods = new HashMap<String,Method>();
for ( Class i : ifx ) {
if ( i.getName().equals( interfaceName ) ) {
collectMethods( i, methods );
break;
}
}
for ( Method m : methods.values() ) {
if ( m.getName().startsWith("get") || m.getName().startsWith( "is" ) ) {
if ( m.getAnnotation( RdfProperty.class ) != null ) {
String pName = m.getAnnotation( RdfProperty.class ).value();
String stem = m.getName().startsWith("is") ? m.getName().substring(2) : m.getName().substring(3);
String adder = "add" + stem;
for ( Method n : val.getClass().getMethods() ) {
if ( adder.equals( n.getName() ) && ! Object.class.equals( n.getParameterTypes()[0] ) ) {
RelationDescriptor rd = new RelationDescriptor();
rd.setRange( n.getParameterTypes()[0] );
rd.setProperty( pName );
rd.setType( isDataType( rd.getRange() ) ? RelationDescriptor.RELTYPE.DATA : RelationDescriptor.RELTYPE.OBJECT );
extractMethods( m, val.getClass(), rd );
props.put( pName, rd );
break;
}
}
}
}
}
relCache.put( val.getClass(), props );
}
return relCache.get( val.getClass() );
}
private static void collectMethods( Class i, Map<String,Method> methods ) {
for ( Method m : i.getMethods() ) {
if ( m.getAnnotation( RdfProperty.class ) != null ) {
if ( ! methods.containsKey( m.getName() ) ) {
methods.put( m.getName(), m );
}
}
}
for ( Class superIf : i.getInterfaces() ) {
collectMethods( superIf, methods );
}
}
private Node addDataRelation( Node root, String rel, Object val, Method getter, Graph graph ) {
Node target = graph.addNode();
RelationDescriptor descr = new RelationDescriptor();
ObjectDescriptor source = (ObjectDescriptor) root.get( "descr" );
descr.setSubject( source.getObject() );
descr.setProperty( rel );
descr.setObject( val );
descr.setType( RelationDescriptor.RELTYPE.DATA );
descr.setRange( val.getClass() );
extractMethods( getter, source.getObject().getClass(), descr );
ObjectDescriptor sod = (ObjectDescriptor) root.get( "descr" );
sod.addOutRelation( descr );
ObjectDescriptor od = new ObjectDescriptor();
od.addInRelation( descr );
od.setType( ObjectDescriptor.NODETYPE.DATA );
od.setObject( val );
target.set( "aggId", root.get( "aggId" ) );
target.set( "label", val.toString() );
target.set( "node", target );
target.set( "descr", od );
target.set( "type", ObjectDescriptor.NODETYPE.DATA );
Edge arc = graph.addEdge( root, target );
arc.set( "label", rel );
arc.set( "iri", rel );
arc.set( "edge", arc );
arc.set( "descr", descr );
return target;
}
private static void extractMethods( Method getter, Class type, RelationDescriptor descr ) {
String methodStem = getter.getName().substring( getter.getName().startsWith("is") ? 2 : 3 );
descr.setGetter( getter );
boolean singleValue = ! Collection.class.isAssignableFrom( getter.getReturnType() );
Method[] methods = type.getMethods();
if ( singleValue ) {
String setterName = "set" + methodStem;
for ( Method setter : methods ) {
if ( setter.getName().equals( setterName ) && setter.getParameterTypes().length == 1 && ! Object.class.equals( setter.getParameterTypes()[0] ) ) {
descr.setSetter( setter );
descr.setRange( setter.getParameterTypes()[0] );
break;
}
}
}
String adderName = "add" + methodStem;
for ( Method adder : methods ) {
if ( adder.getName().equals( adderName ) && adder.getParameterTypes().length == 1 && ! Object.class.equals( adder.getParameterTypes()[0] ) ) {
descr.setAdder( adder );
descr.setRange( adder.getParameterTypes()[0] );
break;
}
}
String removerName = "remove" + methodStem;
try {
Method remover = type.getMethod( removerName, Object.class );
descr.setRemover(remover);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
private static boolean isDataType( Class<?> returnType ) {
return Integer.class.equals( returnType ) || int.class.equals( returnType )
|| Byte.class.equals( returnType ) || byte.class.equals( returnType )
|| Short.class.equals( returnType ) || short.class.equals( returnType )
|| Character.class.equals( returnType ) || char.class.equals( returnType )
|| Float.class.equals( returnType ) || float.class.equals( returnType )
|| Double.class.equals( returnType ) || double.class.equals( returnType )
|| BigInteger.class.equals( returnType ) || BigDecimal.class.equals( returnType )
|| Date.class.equals( returnType )
|| String.class.equals( returnType );
}
}