diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a0526019c5debb8d429a265d5926311a3a06dcd..a219076c4639b8ebc17f48d831d5f1f4a5ccff63 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,8 +10,10 @@ * Added `operation` parameter to the following annotations: `@Adjacency`, `@Incidence`, `@Property`. Setting the parameter will override the auto discovery of the method prefix previously used to discovery the operation of the method. -* Methods annotated with the `@Adjacency` annotation can now return a `List` or a `Set` in addition to the usual +* Getter methods annotated with the `@Adjacency` annotation can now return a `List` or a `Set` in addition to the usual `Iterator` return type. +* Setter methods annotated with the `@Adjacency` annotation can now accept `Iterable` parameters such as `Collection`, + `List`, or `Set` in addition to the usual `Iterator` type. * Methods annotated with the `@Incidence` annotation can now return a `List` or a `Set` in addition to the usual `Iterator` return type. diff --git a/src/main/java/com/syncleus/ferma/framefactories/annotation/AdjacencyMethodHandler.java b/src/main/java/com/syncleus/ferma/framefactories/annotation/AdjacencyMethodHandler.java index c09b34cb4c33eb73a8749bc654662c345ee8d13e..3f215e385efbd1af595256167fcbf7f821b014a8 100644 --- a/src/main/java/com/syncleus/ferma/framefactories/annotation/AdjacencyMethodHandler.java +++ b/src/main/java/com/syncleus/ferma/framefactories/annotation/AdjacencyMethodHandler.java @@ -15,8 +15,6 @@ */ package com.syncleus.ferma.framefactories.annotation; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; import com.syncleus.ferma.ClassInitializer; import com.syncleus.ferma.VertexFrame; import com.syncleus.ferma.annotations.Adjacency; @@ -33,7 +31,6 @@ import org.apache.tinkerpop.gremlin.structure.Edge; import java.lang.annotation.Annotation; import java.lang.reflect.Method; -import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Set; @@ -111,10 +108,12 @@ public class AdjacencyMethodHandler implements MethodHandler { if (arguments == null || arguments.length == 0) throw new IllegalStateException(method.getName() + " was annotated with @Adjacency but had no arguments."); else if (arguments.length == 1) { - if (!(Iterator.class.isAssignableFrom(arguments[0].getType()))) - throw new IllegalStateException(method.getName() + " was annotated with @Adjacency, had a single argument, but that argument was not of the type Class"); + if (ReflectionUtility.acceptsIterator(method, 0)) + return this.setVertexIterator(builder, method, annotation); + if (ReflectionUtility.acceptsIterable(method, 0)) + return this.setVertexIterable(builder, method, annotation); - return this.setVertex(builder, method, annotation); + throw new IllegalStateException(method.getName() + " was annotated with @Adjacency, had a single argument, but that argument was not of the type Iterator or Iterable"); } else throw new IllegalStateException(method.getName() + " was annotated with @Adjacency but had more than 1 arguments."); @@ -174,8 +173,12 @@ public class AdjacencyMethodHandler implements MethodHandler { return builder.method(ElementMatchers.is(method)).intercept(MethodDelegation.to(AddVertexByObjectTypedEdgeInterceptor.class)); } - private <E> DynamicType.Builder<E> setVertex(final DynamicType.Builder<E> builder, final Method method, final Annotation annotation) { - return builder.method(ElementMatchers.is(method)).intercept(MethodDelegation.to(SetVertexInterceptor.class)); + private <E> DynamicType.Builder<E> setVertexIterator(final DynamicType.Builder<E> builder, final Method method, final Annotation annotation) { + return builder.method(ElementMatchers.is(method)).intercept(MethodDelegation.to(SetVertexIteratorInterceptor.class)); + } + + private <E> DynamicType.Builder<E> setVertexIterable(final DynamicType.Builder<E> builder, final Method method, final Annotation annotation) { + return builder.method(ElementMatchers.is(method)).intercept(MethodDelegation.to(SetVertexIterableInterceptor.class)); } private <E> DynamicType.Builder<E> removeVertex(final DynamicType.Builder<E> builder, final Method method, final Annotation annotation) { @@ -531,7 +534,7 @@ public class AdjacencyMethodHandler implements MethodHandler { } } - public static final class SetVertexInterceptor { + public static final class SetVertexIteratorInterceptor { @RuntimeType public static void setVertex(@This final VertexFrame thiz, @Origin final Method method, @RuntimeType @Argument(0) final Iterator vertexSet) { @@ -576,6 +579,51 @@ public class AdjacencyMethodHandler implements MethodHandler { } } + public static final class SetVertexIterableInterceptor { + + @RuntimeType + public static void setVertex(@This final VertexFrame thiz, @Origin final Method method, @RuntimeType @Argument(0) final Iterable vertexSet) { + assert thiz instanceof CachesReflection; + final Adjacency annotation = ((CachesReflection) thiz).getReflectionCache().getAnnotation(method, Adjacency.class); + final Direction direction = annotation.direction(); + final String label = annotation.label(); + + + switch (direction) { + case BOTH: + thiz.unlinkBoth(null, label); + ((Iterator<? extends VertexFrame>)vertexSet.iterator()).forEachRemaining(new Consumer<VertexFrame>() { + @Override + public void accept(VertexFrame vertexFrame) { + thiz.getGraph().addFramedEdge(vertexFrame, thiz, label); + thiz.getGraph().addFramedEdge(thiz, vertexFrame, label); + } + }); + break; + case IN: + thiz.unlinkIn(null, label); + ((Iterator<? extends VertexFrame>)vertexSet.iterator()).forEachRemaining(new Consumer<VertexFrame>() { + @Override + public void accept(VertexFrame vertexFrame) { + thiz.getGraph().addFramedEdge(vertexFrame, thiz, label); + } + }); + break; + case OUT: + thiz.unlinkOut(null, label); + ((Iterator<? extends VertexFrame>)vertexSet.iterator()).forEachRemaining(new Consumer<VertexFrame>() { + @Override + public void accept(VertexFrame vertexFrame) { + thiz.getGraph().addFramedEdge(thiz, vertexFrame, label); + } + }); + break; + default: + throw new IllegalStateException(method.getName() + " is annotated with a direction other than BOTH, IN, or OUT."); + } + } + } + public static final class RemoveVertexInterceptor { @RuntimeType diff --git a/src/main/java/com/syncleus/ferma/framefactories/annotation/ReflectionUtility.java b/src/main/java/com/syncleus/ferma/framefactories/annotation/ReflectionUtility.java index 8514105445fcee4202cee3563ff5604cecf9431f..a6b7895ebc894300aa9cc01fe4ac48b4737a57dd 100644 --- a/src/main/java/com/syncleus/ferma/framefactories/annotation/ReflectionUtility.java +++ b/src/main/java/com/syncleus/ferma/framefactories/annotation/ReflectionUtility.java @@ -21,10 +21,7 @@ import com.syncleus.ferma.annotations.Property; import org.apache.tinkerpop.gremlin.structure.Vertex; import java.lang.reflect.*; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; public class ReflectionUtility { @@ -162,8 +159,12 @@ public class ReflectionUtility { return method.getName().startsWith(ADD); } - public static boolean acceptsIterator(final Method method) { - return 1 == method.getParameterTypes().length && Iterator.class.isAssignableFrom(method.getParameterTypes()[0]); + public static boolean acceptsIterator(final Method method, int parameterIndex) { + return (parameterIndex + 1) == method.getParameterTypes().length && Iterator.class.isAssignableFrom(method.getParameterTypes()[parameterIndex]); + } + + public static boolean acceptsIterable(final Method method, int parameterIndex) { + return (parameterIndex + 1) == method.getParameterTypes().length && Iterable.class.isAssignableFrom(method.getParameterTypes()[parameterIndex]); } public static boolean returnsIterator(final Method method) { @@ -178,14 +179,6 @@ public class ReflectionUtility { return Set.class.isAssignableFrom(method.getReturnType()); } - public static boolean returnsVertex(final Method method) { - return Vertex.class.isAssignableFrom(method.getReturnType()); - } - - public static boolean returnsMap(final Method method) { - return Map.class.isAssignableFrom(method.getReturnType()); - } - public static Type getType(final Type[] types, final int pos) { if (pos >= types.length) throw new RuntimeException("No type can be found at position " diff --git a/src/test/java/com/syncleus/ferma/annotations/AdjacencyMethodHandlerTest.java b/src/test/java/com/syncleus/ferma/annotations/AdjacencyMethodHandlerTest.java index 09b812969047bc5a0af1bfd1b77b6b423ec27bb8..391d9daa663b247e0e1eae76c3974e5983470f1f 100644 --- a/src/test/java/com/syncleus/ferma/annotations/AdjacencyMethodHandlerTest.java +++ b/src/test/java/com/syncleus/ferma/annotations/AdjacencyMethodHandlerTest.java @@ -497,6 +497,36 @@ public class AdjacencyMethodHandlerTest { Assert.assertNull(child.getName()); } + @Test + public void testSetSonsList() { + final TinkerGraph godGraph = TinkerGraph.open(); + GodGraphLoader.load(godGraph); + + final FramedGraph framedGraph = new DelegatingFramedGraph(godGraph, TEST_TYPES); + + final List<? extends God> gods = framedGraph.traverse( + input -> input.V().has("name", "jupiter")).toList(God.class); + + final God father = gods.iterator().next(); + Assert.assertTrue(father != null); + final VertexFrame fatherVertex = father; + Assert.assertEquals(fatherVertex.getProperty("name"), "jupiter"); + + God child = father.getSon(God.class); + Assert.assertNotNull(child); + Assert.assertTrue(child instanceof VertexFrame); + final VertexFrame childVertex = child; + Assert.assertEquals(childVertex.getElement().property("name").value(), "hercules"); + Assert.assertTrue(child instanceof GodExtended); + + father.setSonsList(Arrays.asList(framedGraph.addFramedVertex(God.DEFAULT_INITIALIZER))); + + child = father.getSon(God.class); + + Assert.assertNotNull(child); + Assert.assertNull(child.getName()); + } + @Test public void testApplySons() { final TinkerGraph godGraph = TinkerGraph.open(); diff --git a/src/test/java/com/syncleus/ferma/annotations/God.java b/src/test/java/com/syncleus/ferma/annotations/God.java index b986f6215db1923c396769722addd447f674335e..102d1755cbab93b3464b19762ab614f456509486 100644 --- a/src/test/java/com/syncleus/ferma/annotations/God.java +++ b/src/test/java/com/syncleus/ferma/annotations/God.java @@ -101,6 +101,9 @@ public interface God extends VertexFrame { @Adjacency(label = "father", direction = Direction.IN) void setSons(Iterator<? extends God> vertexSet); + @Adjacency(label = "father", direction = Direction.IN) + void setSonsList(List<? extends God> vertexList); + @Adjacency(label = "father", direction = Direction.IN, operation = Adjacency.Operation.SET) void applySons(Iterator<? extends God> vertexSet);