diff --git a/src/main/java/com/syncleus/grail/graph/ReflectionUtility.java b/src/main/java/com/syncleus/grail/graph/ReflectionUtility.java new file mode 100644 index 0000000000000000000000000000000000000000..90037e43756a2ec21a58ec66a7f07d4c59ce3296 --- /dev/null +++ b/src/main/java/com/syncleus/grail/graph/ReflectionUtility.java @@ -0,0 +1,64 @@ +/****************************************************************************** + * * + * Copyright: (c) Syncleus, Inc. * + * * + * You may redistribute and modify this source code under the terms and * + * conditions of the Open Source Community License - Type C version 1.0 * + * or any later version as published by Syncleus, Inc. at www.syncleus.com. * + * There should be a copy of the license included with this file. If a copy * + * of the license is not included you are granted no right to distribute or * + * otherwise use this file except through a legal and valid license. You * + * should also contact Syncleus, Inc. at the information below if you cannot * + * find a license: * + * * + * Syncleus, Inc. * + * 2604 South 12th Street * + * Philadelphia, PA 19148 * + * * + ******************************************************************************/ +package com.syncleus.grail.graph; + +import com.tinkerpop.frames.modules.typedgraph.*; + +final class ReflectionUtility { + private ReflectionUtility() { + } + + public static TypeValue determineTypeValue(final Class<?> type) { + final TypeValue typeValue = type.getDeclaredAnnotation(TypeValue.class); + if( typeValue == null ) + throw new IllegalArgumentException("The specified type does not have a TypeValue annotation"); + return typeValue; + } + + public static TypeField determineTypeField(final Class<?> type) { + TypeField typeField = type.getAnnotation(TypeField.class); + if( typeField == null ) { + final Class<?>[] parents = type.getInterfaces(); + for( final Class<?> parent : parents ) { + typeField = determineTypeFieldRecursive(parent); + if( typeField != null ) + return typeField; + } + + //we know typeField must still be null since we didnt return a value yet + throw new IllegalArgumentException("The specified type does not have a parent with a typeField annotation."); + } + + return typeField; + } + + public static TypeField determineTypeFieldRecursive(final Class<?> type) { + TypeField typeField = type.getAnnotation(TypeField.class); + if( typeField == null ) { + final Class<?>[] parents = type.getInterfaces(); + for( final Class<?> parent : parents ) { + typeField = determineTypeFieldRecursive(parent); + if( typeField != null ) + return typeField; + } + return null; + } + return typeField; + } +} diff --git a/src/main/java/com/syncleus/grail/graph/TypedAdjacencyMethodHandler.java b/src/main/java/com/syncleus/grail/graph/TypedAdjacencyMethodHandler.java index a2ebf812b9a55add66ae3c864460fe7b43c5e06f..d6e5ef3608bddf48ef684061f96ffb39bc70203c 100644 --- a/src/main/java/com/syncleus/grail/graph/TypedAdjacencyMethodHandler.java +++ b/src/main/java/com/syncleus/grail/graph/TypedAdjacencyMethodHandler.java @@ -89,8 +89,8 @@ public class TypedAdjacencyMethodHandler implements MethodHandler<TypedAdjacency } private Iterable getNodes(final Class type, final Direction direction, final String label, final FramedGraph<?> framedGraph, final Vertex vertex) { - final TypeValue typeValue = TypedAdjacencyMethodHandler.determineTypeValue(type); - final TypeField typeField = TypedAdjacencyMethodHandler.determineTypeField(type); + final TypeValue typeValue = ReflectionUtility.determineTypeValue(type); + final TypeField typeField = ReflectionUtility.determineTypeField(type); final Set<String> allAllowedValues = this.hierarchy.get(typeValue.value()); switch(direction) { case BOTH: @@ -105,8 +105,8 @@ public class TypedAdjacencyMethodHandler implements MethodHandler<TypedAdjacency } private Object getNode(final Class type, final Direction direction, final String label, final FramedGraph<?> framedGraph, final Vertex vertex) { - final TypeValue typeValue = TypedAdjacencyMethodHandler.determineTypeValue(type); - final TypeField typeField = TypedAdjacencyMethodHandler.determineTypeField(type); + final TypeValue typeValue = ReflectionUtility.determineTypeValue(type); + final TypeField typeField = ReflectionUtility.determineTypeField(type); final Set<String> allAllowedValues = this.hierarchy.get(typeValue.value()); switch(direction) { case BOTH: @@ -120,8 +120,8 @@ public class TypedAdjacencyMethodHandler implements MethodHandler<TypedAdjacency } private static Object addNode(final Class type, final Direction direction, final String label, final FramedGraph<?> framedGraph, final Vertex vertex) { - TypedAdjacencyMethodHandler.determineTypeValue(type); - TypedAdjacencyMethodHandler.determineTypeField(type); + ReflectionUtility.determineTypeValue(type); + ReflectionUtility.determineTypeField(type); final Vertex newVertex = framedGraph.addVertex(null); final Object newNode = framedGraph.frame(newVertex, type); @@ -147,42 +147,4 @@ public class TypedAdjacencyMethodHandler implements MethodHandler<TypedAdjacency if( ! method.getReturnType().isAssignableFrom(type) ) throw new IllegalArgumentException("The type is not a subtype of the return type."); } - - private static TypeValue determineTypeValue(final Class<?> type) { - final TypeValue typeValue = type.getDeclaredAnnotation(TypeValue.class); - if( typeValue == null ) - throw new IllegalArgumentException("The specified type does not have a TypeValue annotation"); - return typeValue; - } - - private static TypeField determineTypeField(final Class<?> type) { - TypeField typeField = type.getAnnotation(TypeField.class); - if( typeField == null ) { - final Class<?>[] parents = type.getInterfaces(); - for( final Class<?> parent : parents ) { - typeField = TypedAdjacencyMethodHandler.determineTypeFieldRecursive(parent); - if( typeField != null ) - return typeField; - } - - //we know typeField must still be null since we didnt return a value yet - throw new IllegalArgumentException("The specified type does not have a parent with a typeField annotation."); - } - - return typeField; - } - - private static TypeField determineTypeFieldRecursive(final Class<?> type) { - TypeField typeField = type.getAnnotation(TypeField.class); - if( typeField == null ) { - final Class<?>[] parents = type.getInterfaces(); - for( final Class<?> parent : parents ) { - typeField = TypedAdjacencyMethodHandler.determineTypeFieldRecursive(parent); - if( typeField != null ) - return typeField; - } - return null; - } - return typeField; - } } diff --git a/src/main/java/com/syncleus/grail/graph/TypedIncidenceMethodHandler.java b/src/main/java/com/syncleus/grail/graph/TypedIncidenceMethodHandler.java index 491f241dc4537679dce8cb2c69a0e528b2784517..4f65b04bbec96c3b3170ecb6f9c497db1a91921f 100644 --- a/src/main/java/com/syncleus/grail/graph/TypedIncidenceMethodHandler.java +++ b/src/main/java/com/syncleus/grail/graph/TypedIncidenceMethodHandler.java @@ -22,6 +22,7 @@ import com.tinkerpop.blueprints.*; import com.tinkerpop.frames.*; import com.tinkerpop.frames.modules.MethodHandler; import com.tinkerpop.frames.modules.typedgraph.*; +import com.tinkerpop.gremlin.Tokens; import com.tinkerpop.gremlin.java.GremlinPipeline; import java.lang.reflect.*; import java.util.*; @@ -60,10 +61,10 @@ public class TypedIncidenceMethodHandler implements MethodHandler<TypedIncidence final Vertex vertex = (Vertex) element; if( method.getReturnType().isAssignableFrom(Iterable.class)) - return TypedIncidenceMethodHandler.getEdges(type, annotation.direction(), annotation.label(), framedGraph, vertex); + return this.getEdges(type, annotation.direction(), annotation.label(), framedGraph, vertex); TypedIncidenceMethodHandler.checkReturnType(method, type); - return TypedIncidenceMethodHandler.getEdge(type, annotation.direction(), annotation.label(), framedGraph, vertex); + return this.getEdge(type, annotation.direction(), annotation.label(), framedGraph, vertex); } else throw new IllegalStateException("method " + method.getName() + " was annotated with @TypedIncidence but had more than 1 arguments."); @@ -72,32 +73,34 @@ public class TypedIncidenceMethodHandler implements MethodHandler<TypedIncidence throw new IllegalStateException("method " + method.getName() + " was annotated with @TypedIncidence but did not begin with either of the following keywords: add, get"); } - private static Iterable getEdges(final Class type, final Direction direction, final String label, final FramedGraph<?> framedGraph, final Vertex vertex) { - final TypeValue typeValue = TypedIncidenceMethodHandler.determineTypeValue(type); - final TypeField typeField = TypedIncidenceMethodHandler.determineTypeField(type); + private Iterable getEdges(final Class type, final Direction direction, final String label, final FramedGraph<?> framedGraph, final Vertex vertex) { + final TypeValue typeValue = ReflectionUtility.determineTypeValue(type); + final TypeField typeField = ReflectionUtility.determineTypeField(type); + final Set<String> allAllowedValues = this.hierarchy.get(typeValue.value()); switch(direction) { case BOTH: - return framedGraph.frameEdges((Iterable<Edge>) new GremlinPipeline<Vertex, Vertex>(vertex).bothE(label).has(typeField.value(), typeValue.value()), type); + return framedGraph.frameEdges((Iterable<Edge>) new GremlinPipeline<Vertex, Vertex>(vertex).bothE(label).has(typeField.value(), Tokens.T.in, allAllowedValues), type); case IN: - return framedGraph.frameEdges((Iterable<Edge>) new GremlinPipeline<Vertex, Vertex>(vertex).inE(label).has(typeField.value(), typeValue.value()), type); + return framedGraph.frameEdges((Iterable<Edge>) new GremlinPipeline<Vertex, Vertex>(vertex).inE(label).has(typeField.value(), Tokens.T.in, allAllowedValues), type); //Assume out direction default: - return framedGraph.frameEdges((Iterable<Edge>) new GremlinPipeline<Vertex, Vertex>(vertex).outE(label).has(typeField.value(), typeValue.value()), type); + return framedGraph.frameEdges((Iterable<Edge>) new GremlinPipeline<Vertex, Vertex>(vertex).outE(label).has(typeField.value(), Tokens.T.in, allAllowedValues), type); } } - private static Object getEdge(final Class type, final Direction direction, final String label, final FramedGraph<?> framedGraph, final Vertex vertex) { - final TypeValue typeValue = TypedIncidenceMethodHandler.determineTypeValue(type); - final TypeField typeField = TypedIncidenceMethodHandler.determineTypeField(type); + private Object getEdge(final Class type, final Direction direction, final String label, final FramedGraph<?> framedGraph, final Vertex vertex) { + final TypeValue typeValue = ReflectionUtility.determineTypeValue(type); + final TypeField typeField = ReflectionUtility.determineTypeField(type); + final Set<String> allAllowedValues = this.hierarchy.get(typeValue.value()); switch(direction) { case BOTH: - return framedGraph.frame((Edge) new GremlinPipeline<Vertex, Vertex>(vertex).bothE(label).has(typeField.value(), typeValue.value()).next(), type); + return framedGraph.frame((Edge) new GremlinPipeline<Vertex, Vertex>(vertex).bothE(label).has(typeField.value(), Tokens.T.in, allAllowedValues).next(), type); case IN: - return framedGraph.frame((Edge) new GremlinPipeline<Vertex, Vertex>(vertex).inE(label).has(typeField.value(), typeValue.value()).next(), type); + return framedGraph.frame((Edge) new GremlinPipeline<Vertex, Vertex>(vertex).inE(label).has(typeField.value(), Tokens.T.in, allAllowedValues).next(), type); //Assume out direction default: - return framedGraph.frame((Edge) new GremlinPipeline<Vertex, Vertex>(vertex).outE(label).has(typeField.value(), typeValue.value()).next(), type); + return framedGraph.frame((Edge) new GremlinPipeline<Vertex, Vertex>(vertex).outE(label).has(typeField.value(), Tokens.T.in, allAllowedValues).next(), type); } } @@ -105,42 +108,4 @@ public class TypedIncidenceMethodHandler implements MethodHandler<TypedIncidence if( ! method.getReturnType().isAssignableFrom(type) ) throw new IllegalArgumentException("The type is not a subtype of the return type."); } - - private static TypeValue determineTypeValue(final Class<?> type) { - final TypeValue typeValue = type.getDeclaredAnnotation(TypeValue.class); - if( typeValue == null ) - throw new IllegalArgumentException("The specified type does not have a TypeValue annotation"); - return typeValue; - } - - private static TypeField determineTypeField(final Class<?> type) { - TypeField typeField = type.getAnnotation(TypeField.class); - if( typeField == null ) { - final Class<?>[] parents = type.getInterfaces(); - for( final Class<?> parent : parents ) { - typeField = TypedIncidenceMethodHandler.determineTypeFieldRecursive(parent); - if( typeField != null ) - return typeField; - } - - //typeField is known to still be null. - throw new IllegalArgumentException("The specified type does not have a parent with a typeField annotation."); - } - - return typeField; - } - - private static TypeField determineTypeFieldRecursive(final Class<?> type) { - TypeField typeField = type.getAnnotation(TypeField.class); - if( typeField == null ) { - final Class<?>[] parents = type.getInterfaces(); - for( final Class<?> parent : parents ) { - typeField = TypedIncidenceMethodHandler.determineTypeFieldRecursive(parent); - if( typeField != null ) - return typeField; - } - return null; - } - return typeField; - } } diff --git a/src/test/java/com/syncleus/grail/graph/FatherEdgeExtended.java b/src/test/java/com/syncleus/grail/graph/FatherEdgeExtended.java new file mode 100644 index 0000000000000000000000000000000000000000..f1138eab43f040f10815d18a44a8dcafc8a54451 --- /dev/null +++ b/src/test/java/com/syncleus/grail/graph/FatherEdgeExtended.java @@ -0,0 +1,28 @@ +/****************************************************************************** + * * + * Copyright: (c) Syncleus, Inc. * + * * + * You may redistribute and modify this source code under the terms and * + * conditions of the Open Source Community License - Type C version 1.0 * + * or any later version as published by Syncleus, Inc. at www.syncleus.com. * + * There should be a copy of the license included with this file. If a copy * + * of the license is not included you are granted no right to distribute or * + * otherwise use this file except through a legal and valid license. You * + * should also contact Syncleus, Inc. at the information below if you cannot * + * find a license: * + * * + * Syncleus, Inc. * + * 2604 South 12th Street * + * Philadelphia, PA 19148 * + * * + ******************************************************************************/ +package com.syncleus.grail.graph; + +import com.tinkerpop.frames.Property; +import com.tinkerpop.frames.modules.typedgraph.TypeValue; + +@TypeValue("FatherEdgeExtended") +public interface FatherEdgeExtended extends FatherEdge { + @Property("extending") + String getExtending(); +} diff --git a/src/test/java/com/syncleus/grail/graph/GodExtended.java b/src/test/java/com/syncleus/grail/graph/GodExtended.java index d56d1c2fa128afa20ddaf8c17eeddb0290858512..196ebbcb97fdcae6857c019b7cf3454d31c1451e 100644 --- a/src/test/java/com/syncleus/grail/graph/GodExtended.java +++ b/src/test/java/com/syncleus/grail/graph/GodExtended.java @@ -18,6 +18,7 @@ ******************************************************************************/ package com.syncleus.grail.graph; +import com.tinkerpop.blueprints.Direction; import com.tinkerpop.frames.annotations.gremlin.GremlinGroovy; import com.tinkerpop.frames.modules.typedgraph.TypeValue; diff --git a/src/test/java/com/syncleus/grail/graph/titangraph/TitanGods.java b/src/test/java/com/syncleus/grail/graph/titangraph/TitanGods.java index 4814b65d8ef458196781079bb37dca54c3d459ee..fff76bbaca4dec32d461da5823c04a6ed28c7888 100644 --- a/src/test/java/com/syncleus/grail/graph/titangraph/TitanGods.java +++ b/src/test/java/com/syncleus/grail/graph/titangraph/TitanGods.java @@ -136,7 +136,7 @@ public class TitanGods { neptune.addEdge("brother", jupiter); neptune.addEdge("brother", pluto); - ElementHelper.setProperties(hercules.addEdge("father", jupiter), "classType", "Father"); + ElementHelper.setProperties(hercules.addEdge("father", jupiter), "classType", "FatherEdgeExtended"); hercules.addEdge("lives", sky).setProperty("reason", "loves heights"); ElementHelper.setProperties(hercules.addEdge("battled", nemean), "time", 1, "place", Geoshape.point(38.1f, 23.7f)); ElementHelper.setProperties(hercules.addEdge("battled", hydra), "time", 2, "place", Geoshape.point(37.7f, 23.9f)); diff --git a/src/test/java/com/syncleus/grail/graph/titangraph/TypedAdjacencyHandlerTest.java b/src/test/java/com/syncleus/grail/graph/titangraph/TypedAdjacencyHandlerTest.java index 14cae9e2e861266d8e7509398e1d6e857aabe9cd..4c9c0a5e44a06c22b9865e5f60981d5134e0073d 100644 --- a/src/test/java/com/syncleus/grail/graph/titangraph/TypedAdjacencyHandlerTest.java +++ b/src/test/java/com/syncleus/grail/graph/titangraph/TypedAdjacencyHandlerTest.java @@ -47,6 +47,23 @@ public class TypedAdjacencyHandlerTest { Assert.assertTrue(child instanceof GodExtended); } + @Test + public void testGetSonsExtended() { + final TitanGraph godGraph = TitanGods.create("./target/TitanTestDB"); + final FramedGraphFactory factory = new GrailGraphFactory(Collections.<Module>emptyList(), TypedAdjacencyHandlerTest.TEST_TYPES); + + final FramedGraph<?> framedGraph = factory.create(godGraph); + + final Iterable<God> gods = (Iterable<God>) framedGraph.getVertices("name", "jupiter", God.class); + final God father = gods.iterator().next(); + Assert.assertEquals(father.getName(), "jupiter"); + + final Iterable<? extends God> children = father.getSons(GodExtended.class); + final God child = children.iterator().next(); + Assert.assertEquals(child.getName(), "hercules"); + Assert.assertTrue(child instanceof GodExtended); + } + @Test public void testGetSonsNoLabel() { final TitanGraph godGraph = TitanGods.create("./target/TitanTestDB"); diff --git a/src/test/java/com/syncleus/grail/graph/titangraph/TypedIncidenceHandlerTest.java b/src/test/java/com/syncleus/grail/graph/titangraph/TypedIncidenceHandlerTest.java index f2b89cd123d9f7804a28c33a60a751c20c9594d7..17e9ad55c73dc4b2cf9c30a9b31477688f2ca9c0 100644 --- a/src/test/java/com/syncleus/grail/graph/titangraph/TypedIncidenceHandlerTest.java +++ b/src/test/java/com/syncleus/grail/graph/titangraph/TypedIncidenceHandlerTest.java @@ -28,7 +28,7 @@ import org.junit.*; import java.util.*; public class TypedIncidenceHandlerTest { - private static final Set<Class<?>> TEST_TYPES = new HashSet<Class<?>>(Arrays.asList(new Class<?>[]{God.class, FatherEdge.class, GodExtended.class})); + private static final Set<Class<?>> TEST_TYPES = new HashSet<Class<?>>(Arrays.asList(new Class<?>[]{God.class, FatherEdge.class, GodExtended.class, FatherEdgeExtended.class})); @Test public void testGetSonEdges() { @@ -47,6 +47,23 @@ public class TypedIncidenceHandlerTest { Assert.assertTrue(childEdge.getSon() instanceof GodExtended); } + @Test + public void testGetSonEdgesExtended() { + final TitanGraph godGraph = TitanGods.create("./target/TitanTestDB"); + final FramedGraphFactory factory = new GrailGraphFactory(Collections.<Module>emptyList(), TypedIncidenceHandlerTest.TEST_TYPES); + + final FramedGraph<?> framedGraph = factory.create(godGraph); + + final Iterable<God> gods = (Iterable<God>) framedGraph.getVertices("name", "jupiter", God.class); + final God father = gods.iterator().next(); + Assert.assertEquals(father.getName(), "jupiter"); + + final Iterable<? extends FatherEdge> childEdges = father.getSonEdges(FatherEdgeExtended.class); + final FatherEdge childEdge = childEdges.iterator().next(); + Assert.assertEquals(childEdge.getSon().getName(), "hercules"); + Assert.assertTrue(childEdge.getSon() instanceof GodExtended); + } + @Test public void testGetSonEdge() { final TitanGraph godGraph = TitanGods.create("./target/TitanTestDB");