diff --git a/CHANGELOG.md b/CHANGELOG.md index 119974383506709c90ab903a726446ada9087e6e..48bdd905594894e38e28e8e140984a9128f7b9bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ `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. +* Remove methods annotated with the `@Adjacency` annotation can now have no parameters and they will remove all edges + matching the specified label and direction. * Setter methods annotated with the `@Adjacency` annotation can now accept `VertexFrame` parameters including other vertex in the users custom model. * Methods annotated with the `@Incidence` annotation can now return a `List` or a `Set` in addition to the usual 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 618a75f9b8535a8d7a4827770887b3008010a784..7b24d93b3dc03890429ec5a124943be26480aee4 100644 --- a/src/main/java/com/syncleus/ferma/framefactories/annotation/AdjacencyMethodHandler.java +++ b/src/main/java/com/syncleus/ferma/framefactories/annotation/AdjacencyMethodHandler.java @@ -99,7 +99,7 @@ public class AdjacencyMethodHandler implements MethodHandler { throw new IllegalStateException(method.getName() + " was annotated with @Adjacency but had more than 1 arguments."); else if (ReflectionUtility.isRemoveMethod(method)) if (arguments == null || arguments.length == 0) - throw new IllegalStateException(method.getName() + " was annotated with @Adjacency but had no arguments."); + return this.removeAll(builder, method, annotation); else if (arguments.length == 1) return this.removeVertex(builder, method, annotation); else @@ -191,6 +191,10 @@ public class AdjacencyMethodHandler implements MethodHandler { return builder.method(ElementMatchers.is(method)).intercept(MethodDelegation.to(RemoveVertexInterceptor.class)); } + private <E> DynamicType.Builder<E> removeAll(final DynamicType.Builder<E> builder, final Method method, final Annotation annotation) { + return builder.method(ElementMatchers.is(method)).intercept(MethodDelegation.to(RemoveAllInterceptor.class)); + } + public static final class GetVertexesIteratorDefaultInterceptor { @RuntimeType @@ -703,4 +707,47 @@ public class AdjacencyMethodHandler implements MethodHandler { } } } + + public static final class RemoveAllInterceptor { + + @RuntimeType + public static void removeVertex(@This final VertexFrame thiz, @Origin final Method method) { + 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: + final Iterator<Edge> bothEdges = thiz.getRawTraversal().bothE(label); + bothEdges.forEachRemaining(new Consumer<Edge>() { + @Override + public void accept(final Edge edge) { + edge.remove(); + } + }); + break; + case IN: + final Iterator<Edge> inEdges = thiz.getRawTraversal().inE(label); + inEdges.forEachRemaining(new Consumer<Edge>() { + @Override + public void accept(final Edge edge) { + edge.remove(); + } + }); + break; + case OUT: + final Iterator<Edge> outEdges = thiz.getRawTraversal().outE(label); + outEdges.forEachRemaining(new Consumer<Edge>() { + @Override + public void accept(final Edge edge) { + edge.remove(); + } + }); + break; + default: + throw new IllegalStateException(method.getName() + " is annotated with a direction other than BOTH, IN, or OUT."); + } + } + } } diff --git a/src/test/java/com/syncleus/ferma/annotations/AdjacencyMethodHandlerTest.java b/src/test/java/com/syncleus/ferma/annotations/AdjacencyMethodHandlerTest.java index b0b4924abc60e444a767f3148edc70b6c5dc9ccc..bbbfb9d67956b1ae71f5a9b782bbe756125ff1af 100644 --- a/src/test/java/com/syncleus/ferma/annotations/AdjacencyMethodHandlerTest.java +++ b/src/test/java/com/syncleus/ferma/annotations/AdjacencyMethodHandlerTest.java @@ -15,16 +15,12 @@ */ package com.syncleus.ferma.annotations; -import java.util.function.Function; import com.syncleus.ferma.*; -import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; -import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; import org.apache.tinkerpop.gremlin.structure.Direction; import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph; import org.junit.Assert; import org.junit.Test; -import javax.annotation.Nullable; import java.util.*; public class AdjacencyMethodHandlerTest { @@ -618,6 +614,25 @@ public class AdjacencyMethodHandlerTest { Assert.assertFalse(father.getSons(God.class).hasNext()); } + @Test + public void testRemoveEverySon() { + 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); + father.addSon(God.DEFAULT_INITIALIZER); + + Assert.assertEquals(2, father.getSonsList(God.class).size()); + father.removeEverySon(); + Assert.assertEquals(0, father.getSonsList(God.class).size()); + } + @Test public void testDeleteSon() { 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 f0506127340174ac282b767213699c0093ddd1e9..0cd378e482d458dca85211eeae007224f6c49e9c 100644 --- a/src/test/java/com/syncleus/ferma/annotations/God.java +++ b/src/test/java/com/syncleus/ferma/annotations/God.java @@ -143,6 +143,9 @@ public interface God extends VertexFrame { @Incidence(label = "father", direction = Direction.IN) void removeSonEdge(FatherEdge edge); + @Adjacency(label = "father", direction = Direction.IN) + void removeEverySon(); + @Incidence(label = "father", direction = Direction.IN, operation = Incidence.Operation.GET) <N extends FatherEdge> Iterator<? extends N> obtainSonEdges(Class<? extends N> type);