tinkerpop-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From spmalle...@apache.org
Subject [1/3] tinkerpop git commit: TINKERPOP-1791 Added GremlinDsl.AnonymousMethod annotation
Date Thu, 26 Oct 2017 10:45:33 GMT
Repository: tinkerpop
Updated Branches:
  refs/heads/master d7b389cbe -> 42b44f1f2


TINKERPOP-1791 Added GremlinDsl.AnonymousMethod annotation

Allows the ability to bypass the type inference system and provides for direct specification
of the type parameters when generating anonymous methods.


Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo
Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/daa442cd
Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/daa442cd
Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/daa442cd

Branch: refs/heads/master
Commit: daa442cd7e100a7f48541cf17bc29b17c68cb5a5
Parents: 3648f80
Author: Stephen Mallette <spmva@genoprime.com>
Authored: Fri Oct 20 08:58:52 2017 -0400
Committer: Stephen Mallette <spmva@genoprime.com>
Committed: Fri Oct 20 08:58:52 2017 -0400

----------------------------------------------------------------------
 CHANGELOG.asciidoc                              |  1 +
 docs/src/reference/the-traversal.asciidoc       |  5 +-
 .../upgrade/release-3.2.x-incubating.asciidoc   | 56 ++++++++++++++++++-
 .../src/main/java/SocialTraversalDsl.java       | 13 ++++-
 .../process/traversal/dsl/GremlinDsl.java       | 20 +++++++
 .../traversal/dsl/GremlinDslProcessor.java      | 58 ++++++++++++++++----
 .../traversal/dsl/SocialTraversalDsl.java       | 19 ++++++-
 7 files changed, 156 insertions(+), 16 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/daa442cd/CHANGELOG.asciidoc
----------------------------------------------------------------------
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 07f1d69..dd7624a 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -31,6 +31,7 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 * Fixed a bug where bytecode containing lambdas would randomly select a traversal source
from bindings.
 * Deprecated `GremlinScriptEngine.eval()` methods and replaced them with new overloads that
include the specific `TraversalSource` to bind to.
 * Added `GraphHelper.cloneElements(Graph original, Graph clone)` to the `gremlin-test` module
to quickly clone a graph.
+* Added `GremlinDsl.AnonymousMethod` annotation to help provide explicit types for anonymous
methods when the types are not easily inferred.
 * Bump to GMavenPlus 1.6.
 * Added better error message for illegal use of `repeat()`-step.
 * Fixed a bug in `RangeByIsCountStrategy` that led to unexpected behaviors when predicates
were used with floating point numbers.

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/daa442cd/docs/src/reference/the-traversal.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/reference/the-traversal.asciidoc b/docs/src/reference/the-traversal.asciidoc
index 167073a..632ed09 100644
--- a/docs/src/reference/the-traversal.asciidoc
+++ b/docs/src/reference/the-traversal.asciidoc
@@ -3034,7 +3034,10 @@ public interface SocialTraversalDsl<S, E> extends GraphTraversal.Admin<S,
E> {
 ----
 
 IMPORTANT: Follow the TinkerPop convention of using `<S,E>` in naming generics as those
conventions are taken into
-account when generating the anonymous traversal class.
+account when generating the anonymous traversal class. The processor attempts to infer the
appropriate type parameters
+when generating the anonymous traversal class. If it cannot do it correctly, it is possible
to avoid the inference by
+using the `GremlinDsl.AnonymousMethod` annotation on the DSL method. It allows explicit specification
of the types to
+use.
 
 The `@GremlinDsl` annotation is used by the link:https://docs.oracle.com/javase/8/docs/api/index.html?javax/annotation/processing/Processor.html[Java
Annotation Processor]
 to generate the boilerplate class structure required to properly use the DSL within the TinkerPop
framework. These

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/daa442cd/docs/src/upgrade/release-3.2.x-incubating.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/upgrade/release-3.2.x-incubating.asciidoc b/docs/src/upgrade/release-3.2.x-incubating.asciidoc
index 732e29e..50226bb 100644
--- a/docs/src/upgrade/release-3.2.x-incubating.asciidoc
+++ b/docs/src/upgrade/release-3.2.x-incubating.asciidoc
@@ -29,7 +29,7 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 
 Please see the link:https://github.com/apache/tinkerpop/blob/3.2.7/CHANGELOG.asciidoc#release-3-2-7[changelog]
for a complete list of all the modifications that are part of this release.
 
-=== Embedded Remote Connection
+==== Embedded Remote Connection
 
 As Gremlin Language Variants (GLVs) expand their usage and use of `withRemote()` becomes
more common, the need to mock
 the "remote" in unit tests increases. To simplify mocking in Java, the new `EmbeddedRemoteConnection`
provides a
@@ -37,6 +37,60 @@ simple way to provide a "remote" that is actually local to the same JVM.
 
 See: link:https://issues.apache.org/jira/browse/TINKERPOP-1756[TINKERPOP-1756]
 
+==== DSL Type Specification
+
+Prior to this version, the Java annotation processor for Gremlin DSLs has tried to infer
the appropriate type
+specifications when generating anonymous methods. It largely performed this inference on
simple conventions in the
+DSL method's template specification and there were times where it would fail. For example,
a method like this:
+
+[source,java]
+----
+public default GraphTraversal<S, E> person() {
+    return hasLabel("person");
+}
+----
+
+would generate an anonymous method like:
+
+[source,java]
+----
+public static <S> SocialGraphTraversal<S, E> person() {
+    return hasLabel("person");
+}
+----
+
+and, of course, generate a compile error and `E` was not recognized as a symbol. The preferred
generation would likely
+be:
+
+[source,java]
+----
+public static <S> SocialGraphTraversal<S, S> person() {
+    return hasLabel("person");
+}
+----
+
+To remedy this situation, a new annotation has been added which allows the user to control
the type specifications
+more directly providing a way to avoid/override the inference system:
+
+[source,java]
+----
+@GremlinDsl.AnonymousMethod(returnTypeParameters = {"A", "A"}, methodTypeParameters = {"A"})
+public default GraphTraversal<S, E> person() {
+    return hasLabel("person");
+}
+----
+
+which will then generate:
+
+[source,java]
+----
+public static <A> SocialGraphTraversal<A, A> person() {
+    return hasLabel("person");
+}
+----
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-1791[TINKERPOP-1791]
+
 ==== Specify a Cluster Object
 
 The `:remote connect` command can now take a pre-defined `Cluster` object as its argument
as opposed to a YAML

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/daa442cd/gremlin-archetype/gremlin-archetype-dsl/src/main/resources/archetype-resources/src/main/java/SocialTraversalDsl.java
----------------------------------------------------------------------
diff --git a/gremlin-archetype/gremlin-archetype-dsl/src/main/resources/archetype-resources/src/main/java/SocialTraversalDsl.java
b/gremlin-archetype/gremlin-archetype-dsl/src/main/resources/archetype-resources/src/main/java/SocialTraversalDsl.java
index 7f83152..e4d48bc 100644
--- a/gremlin-archetype/gremlin-archetype-dsl/src/main/resources/archetype-resources/src/main/java/SocialTraversalDsl.java
+++ b/gremlin-archetype/gremlin-archetype-dsl/src/main/resources/archetype-resources/src/main/java/SocialTraversalDsl.java
@@ -19,6 +19,7 @@
 package ${package};
 
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.GremlinDsl;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.GremlinDsl.AnonymousMethod;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
@@ -45,7 +46,7 @@ public interface SocialTraversalDsl<S, E> extends GraphTraversal.Admin<S,
E> {
      * @param personName the name of the person to filter on
      */
     public default GraphTraversal<S, Vertex> knows(String personName) {
-        return out("knows").hasLabel("person").has("name", personName);
+        return ((SocialTraversalDsl) out("knows")).person().has("name", personName);
     }
 
     /**
@@ -53,7 +54,7 @@ public interface SocialTraversalDsl<S, E> extends GraphTraversal.Admin<S,
E> {
      * those persons.
      */
     public default <E2 extends Number> GraphTraversal<S, E2> youngestFriendsAge()
{
-        return out("knows").hasLabel("person").values("age").min();
+        return ((SocialTraversalDsl) out("knows")).person().values("age").min();
     }
 
     /**
@@ -64,4 +65,12 @@ public interface SocialTraversalDsl<S, E> extends GraphTraversal.Admin<S,
E> {
     public default GraphTraversal<S, Long> createdAtLeast(int number) {
         return outE("created").count().is(P.gte(number));
     }
+
+    /**
+     * Filters objects by the "person" label. This step is designed to work with incoming
vertices.
+     */
+    @GremlinDsl.AnonymousMethod(returnTypeParameters = {"A", "A"}, methodTypeParameters =
{"A"})
+    public default GraphTraversal<S, E> person() {
+        return hasLabel("person");
+    }
 }

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/daa442cd/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/GremlinDsl.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/GremlinDsl.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/GremlinDsl.java
index d3a807f..700c8ee 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/GremlinDsl.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/GremlinDsl.java
@@ -74,4 +74,24 @@ public @interface GremlinDsl {
      * </ul>
      */
     public boolean generateDefaultMethods() default true;
+
+    /**
+     * Annotation that allows the user to directly override the type parameters on generated
anonymous methods. If this
+     * annotation is not specified then the processor will attempt to infer the correct type
parameters to use when
+     * generating the anonymous method representations of the DSL methods.
+     */
+    @Target(ElementType.METHOD)
+    @Retention(RetentionPolicy.CLASS)
+    public @interface AnonymousMethod {
+
+        /**
+         * The type parameters to apply to the return type of the method applied in the order
that they are specified.
+         */
+        public String[] returnTypeParameters() default {};
+
+        /**
+         * The type parameters to apply to the method in the order that they are specified.
+         */
+        public String[] methodTypeParameters() default {};
+    }
 }

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/daa442cd/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/GremlinDslProcessor.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/GremlinDslProcessor.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/GremlinDslProcessor.java
index 470dd4b..cdfad6a 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/GremlinDslProcessor.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/GremlinDslProcessor.java
@@ -61,9 +61,11 @@ import javax.tools.Diagnostic;
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Optional;
 import java.util.Set;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 /**
  * A custom Java annotation processor for the {@link GremlinDsl} annotation that helps to
generate DSLs classes.
@@ -135,24 +137,39 @@ public class GremlinDslProcessor extends AbstractProcessor {
 
         // process the methods of the GremlinDsl annotated class
         for (ExecutableElement templateMethod : findMethodsOfElement(ctx.annotatedDslType,
null)) {
+            final Optional<GremlinDsl.AnonymousMethod> methodAnnotation = Optional.ofNullable(templateMethod.getAnnotation(GremlinDsl.AnonymousMethod.class));
+
             final String methodName = templateMethod.getSimpleName().toString();
 
-            final TypeName returnType = getReturnTypeDefinition(ctx.traversalClassName, templateMethod);
+            // either use the direct return type of the DSL specification or override it
with specification from
+            // GremlinDsl.AnonymousMethod
+            final TypeName returnType = methodAnnotation.isPresent() && methodAnnotation.get().returnTypeParameters().length
> 0 ?
+                    getOverridenReturnTypeDefinition(ctx.traversalClassName, methodAnnotation.get().returnTypeParameters())
:
+                    getReturnTypeDefinition(ctx.traversalClassName, templateMethod);
+            
             final MethodSpec.Builder methodToAdd = MethodSpec.methodBuilder(methodName)
                     .addModifiers(Modifier.STATIC, Modifier.PUBLIC)
                     .addExceptions(templateMethod.getThrownTypes().stream().map(TypeName::get).collect(Collectors.toList()))
                     .returns(returnType);
 
-            templateMethod.getTypeParameters().forEach(tp -> methodToAdd.addTypeVariable(TypeVariableName.get(tp)));
-
-            // might have to deal with an "S" (in __ it's usually an "A") - how to make this
less bound to that convention?
-            final List<? extends TypeMirror> returnTypeArguments = getTypeArguments(templateMethod);
-            returnTypeArguments.stream().filter(rtm -> rtm instanceof TypeVariable).forEach(rtm
-> {
-                if (((TypeVariable) rtm).asElement().getSimpleName().contentEquals("S"))
-                    methodToAdd.addTypeVariable(TypeVariableName.get(((TypeVariable) rtm).asElement().getSimpleName().toString()));
-            });
+            // either use the method type parameter specified from the GremlinDsl.AnonymousMethod
or just infer them
+            // from the DSL specification. "inferring" relies on convention and sometimes
doesn't work for all cases.
+            final String startGeneric = methodAnnotation.isPresent() && methodAnnotation.get().methodTypeParameters().length
> 0 ?
+                    methodAnnotation.get().methodTypeParameters()[0] : "S";
+            if (methodAnnotation.isPresent() && methodAnnotation.get().methodTypeParameters().length
> 0)
+                Stream.of(methodAnnotation.get().methodTypeParameters()).map(TypeVariableName::get).forEach(methodToAdd::addTypeVariable);
+            else {
+                templateMethod.getTypeParameters().forEach(tp -> methodToAdd.addTypeVariable(TypeVariableName.get(tp)));
+
+                // might have to deal with an "S" (in __ it's usually an "A") - how to make
this less bound to that convention?
+                final List<? extends TypeMirror> returnTypeArguments = getTypeArguments(templateMethod);
+                returnTypeArguments.stream().filter(rtm -> rtm instanceof TypeVariable).forEach(rtm
-> {
+                    if (((TypeVariable) rtm).asElement().getSimpleName().contentEquals("S"))
+                        methodToAdd.addTypeVariable(TypeVariableName.get(((TypeVariable)
rtm).asElement().getSimpleName().toString()));
+                });
+            }
 
-            addMethodBody(methodToAdd, templateMethod, "return __.<S>start().$L(",
")", methodName);
+            addMethodBody(methodToAdd, templateMethod, "return __.<" + startGeneric +
">start().$L(", ")", methodName);
             anonymousClass.addMethod(methodToAdd.build());
         }
 
@@ -425,6 +442,27 @@ public class GremlinDslProcessor extends AbstractProcessor {
         methodToAdd.addStatement(body, statementArgs);
     }
 
+    private TypeName getOverridenReturnTypeDefinition(final ClassName returnClazz, final
String[] typeValues) {
+        return ParameterizedTypeName.get(returnClazz, Stream.of(typeValues).map(tv ->
{
+            try {
+                return ClassName.get(Class.forName(tv));
+            } catch (ClassNotFoundException cnfe) {
+                if (tv.contains("extends")) {
+                    final String[] sides = tv.toString().split(" extends ");
+                    final TypeVariableName name = TypeVariableName.get(sides[0]);
+                    try {
+                        name.withBounds(ClassName.get(Class.forName(sides[1])));
+                    } catch (Exception ex) {
+                        name.withBounds(TypeVariableName.get(sides[1]));
+                    }
+                    return name;
+                } else {
+                    return TypeVariableName.get(tv);
+                }
+            }
+        }).collect(Collectors.toList()).toArray(new TypeName[typeValues.length]));
+    }
+
     private TypeName getReturnTypeDefinition(final ClassName returnClazz, final ExecutableElement
templateMethod) {
         final List<? extends TypeMirror> returnTypeArguments = getTypeArguments(templateMethod);
 

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/daa442cd/gremlin-core/src/test/resources/org/apache/tinkerpop/gremlin/process/traversal/dsl/SocialTraversalDsl.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/test/resources/org/apache/tinkerpop/gremlin/process/traversal/dsl/SocialTraversalDsl.java
b/gremlin-core/src/test/resources/org/apache/tinkerpop/gremlin/process/traversal/dsl/SocialTraversalDsl.java
index 4c31330..66db128 100644
--- a/gremlin-core/src/test/resources/org/apache/tinkerpop/gremlin/process/traversal/dsl/SocialTraversalDsl.java
+++ b/gremlin-core/src/test/resources/org/apache/tinkerpop/gremlin/process/traversal/dsl/SocialTraversalDsl.java
@@ -18,9 +18,9 @@
  */
 package org.apache.tinkerpop.gremlin.process.traversal.dsl;
 
-import org.apache.tinkerpop.gremlin.process.traversal.dsl.GremlinDsl;
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.process.traversal.P;
 
 /**
  * @author Stephen Mallette (http://stephen.genoprime.com)
@@ -28,10 +28,25 @@ import org.apache.tinkerpop.gremlin.structure.Vertex;
 @GremlinDsl
 public interface SocialTraversalDsl<S, E> extends GraphTraversal.Admin<S, E>
{
     public default GraphTraversal<S, Vertex> knows(final String personName) {
-        return out("knows").hasLabel("person").has("name", personName);
+        return ((SocialTraversalDsl) out("knows")).person().has("name", personName);
     }
 
     public default <E2 extends Number> GraphTraversal<S, E2> meanAgeOfFriends()
{
+        return ((SocialTraversalDsl) out("knows")).person().values("age").mean();
+    }
+
+    @GremlinDsl.AnonymousMethod(returnTypeParameters = {"A", "A"}, methodTypeParameters =
{"A"})
+    public default GraphTraversal<S, E> person() {
+        return hasLabel("person");
+    }
+
+    @GremlinDsl.AnonymousMethod(returnTypeParameters = {"A", "org.apache.tinkerpop.gremlin.structure.Vertex"},
methodTypeParameters = {"A"})
+    public default GraphTraversal<S, Vertex> knowsOverride(final String personName)
{
+        return out("knows").hasLabel("person").has("name", personName);
+    }
+
+    @GremlinDsl.AnonymousMethod(returnTypeParameters = {"A", "E2"}, methodTypeParameters
= {"A", "E2 extends java.lang.Number"})
+    public default <E2 extends Number> GraphTraversal<S, E2> meanAgeOfFriendsOverride()
{
         return out("knows").hasLabel("person").values("age").mean();
     }
 }


Mime
View raw message