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:33 UTC

[7/7] tinkerpop git commit: Merge branch 'tp32' into tp33

Merge branch 'tp32' into tp33

Conflicts:
	gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngine.java
	gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslatorTest.java


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

Branch: refs/heads/tp33
Commit: 2b4c993ddd16a45545555858d480103952374897
Parents: 1faf762 88d6f77
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Wed Oct 3 16:09:06 2018 -0400
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Wed Oct 3 16:09:06 2018 -0400

----------------------------------------------------------------------
 CHANGELOG.asciidoc                              |   2 +
 .../gremlin/jsr223/TranslatorCustomizer.java    |  38 ++++
 .../gremlin/process/traversal/Translator.java   |  34 +++
 .../jsr223/GremlinGroovyScriptEngine.java       |  11 +-
 .../gremlin/groovy/jsr223/GroovyTranslator.java |  34 ++-
 .../groovy/jsr223/GroovyTranslatorTest.java     | 215 +++++++++++++++++--
 6 files changed, 309 insertions(+), 25 deletions(-)
----------------------------------------------------------------------


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

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/2b4c993d/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngine.java
----------------------------------------------------------------------
diff --cc gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngine.java
index 03ea883,b96b8b9..7785da6
--- 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
@@@ -222,19 -236,27 +224,20 @@@ public class GremlinGroovyScriptEngine 
      private final ImportGroovyCustomizer importGroovyCustomizer;
      private final List<GroovyCustomizer> groovyCustomizers;
  
 -    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}.
 +     * There is no need to require type checking infrastructure if type checking is not enabled.
       */
 -    public GremlinGroovyScriptEngine() {
 -        this(new Customizer[0]);
 -    }
 +    private final boolean typeCheckingEnabled;
  
      /**
 -     * @deprecated As of release 3.0.1, replaced by {@link #GremlinGroovyScriptEngine(CompilerCustomizerProvider...)}
 +     * Creates a new instance using no {@link Customizer}.
       */
 -    @Deprecated
 -    public GremlinGroovyScriptEngine(final ImportCustomizerProvider importCustomizerProvider) {
 -        this((CompilerCustomizerProvider) importCustomizerProvider);
 +    public GremlinGroovyScriptEngine() {
 +        this(new Customizer[0]);
      }
  
      public GremlinGroovyScriptEngine(final Customizer... customizers) {
@@@ -273,6 -289,58 +276,12 @@@
          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();
 -
 -        createClassLoader();
 -    }
 -
 -    /**
 -     * Creates a new instance with the specified {@link CompilerCustomizerProvider} objects.
 -     *
 -     * @deprecated As of release 3.2.4, replaced by {@link #GremlinGroovyScriptEngine(Customizer...)}.
 -     */
 -    @Deprecated
 -    public GremlinGroovyScriptEngine(final CompilerCustomizerProvider... compilerCustomizerProviders) {
 -        final List<CompilerCustomizerProvider> providers = Arrays.asList(compilerCustomizerProviders);
 -
 -        GremlinLoader.load();
 -
 -        importCustomizerProvider = providers.stream()
 -                .filter(p -> p instanceof ImportCustomizerProvider)
 -                .map(p -> (ImportCustomizerProvider) p)
 -                .findFirst().orElse(NoImportCustomizerProvider.INSTANCE);
 -
 -        // determine if interpreter mode should be enabled
 -        interpreterModeEnabled = providers.stream()
 -                .anyMatch(p -> p.getClass().equals(InterpreterModeCustomizerProvider.class));
 -
 -        final Optional<CompilationOptionsCustomizerProvider> compilationOptionsCustomizerProvider = providers.stream()
 -                .filter(p -> p instanceof CompilationOptionsCustomizerProvider)
 -                .map(p -> (CompilationOptionsCustomizerProvider) p).findFirst();
 -        expectedCompilationTime = compilationOptionsCustomizerProvider.isPresent() ?
 -            compilationOptionsCustomizerProvider.get().getExpectedCompilationTime() : 5000;
 -
 -        // remove used providers as the rest will be applied directly
 -        customizerProviders = providers.stream()
 -                .filter(p -> p != null && !(p instanceof ImportCustomizerProvider) && !(p instanceof CompilationOptionsCustomizerProvider))
 -                .collect(Collectors.toList());
 -
 -        // groovy customizers are not used here - set to empty list so that the customizerProviders get used
 -        // in createClassLoader
 -        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();
      }
  

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/2b4c993d/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslator.java
----------------------------------------------------------------------
diff --cc gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslator.java
index 8024590,949fcac..717e1bc
--- 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
@@@ -49,10 -57,14 +56,12 @@@ import java.util.UUID
   */
  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) {

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/2b4c993d/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslatorTest.java
----------------------------------------------------------------------
diff --cc gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslatorTest.java
index 057f2f1,0000000..2cbc962
mode 100644,000000..100644
--- a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslatorTest.java
+++ b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslatorTest.java
@@@ -1,226 -1,0 +1,401 @@@
 +/*
 + *  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.groovy.jsr223;
 +
 +import org.apache.commons.configuration.MapConfiguration;
++import org.apache.tinkerpop.gremlin.AbstractGremlinTest;
++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;
++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;
 +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
 +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.Graph;
 +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.tinkergraph.structure.TinkerFactory;
 +import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph;
 +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.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;
 +
 +/**
 + * @author Marko A. Rodriguez (http://markorodriguez.com)
 + */
 +public class GroovyTranslatorTest {
 +
++    private Graph graph = TinkerGraph.open();
++    private GraphTraversalSource g = graph.traversal();
++    private static final GremlinGroovyScriptEngine engine = new GremlinGroovyScriptEngine();
++
 +    @Test
 +    public void shouldHandleStrategies() throws Exception {
 +        final TinkerGraph graph = TinkerFactory.createModern();
-         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());
 +        assertFalse(traversal.hasNext());
 +    }
 +
 +    @Test
-     public void shouldSupportStringSupplierLambdas() throws Exception {
++    public void shouldSupportStringSupplierLambdas() {
 +        final TinkerGraph graph = TinkerFactory.createModern();
 +        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)"))
 +                .map(Lambda.<Traverser<Object>, Integer>function("it.get().value('name').length()"))
 +                .sideEffect(Lambda.consumer("{ x -> x.sideEffects(\"lengthSum\", x.<Integer>sideEffects('lengthSum') + x.get()) }"))
 +                .order().by(Lambda.comparator("a,b -> a <=> b"))
 +                .sack(Lambda.biFunction("{ a,b -> a + b }"))
 +                .asAdmin();
 +        final List<Integer> sacks = new ArrayList<>();
 +        final List<Integer> lengths = new ArrayList<>();
 +        while (t.hasNext()) {
 +            final Traverser.Admin<Integer> traverser = t.nextTraverser();
 +            sacks.add(traverser.sack());
 +            lengths.add(traverser.get());
 +        }
 +        assertFalse(t.hasNext());
 +        //
 +        assertEquals(6, lengths.size());
 +        assertEquals(3, lengths.get(0).intValue());
 +        assertEquals(3, lengths.get(1).intValue());
 +        assertEquals(3, lengths.get(2).intValue());
 +        assertEquals(4, lengths.get(3).intValue());
 +        assertEquals(5, lengths.get(4).intValue());
 +        assertEquals(6, lengths.get(5).intValue());
 +        ///
 +        assertEquals(6, sacks.size());
 +        assertEquals(4, sacks.get(0).intValue());
 +        assertEquals(4, sacks.get(1).intValue());
 +        assertEquals(4, sacks.get(2).intValue());
 +        assertEquals(5, sacks.get(3).intValue());
 +        assertEquals(6, sacks.get(4).intValue());
 +        assertEquals(7, sacks.get(5).intValue());
 +        //
 +        assertEquals(24, t.getSideEffects().<Number>get("lengthSum").intValue());
 +
 +        final String script = GroovyTranslator.of("g").translate(t.getBytecode());
 +        assertEquals("g.withStrategies(org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.TranslationStrategy.instance())" +
 +                        ".withSideEffect(\"lengthSum\",(int) 0).withSack((int) 1)" +
 +                        ".V()" +
 +                        ".filter({it.get().label().equals('person')})" +
 +                        ".flatMap({it.get().vertices(Direction.OUT)})" +
 +                        ".map({it.get().value('name').length()})" +
 +                        ".sideEffect({ x -> x.sideEffects(\"lengthSum\", x.<Integer>sideEffects('lengthSum') + x.get()) })" +
 +                        ".order().by({a,b -> a <=> b})" +
 +                        ".sack({ a,b -> a + b })",
 +                script);
 +    }
 +
 +    @Test
 +    public void shouldHandleMaps() {
 +        final TinkerGraph graph = TinkerFactory.createModern();
-         GraphTraversalSource g = graph.traversal();
-         String script = GroovyTranslator.of("g").translate(g.V().id().is(new LinkedHashMap<Object,Object>() {{
++        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 TinkerGraph graph = TinkerFactory.createModern();
-         final Function identity = new Lambda.OneArgLambda("it.get()", "gremlin-groovy");
 +        final GraphTraversalSource g = graph.traversal();
++        final Function identity = new Lambda.OneArgLambda("it.get()", "gremlin-groovy");
 +        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 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 TinkerGraph graph = TinkerGraph.open();
++        final GraphTraversalSource g = graph.traversal();
++        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() throws Exception {
++        final TinkerGraph graph = TinkerGraph.open();
++        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);
++
++        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
 +    public void shouldHaveValidToString() {
 +        assertEquals("translator[h:gremlin-groovy]", GroovyTranslator.of("h").toString());
 +    }
 +
 +    @Test
 +    public void shouldEscapeStrings() {
 +        final TinkerGraph graph = TinkerFactory.createModern();
 +        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);
 +    }
 +
 +    @Test
 +    public void shouldHandleVertexAndEdge() {
 +        final TinkerGraph graph = TinkerFactory.createModern();
 +        final GraphTraversalSource g = graph.traversal();
 +
 +        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);
 +
 +        final String script4 = GroovyTranslator.of("g").translate(
 +                g.addE("knows").from(vertex1).to(vertex2).property("when", "2018/09/21")
-                 .asAdmin().getBytecode());
++                        .asAdmin().getBytecode());
 +        assertEquals("g.addE(\"knows\")" +
-                 ".from(new org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertex(\"customer:10:foo bar \\$100#90\",\"customer\", Collections.emptyMap()))" +
-                 ".to(new org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertex(\"user:20:foo\\\\u0020bar\\\\u005c\\\\u0022mr\\\\u005c\\\\u0022\\\\u00241000#50\",\"user\", Collections.emptyMap()))" +
-                 ".property(\"when\",\"2018/09/21\")",
++                        ".from(new org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertex(\"customer:10:foo bar \\$100#90\",\"customer\", Collections.emptyMap()))" +
++                        ".to(new org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertex(\"user:20:foo\\\\u0020bar\\\\u005c\\\\u0022mr\\\\u005c\\\\u0022\\\\u00241000#50\",\"user\", Collections.emptyMap()))" +
++                        ".property(\"when\",\"2018/09/21\")",
 +                script4);
 +    }
 +
++    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);
++        }
++    }
++
++    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;
++        }
++    }
 +}