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 {