From 4f8ae9c83ddeab8fd8710a3755979872ed827ace Mon Sep 17 00:00:00 2001
From: Jeffrey Phillips Freeman <jeffrey.freeman@syncleus.com>
Date: Thu, 21 Sep 2017 22:55:42 -0400
Subject: [PATCH] feat: can now specify a pacage to scan instead of explicit
 classes.

---
 CHANGELOG.md                                  |  3 +-
 pom.xml                                       |  2 +-
 .../syncleus/ferma/DelegatingFramedGraph.java | 20 +++---
 .../com/syncleus/ferma/ReflectionCache.java   |  7 ++
 src/test/java/com/syncleus/ferma/Person.java  |  2 +
 .../ferma/PolymorphicTypeResolverTest.java    | 65 +++++++++++++++++++
 .../java/com/syncleus/ferma/Programmer.java   |  3 +
 .../AdjacencyMethodHandlerTest.java           |  1 +
 .../com/syncleus/ferma/annotations/God.java   |  1 +
 .../ferma/annotations/GodExtended.java        |  1 +
 .../ferma/annotations/GodIntermediate.java    |  1 +
 11 files changed, 96 insertions(+), 10 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index cee5412f..60b3727b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,10 +1,11 @@
 # Ferma Changelog
 
-## 3.1.1
+## 3.2.0
 
 * Added nexus staging deployment plugin.
 * Removed explicit version from licensing plugin.
 * Pom updated to require maven 3.0.4
+* Added additional constructor to DelegatingFramedGraph which accepts a package name to scan instead of needing to explicitly pass all the model's classes as a set.
 
 ## 3.1.0
 
diff --git a/pom.xml b/pom.xml
index 805dc805..724621bc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -12,7 +12,7 @@
     <groupId>com.syncleus.ferma</groupId>
     <artifactId>ferma</artifactId>
     <packaging>jar</packaging>
-    <version>3.1.1-SNAPSHOT</version>
+    <version>3.2.0-SNAPSHOT</version>
 
     <prerequisites>
         <maven>3.0.4</maven>
diff --git a/src/main/java/com/syncleus/ferma/DelegatingFramedGraph.java b/src/main/java/com/syncleus/ferma/DelegatingFramedGraph.java
index 8a12aec5..1a2d5f59 100644
--- a/src/main/java/com/syncleus/ferma/DelegatingFramedGraph.java
+++ b/src/main/java/com/syncleus/ferma/DelegatingFramedGraph.java
@@ -170,15 +170,19 @@ public class DelegatingFramedGraph<G extends Graph> implements WrappedFramedGrap
      *            The types to be consider for type resolution.
      */
     public DelegatingFramedGraph(final G delegate, final Collection<? extends Class<?>> types) {
-        this.delegate = delegate;
-
-        if( types == null )
-            throw new IllegalArgumentException("types can not be null");
+        this(delegate, new ReflectionCache(types), true, true);
+    }
 
-        final ReflectionCache reflections = new ReflectionCache(types);
-        this.defaultResolver = new PolymorphicTypeResolver(reflections);
-        this.untypedResolver = new UntypedTypeResolver();
-        this.builder = new AnnotationFrameFactory(reflections);
+    /**
+     * Construct a Typed framed graph with the specified type resolution and with annotation support
+     *
+     * @param delegate
+     *            The graph to wrap.
+     * @param modelPackage
+     *            The package scanned for classes to be considered for type resolution.
+     */
+    public DelegatingFramedGraph(final G delegate, final String modelPackage) {
+        this(delegate, new ReflectionCache(modelPackage), true, true);
     }
 
     /**
diff --git a/src/main/java/com/syncleus/ferma/ReflectionCache.java b/src/main/java/com/syncleus/ferma/ReflectionCache.java
index 8e640c88..3d2c735a 100644
--- a/src/main/java/com/syncleus/ferma/ReflectionCache.java
+++ b/src/main/java/com/syncleus/ferma/ReflectionCache.java
@@ -15,6 +15,7 @@
  */
 package com.syncleus.ferma;
 
+import com.syncleus.ferma.annotations.GraphElement;
 import org.reflections.Reflections;
 import org.reflections.scanners.SubTypesScanner;
 import org.reflections.scanners.TypeAnnotationsScanner;
@@ -44,6 +45,12 @@ public class ReflectionCache extends Reflections {
         this.hierarchy = constructHierarchy(annotatedTypes);
     }
 
+    public ReflectionCache(final String modelPackage) {
+        super(modelPackage);
+
+        this.hierarchy = constructHierarchy(this.getTypesAnnotatedWith(GraphElement.class));
+    }
+
     public Set<? extends String> getSubTypeNames(final Class<?> type) {
         Set<String> subtypes = this.hierarchy.get(type.getName());
         if (subtypes == null)
diff --git a/src/test/java/com/syncleus/ferma/Person.java b/src/test/java/com/syncleus/ferma/Person.java
index 108cbf9b..68589e24 100644
--- a/src/test/java/com/syncleus/ferma/Person.java
+++ b/src/test/java/com/syncleus/ferma/Person.java
@@ -17,6 +17,7 @@ package com.syncleus.ferma;
 
 import java.util.function.Function;
 import com.google.common.collect.Lists;
+import com.syncleus.ferma.annotations.GraphElement;
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
 import org.apache.tinkerpop.gremlin.structure.Element;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
@@ -25,6 +26,7 @@ import javax.annotation.Nullable;
 import java.util.Iterator;
 import java.util.List;
 
+@GraphElement
 public class Person extends AbstractVertexFrame {
     static final ClassInitializer<Person> DEFAULT_INITIALIZER = new DefaultClassInitializer(Person.class);
 
diff --git a/src/test/java/com/syncleus/ferma/PolymorphicTypeResolverTest.java b/src/test/java/com/syncleus/ferma/PolymorphicTypeResolverTest.java
index dd671de4..5b9f2421 100644
--- a/src/test/java/com/syncleus/ferma/PolymorphicTypeResolverTest.java
+++ b/src/test/java/com/syncleus/ferma/PolymorphicTypeResolverTest.java
@@ -16,6 +16,9 @@
 package com.syncleus.ferma;
 
 import java.util.function.Function;
+
+import com.syncleus.ferma.annotations.God;
+import com.syncleus.ferma.annotations.GodExtended;
 import com.syncleus.ferma.typeresolvers.PolymorphicTypeResolver;
 import com.syncleus.ferma.framefactories.annotation.AnnotationFrameFactory;
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
@@ -32,6 +35,7 @@ import java.util.*;
 public class PolymorphicTypeResolverTest {
 
     private static final Set<Class<?>> TEST_TYPES = new HashSet<>(Arrays.asList(new Class<?>[]{Person.class, Programmer.class}));
+    private static final String TEST_MODEL_PACKAGE = "com.syncleus.ferma";
     private static final String CUSTOM_TYPE_KEY = "some_custom_type_key";
 
     @Test
@@ -53,6 +57,67 @@ public class PolymorphicTypeResolverTest {
         final Person person = framedGraph.traverse(input -> input.V()).next(Person.class);
         Assert.assertFalse(person instanceof Programmer);
     }
+
+    @Test
+    public void testHasTypeParentFromPackage() {
+        final Graph godGraph = TinkerGraph.open();
+        final FramedGraph framedGraph = new DelegatingFramedGraph(godGraph, TEST_MODEL_PACKAGE);
+
+        //add a single node to the graph, a programmer.
+        framedGraph.addFramedVertex(Programmer.class);
+
+        //make sure the newly added node is actually a programmer
+        final Person programmer = framedGraph.traverse(input -> framedGraph.getTypeResolver().hasType(input.V(), Person.class)).next(Person.class);
+        Assert.assertTrue(programmer instanceof Programmer);
+
+        //change the type resolution to person
+        programmer.setTypeResolution(Person.class);
+
+        //make sure the newly added node is not actually a programmer
+        final Person person = framedGraph.traverse(input -> input.V()).next(Person.class);
+        Assert.assertFalse(person instanceof Programmer);
+    }
+
+    @Test
+    public void testHasTypeChildFromPackage() {
+        final Graph godGraph = TinkerGraph.open();
+        final FramedGraph framedGraph = new DelegatingFramedGraph(godGraph, TEST_MODEL_PACKAGE);
+
+        //add a single node to the graph, a programmer.
+        framedGraph.addFramedVertex(Programmer.class);
+
+        //make sure the newly added node is actually a programmer
+        final Person programmer = framedGraph.traverse(input -> framedGraph.getTypeResolver().hasType(input.V(), Programmer.class)).next(Person.class);
+        Assert.assertTrue(programmer instanceof Programmer);
+
+        //change the type resolution to person
+        programmer.setTypeResolution(Person.class);
+
+        //make sure the newly added node is not actually a programmer
+        final Person person = framedGraph.traverse(input -> input.V()).next(Person.class);
+        Assert.assertFalse(person instanceof Programmer);
+    }
+
+
+    @Test
+    public void testHasTypeParentFromPackageRecursively() {
+        final Graph godGraph = TinkerGraph.open();
+        final FramedGraph framedGraph = new DelegatingFramedGraph(godGraph, TEST_MODEL_PACKAGE);
+
+        //add a single node to the graph, a programmer.
+        framedGraph.addFramedVertex(GodExtended.class);
+
+        //make sure the newly added node is actually a programmer
+        final God god = framedGraph.traverse(input -> framedGraph.getTypeResolver().hasType(input.V(), God.class)).next(God.class);
+        Assert.assertTrue(god instanceof GodExtended);
+
+        //change the type resolution to person
+        god.setTypeResolution(God.class);
+
+        //make sure the newly added node is not actually a programmer
+        final Person godAgain = framedGraph.traverse(input -> input.V()).next(Person.class);
+        Assert.assertFalse(godAgain instanceof GodExtended);
+    }
     
     @Test
     public void testCustomTypeKey() {
diff --git a/src/test/java/com/syncleus/ferma/Programmer.java b/src/test/java/com/syncleus/ferma/Programmer.java
index efeecd09..98486c69 100644
--- a/src/test/java/com/syncleus/ferma/Programmer.java
+++ b/src/test/java/com/syncleus/ferma/Programmer.java
@@ -15,6 +15,9 @@
  */
 package com.syncleus.ferma;
 
+import com.syncleus.ferma.annotations.GraphElement;
+
+@GraphElement
 public class Programmer extends Person {
     static final ClassInitializer<Programmer> DEFAULT_INITIALIZER = new DefaultClassInitializer(Programmer.class);
 }
diff --git a/src/test/java/com/syncleus/ferma/annotations/AdjacencyMethodHandlerTest.java b/src/test/java/com/syncleus/ferma/annotations/AdjacencyMethodHandlerTest.java
index 32545ac3..e3c1d44f 100644
--- a/src/test/java/com/syncleus/ferma/annotations/AdjacencyMethodHandlerTest.java
+++ b/src/test/java/com/syncleus/ferma/annotations/AdjacencyMethodHandlerTest.java
@@ -30,6 +30,7 @@ import java.util.*;
 public class AdjacencyMethodHandlerTest {
 
     private static final Set<Class<?>> TEST_TYPES = new HashSet<>(Arrays.asList(new Class<?>[]{God.class, FatherEdge.class, GodExtended.class, GodAlternative.class}));
+    private static final String TEST_MODEL_PACKAGE = "com.syncleus.ferma";
 
     @Test
     public void testGetSonsDefault() {
diff --git a/src/test/java/com/syncleus/ferma/annotations/God.java b/src/test/java/com/syncleus/ferma/annotations/God.java
index 42fcaf22..e783f9a6 100644
--- a/src/test/java/com/syncleus/ferma/annotations/God.java
+++ b/src/test/java/com/syncleus/ferma/annotations/God.java
@@ -20,6 +20,7 @@ import org.apache.tinkerpop.gremlin.structure.Direction;
 
 import java.util.Iterator;
 
+@GraphElement
 public interface God extends VertexFrame {
     static final ClassInitializer<God> DEFAULT_INITIALIZER = new DefaultClassInitializer(God.class);
 
diff --git a/src/test/java/com/syncleus/ferma/annotations/GodExtended.java b/src/test/java/com/syncleus/ferma/annotations/GodExtended.java
index 9f14bbe0..dfb497fc 100644
--- a/src/test/java/com/syncleus/ferma/annotations/GodExtended.java
+++ b/src/test/java/com/syncleus/ferma/annotations/GodExtended.java
@@ -18,6 +18,7 @@ package com.syncleus.ferma.annotations;
 import com.syncleus.ferma.ClassInitializer;
 import com.syncleus.ferma.DefaultClassInitializer;
 
+@GraphElement
 public interface GodExtended extends GodIntermediate {
     static final ClassInitializer<GodExtended> DEFAULT_INITIALIZER = new DefaultClassInitializer(GodExtended.class);
 
diff --git a/src/test/java/com/syncleus/ferma/annotations/GodIntermediate.java b/src/test/java/com/syncleus/ferma/annotations/GodIntermediate.java
index 04ce2289..9e6a49f5 100644
--- a/src/test/java/com/syncleus/ferma/annotations/GodIntermediate.java
+++ b/src/test/java/com/syncleus/ferma/annotations/GodIntermediate.java
@@ -18,6 +18,7 @@ package com.syncleus.ferma.annotations;
 import com.syncleus.ferma.ClassInitializer;
 import com.syncleus.ferma.DefaultClassInitializer;
 
+@GraphElement
 public interface GodIntermediate extends God {
     static final ClassInitializer<GodIntermediate> DEFAULT_INITIALIZER = new DefaultClassInitializer(GodIntermediate.class);
 }
-- 
GitLab