You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by sp...@apache.org on 2018/10/03 20:10:35 UTC

[1/5] tinkerpop git commit: TINKERPOP-2040 Updated changelog

Repository: tinkerpop
Updated Branches:
  refs/heads/tp32 fcbce509f -> 88d6f7786


TINKERPOP-2040 Updated changelog


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

Branch: refs/heads/tp32
Commit: 92c74c3d7ffc25b234187ef2459f8d3d529ac8db
Parents: e5c2e9c
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Mon Oct 1 09:12:30 2018 -0400
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Mon Oct 1 16:18:28 2018 -0400

----------------------------------------------------------------------
 CHANGELOG.asciidoc | 2 ++
 1 file changed, 2 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/92c74c3d/CHANGELOG.asciidoc
----------------------------------------------------------------------
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 6d9770f..9160d09 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -37,6 +37,8 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 * Added synchronized `Map` to Gryo 1.0 registrations.
 * Added `Triple` to Gryo 1.0 registrations.
 * Improved escaping of special characters in strings passed to the `GroovyTranslator`.
+* Improved ability of `GroovyTranslator` to handle more types supported by GraphSON.
+* Improved ability of `GroovyTranslator` to handle custom types.
 * Added better internal processing of `Column` in `by(Function)`.
 * Added `hasNext()` support on `Traversal` for `gremlin-python`.
 * Added support for additional extended types in Gremlin.Net with `decimal`, `TimeSpan`, `BigInteger`, `byte`, `byte[]`, `char` and `short`.


[3/5] tinkerpop git commit: TINKERPOP-2040 Provide improved type handling for GroovyTranslator

Posted by sp...@apache.org.
TINKERPOP-2040 Provide improved type handling for GroovyTranslator

Refactored testing and added support for UUID, Date, Timestamp as well as a method to override standard type handling.


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

Branch: refs/heads/tp32
Commit: 774371dc38d2d73be142eaa69c958226ac660892
Parents: 0403156
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Thu Sep 20 11:53:07 2018 -0400
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Mon Oct 1 16:18:28 2018 -0400

----------------------------------------------------------------------
 .../gremlin/process/traversal/Translator.java   |  34 ++++
 .../groovy/jsr223/GroovyTranslatorTest.java     | 162 +++++++++++++++----
 .../gremlin/groovy/jsr223/GroovyTranslator.java |  32 +++-
 3 files changed, 195 insertions(+), 33 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/774371dc/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Translator.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Translator.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Translator.java
index 7e97fb3..0346092 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Translator.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Translator.java
@@ -19,6 +19,8 @@
 
 package org.apache.tinkerpop.gremlin.process.traversal;
 
+import java.util.function.UnaryOperator;
+
 /**
  * A Translator will translate {@link Bytecode} into another representation. That representation may be a
  * Java instance via {@link StepTranslator} or a String script in some language via {@link ScriptTranslator}.
@@ -55,10 +57,42 @@ public interface Translator<S, T> {
 
     ///
 
+    /**
+     * Translates bytecode to a string representation.
+     */
     public interface ScriptTranslator extends Translator<String, String> {
 
+        /**
+         * Provides a way to customize and override the standard translation process. A {@link ScriptTranslator}
+         * implementation can choose to expose a way to accept a {@code TypeTranslator} which will convert an incoming
+         * object to a different form which will then be normally processed or can return a {@link Handled} object
+         * with the already translated script.
+         */
+        public interface TypeTranslator extends UnaryOperator<Object> {
+            public static TypeTranslator identity() {
+                return t -> t;
+            }
+        }
+
+        /**
+         * Contains a completed type translation from the {@link TypeTranslator}.
+         */
+        public class Handled {
+            private final String translation;
+
+            public Handled(final String translation) {
+                this.translation = translation;
+            }
+
+            public String getTranslation() {
+                return translation;
+            }
+        }
     }
 
+    /**
+     * Translates bytecode to actual steps.
+     */
     public interface StepTranslator<S extends TraversalSource, T extends Traversal.Admin<?, ?>> extends Translator<S, T> {
 
     }

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/774371dc/gremlin-groovy-test/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslatorTest.java
----------------------------------------------------------------------
diff --git a/gremlin-groovy-test/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslatorTest.java b/gremlin-groovy-test/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslatorTest.java
index 942f1a5..686c7bd 100644
--- a/gremlin-groovy-test/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslatorTest.java
+++ b/gremlin-groovy-test/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslatorTest.java
@@ -22,6 +22,7 @@ package org.apache.tinkerpop.gremlin.groovy.jsr223;
 import org.apache.commons.configuration.MapConfiguration;
 import org.apache.tinkerpop.gremlin.AbstractGremlinTest;
 import org.apache.tinkerpop.gremlin.LoadGraphWith;
+import org.apache.tinkerpop.gremlin.process.traversal.Translator;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
@@ -32,47 +33,56 @@ import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.Transl
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy;
 import org.apache.tinkerpop.gremlin.structure.Edge;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.util.ElementHelper;
 import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedEdge;
 import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertex;
 import org.apache.tinkerpop.gremlin.util.function.Lambda;
 import org.junit.Test;
 
 import javax.script.Bindings;
+import javax.script.ScriptException;
 import javax.script.SimpleBindings;
+import java.sql.Timestamp;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Calendar;
 import java.util.Collections;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.UUID;
 import java.util.function.Function;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
 
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
  */
 public class GroovyTranslatorTest extends AbstractGremlinTest {
 
+    private static final GremlinGroovyScriptEngine engine = new GremlinGroovyScriptEngine();
+
     @Test
     @LoadGraphWith(LoadGraphWith.GraphData.MODERN)
     public void shouldHandleStrategies() throws Exception {
-        GraphTraversalSource g = graph.traversal();
-        g = g.withStrategies(SubgraphStrategy.create(new MapConfiguration(new HashMap<String, Object>() {{
+        final GraphTraversalSource g = graph.traversal().withStrategies(SubgraphStrategy.create(new MapConfiguration(new HashMap<String, Object>() {{
             put(SubgraphStrategy.VERTICES, __.has("name", "marko"));
         }})));
         final Bindings bindings = new SimpleBindings();
         bindings.put("g", g);
-        Traversal.Admin<Vertex, Object> traversal = new GremlinGroovyScriptEngine().eval(g.V().values("name").asAdmin().getBytecode(), bindings, "g");
+        Traversal.Admin<Vertex, Object> traversal = engine.eval(g.V().values("name").asAdmin().getBytecode(), bindings, "g");
         assertEquals("marko", traversal.next());
         assertFalse(traversal.hasNext());
         //
-        traversal = new GremlinGroovyScriptEngine().eval(g.withoutStrategies(SubgraphStrategy.class).V().count().asAdmin().getBytecode(), bindings, "g");
+        traversal = engine.eval(g.withoutStrategies(SubgraphStrategy.class).V().count().asAdmin().getBytecode(), bindings, "g");
         assertEquals(new Long(6), traversal.next());
         assertFalse(traversal.hasNext());
         //
-        traversal = new GremlinGroovyScriptEngine().eval(g.withStrategies(SubgraphStrategy.create(new MapConfiguration(new HashMap<String, Object>() {{
+        traversal = engine.eval(g.withStrategies(SubgraphStrategy.create(new MapConfiguration(new HashMap<String, Object>() {{
             put(SubgraphStrategy.VERTICES, __.has("name", "marko"));
         }})), ReadOnlyStrategy.instance()).V().values("name").asAdmin().getBytecode(), bindings, "g");
         assertEquals("marko", traversal.next());
@@ -81,10 +91,10 @@ public class GroovyTranslatorTest extends AbstractGremlinTest {
 
     @Test
     @LoadGraphWith(LoadGraphWith.GraphData.MODERN)
-    public void shouldSupportStringSupplierLambdas() throws Exception {
+    public void shouldSupportStringSupplierLambdas() {
         GraphTraversalSource g = graph.traversal();
         g = g.withStrategies(new TranslationStrategy(g, GroovyTranslator.of("g")));
-        GraphTraversal.Admin<Vertex, Integer> t = g.withSideEffect("lengthSum", 0).withSack(1)
+        final GraphTraversal.Admin<Vertex, Integer> t = g.withSideEffect("lengthSum", 0).withSack(1)
                 .V()
                 .filter(Lambda.predicate("it.get().label().equals('person')"))
                 .flatMap(Lambda.function("it.get().vertices(Direction.OUT)"))
@@ -130,25 +140,77 @@ public class GroovyTranslatorTest extends AbstractGremlinTest {
                         ".order().by({a,b -> a <=> b})" +
                         ".sack({ a,b -> a + b })",
                 script);
+        assertThatScriptOk(script, "g", g);
     }
 
     @Test
     @LoadGraphWith(LoadGraphWith.GraphData.MODERN)
     public void shouldHandleMaps() {
-        final GraphTraversalSource g = graph.traversal();
         final String script = GroovyTranslator.of("g").translate(g.V().id().is(new LinkedHashMap<Object,Object>() {{
             put(3, "32");
             put(Arrays.asList(1, 2, 3.1d), 4);
         }}).asAdmin().getBytecode());
         assertEquals("g.V().id().is([((int) 3):(\"32\"),([(int) 1, (int) 2, 3.1d]):((int) 4)])", script);
+        assertThatScriptOk(script, "g", g);
     }
 
     @Test
     public void shouldHandleEmptyMaps() {
         final Function identity = new Lambda.OneArgLambda("it.get()", "gremlin-groovy");
-        final GraphTraversalSource g = graph.traversal();
         final String script = GroovyTranslator.of("g").translate(g.inject(Collections.emptyMap()).map(identity).asAdmin().getBytecode());
         assertEquals("g.inject([]).map({it.get()})", script);
+        assertThatScriptOk(script, "g", g);
+    }
+
+    @Test
+    public void shouldHandleDate() {
+        final Calendar c = Calendar.getInstance();
+        c.set(1975, Calendar.SEPTEMBER, 7);
+        final Date d = c.getTime();
+        assertTranslation(String.format("new java.util.Date(%s)", d.getTime()), d);
+    }
+
+    @Test
+    public void shouldHandleTimestamp() {
+        final Calendar c = Calendar.getInstance();
+        c.set(1975, Calendar.SEPTEMBER, 7);
+        final Timestamp t = new Timestamp(c.getTime().getTime());
+        assertTranslation(String.format("new java.sql.Timestamp(%s)", t.getTime()), t);
+    }
+
+    @Test
+    public void shouldHandleUuid() {
+        final UUID uuid = UUID.fromString("ffffffff-fd49-1e4b-0000-00000d4b8a1d");
+        assertTranslation(String.format("java.util.UUID.fromString('%s')", uuid), uuid);
+    }
+
+    @Test
+    public void shouldOverrideDefaultTypeTranslationWithSomethingBonkers() {
+        final String thingToSuffixAllStringsWith = "-why-would-anyone-do-this";
+        final String script = GroovyTranslator.of("g", x -> x instanceof String ? x + thingToSuffixAllStringsWith : x).
+                translate(g.inject("yyy", "xxx").asAdmin().getBytecode());
+        assertEquals(String.format("g.inject(\"yyy%s\",\"xxx%s\")", thingToSuffixAllStringsWith, thingToSuffixAllStringsWith), script);
+        assertThatScriptOk(script, "g", g);
+    }
+
+    @Test
+    public void shouldIncludeCustomTypeTranslationForSomethingSilly() {
+        final SillyClass notSillyEnough = SillyClass.from("not silly enough", 100);
+        final GraphTraversalSource g = graph.traversal();
+
+        // without type translation we get uglinesss
+        final String scriptBad = GroovyTranslator.of("g").
+                translate(g.inject(notSillyEnough).asAdmin().getBytecode());
+        assertEquals(String.format("g.inject(%s)", "not silly enough:100"), scriptBad);
+
+        // with type translation we get valid gremlin
+        final String scriptGood = GroovyTranslator.of("g",
+                x -> x instanceof SillyClass ?
+                        new Translator.ScriptTranslator.Handled(String.format("org.apache.tinkerpop.gremlin.groovy.jsr223.GroovyTranslatorTest.SillyClass.from('%s', (int) %s)",
+                        ((SillyClass) x).getX(), ((SillyClass) x).getY())) : x).
+                translate(g.inject(notSillyEnough).asAdmin().getBytecode());
+        assertEquals(String.format("g.inject(org.apache.tinkerpop.gremlin.groovy.jsr223.GroovyTranslatorTest.SillyClass.from('%s', (int) %s))", notSillyEnough.getX(), notSillyEnough.getY()), scriptGood);
+        assertThatScriptOk(scriptGood, "g", g);
     }
 
     @Test
@@ -158,20 +220,8 @@ public class GroovyTranslatorTest extends AbstractGremlinTest {
 
     @Test
     public void shouldEscapeStrings() {
-        final GraphTraversalSource g = graph.traversal();
-        final String script = GroovyTranslator.of("g").translate(g.addV("customer")
-                .property("customer_id", 501L)
-                .property("name", "Foo\u0020Bar")
-                .property("age", 25)
-                .property("special", "`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?")
-                .asAdmin().getBytecode());
-
-        assertEquals("g.addV(\"customer\")" +
-                        ".property(\"customer_id\",501L)" +
-                        ".property(\"name\",\"Foo Bar\")" +
-                        ".property(\"age\",(int) 25)" +
-                        ".property(\"special\",\"\"\"`~!@#\\$%^&*()-_=+[{]}\\\\|;:'\\\",<.>/?\"\"\")",
-                script);
+        assertTranslation("501L,\"Foo Bar\",(int) 25,\"\"\"`~!@#\\$%^&*()-_=+[{]}\\\\|;:'\\\",<.>/?\"\"\"",
+                501L, "Foo\u0020Bar", 25, "`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?");
     }
 
     @Test
@@ -181,33 +231,87 @@ public class GroovyTranslatorTest extends AbstractGremlinTest {
         final Object id1 = "customer:10:foo\u0020bar\u0020\u0024100#90"; // customer:10:foo bar $100#90
         final Vertex vertex1 = DetachedVertex.build().setLabel("customer").setId(id1)
                 .create();
-        final String script1 = GroovyTranslator.of("g").translate(g.V(vertex1).asAdmin().getBytecode());
-        assertEquals("g.V(new org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertex(" +
+        final String script1 = GroovyTranslator.of("g").translate(g.inject(vertex1).asAdmin().getBytecode());
+        assertEquals("g.inject(new org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertex(" +
                         "\"customer:10:foo bar \\$100#90\"," +
                         "\"customer\", Collections.emptyMap()))",
                 script1);
+        assertThatScriptOk(script1, "g", g);
 
         final Object id2 = "user:20:foo\\u0020bar\\u005c\\u0022mr\\u005c\\u0022\\u00241000#50"; // user:20:foo\u0020bar\u005c\u0022mr\u005c\u0022\u00241000#50
         final Vertex vertex2 = DetachedVertex.build().setLabel("user").setId(id2)
                 .create();
-        final String script2 = GroovyTranslator.of("g").translate(g.V(vertex2).asAdmin().getBytecode());
-        assertEquals("g.V(new org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertex(" +
+        final String script2 = GroovyTranslator.of("g").translate(g.inject(vertex2).asAdmin().getBytecode());
+        assertEquals("g.inject(new org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertex(" +
                         "\"user:20:foo\\\\u0020bar\\\\u005c\\\\u0022mr\\\\u005c\\\\u0022\\\\u00241000#50\"," +
                         "\"user\", Collections.emptyMap()))",
                 script2);
+        assertThatScriptOk(script2, "g", g);
 
         final Object id3 = "knows:30:foo\u0020bar\u0020\u0024100:\\u0020\\u0024500#70";
         final Edge edge = DetachedEdge.build().setLabel("knows").setId(id3)
                 .setOutV((DetachedVertex) vertex1)
                 .setInV((DetachedVertex) vertex2)
                 .create();
-        final String script3 = GroovyTranslator.of("g").translate(g.E(edge).asAdmin().getBytecode());
-        assertEquals("g.E(new org.apache.tinkerpop.gremlin.structure.util.detached.DetachedEdge(" +
+        final String script3 = GroovyTranslator.of("g").translate(g.inject(edge).asAdmin().getBytecode());
+        assertEquals("g.inject(new org.apache.tinkerpop.gremlin.structure.util.detached.DetachedEdge(" +
                         "\"knows:30:foo bar \\$100:\\\\u0020\\\\u0024500#70\"," +
                         "\"knows\",Collections.emptyMap()," +
                         "\"customer:10:foo bar \\$100#90\",\"customer\"," +
                         "\"user:20:foo\\\\u0020bar\\\\u005c\\\\u0022mr\\\\u005c\\\\u0022\\\\u00241000#50\",\"user\"))",
                 script3);
+        assertThatScriptOk(script3, "g", g);
+    }
+
+    public static Object eval(final String s, final Object... args) throws ScriptException {
+        return engine.eval(s, new SimpleBindings(ElementHelper.asMap(args)));
+    }
+
+    public static Object eval(final String s, final Bindings b) throws ScriptException {
+        return engine.eval(s, b);
+    }
+
+    private void assertTranslation(final String expectedTranslation, final Object... objs) {
+        final String script = GroovyTranslator.of("g").translate(g.inject(objs).asAdmin().getBytecode());
+        assertEquals(String.format("g.inject(%s)", expectedTranslation), script);
+        assertThatScriptOk(script, "g", g);
+    }
+
+    private void assertThatScriptOk(final String s, final Object... args) {
+        try {
+            assertNotNull(eval(s, args));
+        } catch (ScriptException se) {
+            se.printStackTrace();
+            fail("Script should have eval'd");
+        }
+    }
+
+    public static class SillyClass {
+
+        private final String x;
+        private final int y;
+
+        private SillyClass(final String x, final int y) {
+            this.x = x;
+            this.y = y;
+        }
+
+        public static SillyClass from(final String x, final int y) {
+            return new SillyClass(x, y);
+        }
+
+        public String getX() {
+            return x;
+        }
+
+        public int getY() {
+            return y;
+        }
+
+        @Override
+        public String toString() {
+            return x + ":" + String.valueOf(y);
+        }
     }
 
 }

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/774371dc/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslator.java
----------------------------------------------------------------------
diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslator.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslator.java
index 0452e0b..1d122c6 100644
--- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslator.java
+++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslator.java
@@ -39,11 +39,16 @@ import org.apache.tinkerpop.gremlin.structure.VertexProperty;
 import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
 import org.apache.tinkerpop.gremlin.util.function.Lambda;
 
+import java.sql.Timestamp;
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
+import java.util.UUID;
+import java.util.function.UnaryOperator;
 
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
@@ -53,13 +58,19 @@ public final class GroovyTranslator implements Translator.ScriptTranslator {
     private static final boolean IS_TESTING = Boolean.valueOf(System.getProperty("is.testing", "false"));
 
     private final String traversalSource;
+    private final TypeTranslator typeTranslator;
 
-    private GroovyTranslator(final String traversalSource) {
+    private GroovyTranslator(final String traversalSource, final TypeTranslator typeTranslator) {
         this.traversalSource = traversalSource;
+        this.typeTranslator = typeTranslator;
     }
 
     public static final GroovyTranslator of(final String traversalSource) {
-        return new GroovyTranslator(traversalSource);
+        return of(traversalSource, TypeTranslator.identity());
+    }
+
+    public static final GroovyTranslator of(final String traversalSource, final TypeTranslator typeTranslator) {
+        return new GroovyTranslator(traversalSource, Optional.ofNullable(typeTranslator).orElse(TypeTranslator.identity()));
     }
 
     ///////
@@ -108,8 +119,15 @@ public final class GroovyTranslator implements Translator.ScriptTranslator {
         return traversalScript.toString();
     }
 
-    private String convertToString(final Object object) {
-        if (object instanceof Bytecode.Binding)
+    private String convertToString(final Object o) {
+        // a TypeTranslator that returns Handled means that the typetranslator figure out how to convert the
+        // object to a string and it should be used as-is, otherwise it gets passed down the line through the normal
+        // process
+        final Object object = typeTranslator.apply(o);
+
+        if (object instanceof Handled)
+            return ((Handled) object).getTranslation();
+        else if (object instanceof Bytecode.Binding)
             return ((Bytecode.Binding) object).variable();
         else if (object instanceof Bytecode)
             return this.internalTranslate("__", (Bytecode) object);
@@ -155,6 +173,12 @@ public final class GroovyTranslator implements Translator.ScriptTranslator {
             return "(int) " + object;
         else if (object instanceof Class)
             return ((Class) object).getCanonicalName();
+        else if (object instanceof Timestamp)
+            return "new java.sql.Timestamp(" + ((Timestamp) object).getTime() + ")";
+        else if (object instanceof Date)
+            return "new java.util.Date(" + ((Date) object).getTime() + ")";
+        else if (object instanceof UUID)
+            return "java.util.UUID.fromString('" + object.toString() + "')";
         else if (object instanceof P)
             return convertPToString((P) object, new StringBuilder()).toString();
         else if (object instanceof SackFunctions.Barrier)


[5/5] tinkerpop git commit: Merge branch 'TINKERPOP-2040' into tp32

Posted by sp...@apache.org.
Merge branch 'TINKERPOP-2040' into tp32


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

Branch: refs/heads/tp32
Commit: 88d6f778694e6c65ddde6fdcc18948327e01914a
Parents: fcbce50 92c74c3
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Wed Oct 3 15:28:58 2018 -0400
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Wed Oct 3 15:28:58 2018 -0400

----------------------------------------------------------------------
 CHANGELOG.asciidoc                              |   2 +
 .../gremlin/jsr223/TranslatorCustomizer.java    |  38 ++++
 .../gremlin/process/traversal/Translator.java   |  34 +++
 .../groovy/jsr223/GroovyTranslatorTest.java     | 213 ++++++++++++++++---
 .../jsr223/GremlinGroovyScriptEngine.java       |  15 +-
 .../gremlin/groovy/jsr223/GroovyTranslator.java |  34 ++-
 6 files changed, 301 insertions(+), 35 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/88d6f778/CHANGELOG.asciidoc
----------------------------------------------------------------------


[4/5] tinkerpop git commit: TINKERPOP-2040 Added more tests to cover more GraphSON types

Posted by sp...@apache.org.
TINKERPOP-2040 Added more tests to cover more GraphSON types


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

Branch: refs/heads/tp32
Commit: 67e2ee85dd8048ca21a72889cf8a0218f2add081
Parents: 774371d
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Tue Sep 25 13:46:38 2018 -0400
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Mon Oct 1 16:18:28 2018 -0400

----------------------------------------------------------------------
 .../groovy/jsr223/GroovyTranslatorTest.java     | 30 ++++++++++++++++++++
 .../gremlin/groovy/jsr223/GroovyTranslator.java |  4 +++
 2 files changed, 34 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/67e2ee85/gremlin-groovy-test/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslatorTest.java
----------------------------------------------------------------------
diff --git a/gremlin-groovy-test/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslatorTest.java b/gremlin-groovy-test/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslatorTest.java
index 686c7bd..c224642 100644
--- a/gremlin-groovy-test/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslatorTest.java
+++ b/gremlin-groovy-test/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslatorTest.java
@@ -22,6 +22,9 @@ package org.apache.tinkerpop.gremlin.groovy.jsr223;
 import org.apache.commons.configuration.MapConfiguration;
 import org.apache.tinkerpop.gremlin.AbstractGremlinTest;
 import org.apache.tinkerpop.gremlin.LoadGraphWith;
+import org.apache.tinkerpop.gremlin.process.traversal.Order;
+import org.apache.tinkerpop.gremlin.process.traversal.Pop;
+import org.apache.tinkerpop.gremlin.process.traversal.Scope;
 import org.apache.tinkerpop.gremlin.process.traversal.Translator;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
@@ -31,6 +34,8 @@ import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SubgraphStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.TranslationStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy;
+import org.apache.tinkerpop.gremlin.structure.Column;
+import org.apache.tinkerpop.gremlin.structure.Direction;
 import org.apache.tinkerpop.gremlin.structure.Edge;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
 import org.apache.tinkerpop.gremlin.structure.util.ElementHelper;
@@ -185,6 +190,31 @@ public class GroovyTranslatorTest extends AbstractGremlinTest {
     }
 
     @Test
+    public void shouldHandleColumn() {
+        assertTranslation("Column.keys", Column.keys);
+    }
+
+    @Test
+    public void shouldHandleDirection() {
+        assertTranslation("Direction.BOTH", Direction.BOTH);
+    }
+
+    @Test
+    public void shouldHandleOrder() {
+        assertTranslation("Order.decr", Order.decr);
+    }
+
+    @Test
+    public void shouldHandlePop() {
+        assertTranslation("Pop.last", Pop.last);
+    }
+
+    @Test
+    public void shouldHandleScope() {
+        assertTranslation("Scope.local", Scope.local);
+    }
+
+    @Test
     public void shouldOverrideDefaultTypeTranslationWithSomethingBonkers() {
         final String thingToSuffixAllStringsWith = "-why-would-anyone-do-this";
         final String script = GroovyTranslator.of("g", x -> x instanceof String ? x + thingToSuffixAllStringsWith : x).

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/67e2ee85/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslator.java
----------------------------------------------------------------------
diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslator.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslator.java
index 1d122c6..9cd7791 100644
--- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslator.java
+++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslator.java
@@ -22,7 +22,9 @@ package org.apache.tinkerpop.gremlin.groovy.jsr223;
 import groovy.json.StringEscapeUtils;
 import org.apache.commons.configuration.ConfigurationConverter;
 import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
+import org.apache.tinkerpop.gremlin.process.traversal.Order;
 import org.apache.tinkerpop.gremlin.process.traversal.P;
+import org.apache.tinkerpop.gremlin.process.traversal.Pop;
 import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
 import org.apache.tinkerpop.gremlin.process.traversal.Translator;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
@@ -32,6 +34,8 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
 import org.apache.tinkerpop.gremlin.process.traversal.util.ConnectiveP;
 import org.apache.tinkerpop.gremlin.process.traversal.util.OrP;
+import org.apache.tinkerpop.gremlin.structure.Column;
+import org.apache.tinkerpop.gremlin.structure.Direction;
 import org.apache.tinkerpop.gremlin.structure.Edge;
 import org.apache.tinkerpop.gremlin.structure.Element;
 import org.apache.tinkerpop.gremlin.structure.Vertex;


[2/5] tinkerpop git commit: TINKERPOP-2040 Hooked up Customizer for TypeTranslator

Posted by sp...@apache.org.
TINKERPOP-2040 Hooked up Customizer for TypeTranslator

Enables the Groovy GremlinScriptEngine to use custom TypeTranslators


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

Branch: refs/heads/tp32
Commit: e5c2e9cdc18751e755a21add02a75261c169cf32
Parents: 67e2ee8
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Tue Sep 25 17:29:49 2018 -0400
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Mon Oct 1 16:18:28 2018 -0400

----------------------------------------------------------------------
 .../gremlin/jsr223/TranslatorCustomizer.java    | 38 ++++++++++++++++++++
 .../groovy/jsr223/GroovyTranslatorTest.java     | 23 +++++++++++-
 .../jsr223/GremlinGroovyScriptEngine.java       | 15 ++++++--
 .../gremlin/groovy/jsr223/GroovyTranslator.java |  8 ++---
 4 files changed, 76 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e5c2e9cd/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/TranslatorCustomizer.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/TranslatorCustomizer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/TranslatorCustomizer.java
new file mode 100644
index 0000000..4468255
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/TranslatorCustomizer.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.jsr223;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
+import org.apache.tinkerpop.gremlin.process.traversal.Translator;
+
+/**
+ * Provides a way to customize and override {@link Bytecode} to script translation. Not all {@link GremlinScriptEngine}
+ * will support this capability as translation is optional.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public interface TranslatorCustomizer extends Customizer {
+
+    /**
+     * Construct a {@link Translator.ScriptTranslator.TypeTranslator} that will be used by a
+     * {@link Translator.ScriptTranslator} instance within the {@link GremlinScriptEngine} to translate
+     * {@link Bytecode} to a script.
+     */
+    public Translator.ScriptTranslator.TypeTranslator createTypeTranslator();
+}

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e5c2e9cd/gremlin-groovy-test/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslatorTest.java
----------------------------------------------------------------------
diff --git a/gremlin-groovy-test/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslatorTest.java b/gremlin-groovy-test/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslatorTest.java
index c224642..1622d3d 100644
--- a/gremlin-groovy-test/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslatorTest.java
+++ b/gremlin-groovy-test/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslatorTest.java
@@ -22,6 +22,7 @@ package org.apache.tinkerpop.gremlin.groovy.jsr223;
 import org.apache.commons.configuration.MapConfiguration;
 import org.apache.tinkerpop.gremlin.AbstractGremlinTest;
 import org.apache.tinkerpop.gremlin.LoadGraphWith;
+import org.apache.tinkerpop.gremlin.jsr223.TranslatorCustomizer;
 import org.apache.tinkerpop.gremlin.process.traversal.Order;
 import org.apache.tinkerpop.gremlin.process.traversal.Pop;
 import org.apache.tinkerpop.gremlin.process.traversal.Scope;
@@ -59,9 +60,11 @@ import java.util.List;
 import java.util.UUID;
 import java.util.function.Function;
 
+import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
 import static org.junit.Assert.fail;
 
 /**
@@ -224,7 +227,7 @@ public class GroovyTranslatorTest extends AbstractGremlinTest {
     }
 
     @Test
-    public void shouldIncludeCustomTypeTranslationForSomethingSilly() {
+    public void shouldIncludeCustomTypeTranslationForSomethingSilly() throws Exception {
         final SillyClass notSillyEnough = SillyClass.from("not silly enough", 100);
         final GraphTraversalSource g = graph.traversal();
 
@@ -241,6 +244,15 @@ public class GroovyTranslatorTest extends AbstractGremlinTest {
                 translate(g.inject(notSillyEnough).asAdmin().getBytecode());
         assertEquals(String.format("g.inject(org.apache.tinkerpop.gremlin.groovy.jsr223.GroovyTranslatorTest.SillyClass.from('%s', (int) %s))", notSillyEnough.getX(), notSillyEnough.getY()), scriptGood);
         assertThatScriptOk(scriptGood, "g", g);
+
+        final GremlinGroovyScriptEngine customEngine = new GremlinGroovyScriptEngine(new SillyClassTranslatorCustomizer());
+        final Bindings b = new SimpleBindings();
+        b.put("g", g);
+        final Traversal t = customEngine.eval(g.inject(notSillyEnough).asAdmin().getBytecode(), b, "g");
+        final SillyClass sc = (SillyClass) t.next();
+        assertEquals(notSillyEnough.getX(), sc.getX());
+        assertEquals(notSillyEnough.getY(), sc.getY());
+        assertThat(t.hasNext(), is(false));
     }
 
     @Test
@@ -344,4 +356,13 @@ public class GroovyTranslatorTest extends AbstractGremlinTest {
         }
     }
 
+    public static class SillyClassTranslatorCustomizer implements TranslatorCustomizer {
+
+        @Override
+        public Translator.ScriptTranslator.TypeTranslator createTypeTranslator() {
+            return x -> x instanceof SillyClass ?
+                    new Translator.ScriptTranslator.Handled(String.format("org.apache.tinkerpop.gremlin.groovy.jsr223.GroovyTranslatorTest.SillyClass.from('%s', (int) %s)",
+                            ((SillyClass) x).getX(), ((SillyClass) x).getY())) : x;
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e5c2e9cd/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngine.java
----------------------------------------------------------------------
diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngine.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngine.java
index 381dd88..b96b8b9 100644
--- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngine.java
+++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngine.java
@@ -30,7 +30,6 @@ import groovy.lang.MissingMethodException;
 import groovy.lang.MissingPropertyException;
 import groovy.lang.Script;
 import groovy.lang.Tuple;
-import org.apache.commons.lang.exception.ExceptionUtils;
 import org.apache.tinkerpop.gremlin.groovy.CompilerCustomizerProvider;
 import org.apache.tinkerpop.gremlin.groovy.DefaultImportCustomizerProvider;
 import org.apache.tinkerpop.gremlin.groovy.EmptyImportCustomizerProvider;
@@ -49,7 +48,9 @@ import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptContext;
 import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngine;
 import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngineFactory;
 import org.apache.tinkerpop.gremlin.jsr223.ImportCustomizer;
+import org.apache.tinkerpop.gremlin.jsr223.TranslatorCustomizer;
 import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
+import org.apache.tinkerpop.gremlin.process.traversal.Translator;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
 import org.codehaus.groovy.ast.ClassHelper;
@@ -241,6 +242,7 @@ public class GremlinGroovyScriptEngine extends GroovyScriptEngineImpl
     private final Set<Artifact> artifactsToUse = new HashSet<>();
     private final boolean interpreterModeEnabled;
     private final long expectedCompilationTime;
+    private final Translator.ScriptTranslator.TypeTranslator typeTranslator;
 
     /**
      * Creates a new instance using the {@link DefaultImportCustomizerProvider}.
@@ -287,6 +289,12 @@ public class GremlinGroovyScriptEngine extends GroovyScriptEngineImpl
         interpreterModeEnabled = groovyCustomizers.stream()
                 .anyMatch(p -> p.getClass().equals(InterpreterModeGroovyCustomizer.class));
 
+        final Optional<TranslatorCustomizer> translatorCustomizer = listOfCustomizers.stream().
+                filter(p -> p instanceof TranslatorCustomizer).
+                map(p -> (TranslatorCustomizer) p).findFirst();
+        typeTranslator = translatorCustomizer.isPresent() ? translatorCustomizer.get().createTypeTranslator() :
+                Translator.ScriptTranslator.TypeTranslator.identity();
+
         // not using the old provider model so set that to empty list so that when createClassLoader is called
         // it knows to use groovyCustomizers instead
         customizerProviders = Collections.emptyList();
@@ -330,6 +338,9 @@ public class GremlinGroovyScriptEngine extends GroovyScriptEngineImpl
         groovyCustomizers = Collections.emptyList();
         importGroovyCustomizer = null;
 
+        // TypeTranslator can only be set by a Customizer - use this old deprecated stuff and you're outta luck
+        typeTranslator = Translator.ScriptTranslator.TypeTranslator.identity();
+
         createClassLoader();
     }
 
@@ -464,7 +475,7 @@ public class GremlinGroovyScriptEngine extends GroovyScriptEngineImpl
         inner.putAll(bytecode.getBindings());
         inner.put(HIDDEN_G, b);
 
-        return (Traversal.Admin) this.eval(GroovyTranslator.of(HIDDEN_G).translate(bytecode), inner);
+        return (Traversal.Admin) this.eval(GroovyTranslator.of(HIDDEN_G, typeTranslator).translate(bytecode), inner);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/e5c2e9cd/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslator.java
----------------------------------------------------------------------
diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslator.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslator.java
index 9cd7791..949fcac 100644
--- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslator.java
+++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslator.java
@@ -22,9 +22,7 @@ package org.apache.tinkerpop.gremlin.groovy.jsr223;
 import groovy.json.StringEscapeUtils;
 import org.apache.commons.configuration.ConfigurationConverter;
 import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
-import org.apache.tinkerpop.gremlin.process.traversal.Order;
 import org.apache.tinkerpop.gremlin.process.traversal.P;
-import org.apache.tinkerpop.gremlin.process.traversal.Pop;
 import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
 import org.apache.tinkerpop.gremlin.process.traversal.Translator;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
@@ -34,8 +32,6 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
 import org.apache.tinkerpop.gremlin.process.traversal.util.ConnectiveP;
 import org.apache.tinkerpop.gremlin.process.traversal.util.OrP;
-import org.apache.tinkerpop.gremlin.structure.Column;
-import org.apache.tinkerpop.gremlin.structure.Direction;
 import org.apache.tinkerpop.gremlin.structure.Edge;
 import org.apache.tinkerpop.gremlin.structure.Element;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
@@ -52,10 +48,12 @@ import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 import java.util.UUID;
-import java.util.function.UnaryOperator;
 
 /**
+ * Converts bytecode to a Groovy string of Gremlin.
+ *
  * @author Marko A. Rodriguez (http://markorodriguez.com)
+ * @author Stephen Mallette (http://stephen.genoprime.com)
  */
 public final class GroovyTranslator implements Translator.ScriptTranslator {