| /* |
| * 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.process.traversal.translator; |
| |
| import org.apache.tinkerpop.gremlin.jsr223.TranslatorCustomizer; |
| import org.apache.tinkerpop.gremlin.process.traversal.P; |
| import org.apache.tinkerpop.gremlin.process.traversal.Script; |
| import org.apache.tinkerpop.gremlin.process.traversal.Translator; |
| 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.strategy.decoration.SubgraphStrategy; |
| import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy; |
| import org.apache.tinkerpop.gremlin.structure.Edge; |
| import org.apache.tinkerpop.gremlin.structure.T; |
| import org.apache.tinkerpop.gremlin.structure.Vertex; |
| import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedEdge; |
| import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertex; |
| import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph; |
| import org.apache.tinkerpop.gremlin.util.function.Lambda; |
| import org.junit.Test; |
| |
| import javax.script.Bindings; |
| import javax.script.SimpleBindings; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.LinkedHashMap; |
| import java.util.function.Function; |
| |
| import static org.apache.tinkerpop.gremlin.process.traversal.AnonymousTraversalSource.traversal; |
| import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.hasLabel; |
| import static org.junit.Assert.assertEquals; |
| |
| /** |
| * test {@link GroovyTranslator} which return parameterized result, covers: |
| * - parameterized script checking |
| * - binding checking |
| * - eval result checking |
| * |
| * <p> |
| * {@link GroovyTranslatorTest } is used to test {@link GroovyTranslator}, both test cases looks the same |
| * <p> |
| * |
| * @author Marko A. Rodriguez (http://markorodriguez.com) |
| * @author Stephen Mallette (http://stephen.genoprime.com) |
| * @author Stark Arya (sandszhou.zj@alibaba-inc.com) |
| */ |
| public class ParameterizedGroovyTranslatorTest { |
| |
| private static final GraphTraversalSource g = traversal().withEmbedded(EmptyGraph.instance()); |
| private static final Translator.ScriptTranslator translator = GroovyTranslator.of("g", true); |
| |
| @Test |
| public void shouldHandleStrategies() throws Exception { |
| assertEquals("g.withStrategies(ReadOnlyStrategy,new SubgraphStrategy(checkAdjacentVertices: _args_0, vertices: __.hasLabel(_args_1))).V().has(_args_2)", |
| translator.translate(g.withStrategies(ReadOnlyStrategy.instance(), |
| SubgraphStrategy.build().checkAdjacentVertices(false).vertices(hasLabel("person")).create()). |
| V().has("name").asAdmin().getBytecode()).getScript()); |
| } |
| |
| @Test |
| public void shouldSupportStringSupplierLambdas() { |
| 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 Script script = translator.translate(t.getBytecode()); |
| final Bindings bindings = new SimpleBindings(); |
| script.getParameters().ifPresent(bindings::putAll); |
| assertEquals(9, bindings.size()); |
| assertEquals("lengthSum", bindings.get("_args_0")); |
| assertEquals(Integer.valueOf(0), bindings.get("_args_1")); |
| assertEquals(Integer.valueOf(1), bindings.get("_args_2")); |
| assertEquals(Lambda.predicate("it.get().label().equals('person')"), bindings.get("_args_3")); |
| assertEquals(Lambda.function("it.get().vertices(Direction.OUT)"), bindings.get("_args_4")); |
| assertEquals(Lambda.<Traverser<Object>, Integer>function("it.get().value('name').length()"), bindings.get("_args_5")); |
| assertEquals(Lambda.consumer("{ x -> x.sideEffects(\"lengthSum\", x.<Integer>sideEffects('lengthSum') + x.get()) }"), bindings.get("_args_6")); |
| assertEquals(Lambda.comparator("a,b -> a <=> b"), bindings.get("_args_7")); |
| assertEquals(Lambda.biFunction("{ a,b -> a + b }"), bindings.get("_args_8")); |
| assertEquals("g.withSideEffect(_args_0,_args_1).withSack(_args_2)" + |
| ".V()" + |
| ".filter(_args_3)" + |
| ".flatMap(_args_4)" + |
| ".map(_args_5)" + |
| ".sideEffect(_args_6)" + |
| ".order().by(_args_7)" + |
| ".sack(_args_8)", |
| script.getScript()); |
| } |
| |
| @Test |
| public void shouldHandleArray() { |
| final Script script = translator.translate(g.V().has(T.id, P.within(new ArrayList() {{ |
| add(1); |
| add(2); |
| add(3); |
| add(4); |
| add(5); |
| }})).asAdmin().getBytecode()); |
| final Bindings bindings = new SimpleBindings(); |
| script.getParameters().ifPresent(bindings::putAll); |
| assertEquals(5, bindings.size()); |
| assertEquals(Integer.valueOf(1), bindings.get("_args_0")); |
| assertEquals(Integer.valueOf(2), bindings.get("_args_1")); |
| assertEquals(Integer.valueOf(3), bindings.get("_args_2")); |
| assertEquals(Integer.valueOf(4), bindings.get("_args_3")); |
| assertEquals(Integer.valueOf(5), bindings.get("_args_4")); |
| assertEquals("g.V().has(T.id,P.within([_args_0, _args_1, _args_2, _args_3, _args_4]))", script.getScript()); |
| } |
| |
| @Test |
| public void shouldHandleSet() { |
| final Script script = translator.translate(g.V().id().is(new HashSet<Object>() {{ |
| add(3); |
| add(Arrays.asList(1, 2, 3.1d)); |
| add(3); |
| add("3"); |
| }}).asAdmin().getBytecode()); |
| final Bindings bindings = new SimpleBindings(); |
| script.getParameters().ifPresent(bindings::putAll); |
| assertEquals(5, bindings.size()); |
| assertEquals(Integer.valueOf(3), bindings.get("_args_0")); |
| assertEquals("3", bindings.get("_args_1")); |
| assertEquals(Integer.valueOf(1), bindings.get("_args_2")); |
| assertEquals(Integer.valueOf(2), bindings.get("_args_3")); |
| assertEquals(Double.valueOf(3.1), bindings.get("_args_4")); |
| assertEquals("g.V().id().is([_args_0, _args_1, [_args_2, _args_3, _args_4]] as Set)", script.getScript()); |
| } |
| |
| @Test |
| public void shouldHandleMaps() { |
| final Script script = translator.translate(g.V().id().is(new LinkedHashMap<Object,Object>() {{ |
| put(3, "32"); |
| put(Arrays.asList(1, 2, 3.1d), 4); |
| }}).asAdmin().getBytecode()); |
| final Bindings bindings = new SimpleBindings(); |
| script.getParameters().ifPresent(bindings::putAll); |
| assertEquals(6, bindings.size()); |
| assertEquals(Integer.valueOf(3), bindings.get("_args_0")); |
| assertEquals("32", bindings.get("_args_1")); |
| assertEquals(Integer.valueOf(1), bindings.get("_args_2")); |
| assertEquals(Integer.valueOf(2), bindings.get("_args_3")); |
| assertEquals(Double.valueOf(3.1), bindings.get("_args_4")); |
| assertEquals(Integer.valueOf(4), bindings.get("_args_5")); |
| assertEquals("g.V().id().is([(_args_0):(_args_1),([_args_2, _args_3, _args_4]):(_args_5)])", script.getScript()); |
| } |
| |
| @Test |
| public void shouldHandleEmptyMaps() { |
| final Function identity = new Lambda.OneArgLambda("it.get()", "gremlin-groovy"); |
| final Script script = translator.translate(g.inject(Collections.emptyMap()).map(identity).asAdmin().getBytecode()); |
| final Bindings bindings = new SimpleBindings(); |
| script.getParameters().ifPresent(bindings::putAll); |
| assertEquals(1, bindings.size()); |
| assertEquals(identity, bindings.get("_args_0")); |
| assertEquals("g.inject([]).map(_args_0)", script.getScript()); |
| } |
| |
| @Test |
| public void shouldIncludeCustomTypeTranslationForSomethingSilly() throws Exception { |
| final ParameterizedSillyClass notSillyEnough = ParameterizedSillyClass.from("not silly enough", 100); |
| |
| // without type translation we get uglinesss |
| final Script parameterizedScriptBad = translator.translate(g.inject(notSillyEnough).asAdmin().getBytecode()); |
| final Bindings bindings = new SimpleBindings(); |
| parameterizedScriptBad.getParameters().ifPresent(bindings::putAll); |
| assertEquals(String.format("g.inject(%s)", "_args_0"), parameterizedScriptBad.getScript()); |
| assertEquals(1, bindings.size()); |
| assertEquals(notSillyEnough, bindings.get("_args_0")); |
| bindings.clear(); |
| |
| // with type translation we get valid gremlin |
| final Script parameterizedScriptGood = GroovyTranslator.of("g", new ParameterizedSillyClassTranslatorCustomizer().createTypeTranslator()). |
| translate(g.inject(notSillyEnough).asAdmin().getBytecode()); |
| parameterizedScriptGood.getParameters().ifPresent(bindings::putAll); |
| assertEquals(2, bindings.size()); |
| assertEquals(notSillyEnough.getX(), bindings.get("_args_0")); |
| assertEquals(notSillyEnough.getY(), bindings.get("_args_1")); |
| assertEquals("g.inject(org.apache.tinkerpop.gremlin.process.traversal.translator.ParameterizedGroovyTranslatorTest.ParameterizedSillyClass.from(_args_0,_args_1))", |
| parameterizedScriptGood.getScript()); |
| } |
| |
| @Test |
| public void shouldHaveValidToString() { |
| assertEquals("translator[h:gremlin-groovy]", GroovyTranslator.of("h", true).toString()); |
| } |
| |
| @Test |
| public void shouldEscapeStrings() { |
| final Script script = translator.translate(g.addV("customer") |
| .property("customer_id", 501L) |
| .property("name", "Foo\u0020Bar") |
| .property("age", 25) |
| .property("special", "`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?") |
| .asAdmin().getBytecode()); |
| final Bindings bindings = new SimpleBindings(); |
| script.getParameters().ifPresent(bindings::putAll); |
| assertEquals(9, bindings.size()); |
| assertEquals("customer", bindings.get("_args_0")); |
| assertEquals("customer_id", bindings.get("_args_1")); |
| assertEquals(Long.valueOf(501), bindings.get("_args_2")); |
| assertEquals("name", bindings.get("_args_3")); |
| assertEquals("Foo\u0020Bar", bindings.get("_args_4")); |
| assertEquals("age", bindings.get("_args_5")); |
| assertEquals(Integer.valueOf(25), bindings.get("_args_6")); |
| assertEquals("special", bindings.get("_args_7")); |
| assertEquals("`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?", bindings.get("_args_8")); |
| assertEquals("g.addV(_args_0).property(_args_1,_args_2).property(_args_3,_args_4).property(_args_5,_args_6).property(_args_7,_args_8)", script.getScript()); |
| } |
| |
| @Test |
| public void shouldHandleVertexAndEdge() { |
| 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 Script script1 = translator.translate(g.inject(vertex1).asAdmin().getBytecode()); |
| final Bindings bindings = new SimpleBindings(); |
| script1.getParameters().ifPresent(bindings::putAll); |
| assertEquals(2, bindings.size()); |
| assertEquals(id1, bindings.get("_args_0")); |
| assertEquals("customer", bindings.get("_args_1")); |
| assertEquals("g.inject(new ReferenceVertex(_args_0,_args_1))", script1.getScript()); |
| bindings.clear(); |
| |
| 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 Script script2 = translator.translate(g.inject(vertex2).asAdmin().getBytecode()); |
| script2.getParameters().ifPresent(bindings::putAll); |
| assertEquals(2, bindings.size()); |
| assertEquals(id2, bindings.get("_args_0")); |
| assertEquals("user", bindings.get("_args_1")); |
| assertEquals("g.inject(new ReferenceVertex(_args_0,_args_1))", script2.getScript()); |
| bindings.clear(); |
| |
| 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 Script script3 = translator.translate(g.inject(edge).asAdmin().getBytecode()); |
| script3.getParameters().ifPresent(bindings::putAll); |
| assertEquals(6, bindings.size()); |
| assertEquals(id3, bindings.get("_args_0")); |
| assertEquals("knows", bindings.get("_args_1")); |
| assertEquals(id2, bindings.get("_args_2")); |
| assertEquals("user", bindings.get("_args_3")); |
| assertEquals(id1, bindings.get("_args_4")); |
| assertEquals("customer", bindings.get("_args_5")); |
| assertEquals("g.inject(new ReferenceEdge(_args_0,_args_1,new ReferenceVertex(_args_2,_args_3),new ReferenceVertex(_args_4,_args_5)))", script3.getScript()); |
| bindings.clear(); |
| |
| final Script script4 = translator.translate( |
| g.addE("knows").from(vertex1).to(vertex2).property("when", "2018/09/21") |
| .asAdmin().getBytecode()); |
| script4.getParameters().ifPresent(bindings::putAll); |
| assertEquals(7, bindings.size()); |
| assertEquals("knows", bindings.get("_args_0")); |
| assertEquals(id1, bindings.get("_args_1")); |
| assertEquals("customer", bindings.get("_args_2")); |
| assertEquals(id2, bindings.get("_args_3")); |
| assertEquals("user", bindings.get("_args_4")); |
| assertEquals("when", bindings.get("_args_5")); |
| assertEquals("2018/09/21", bindings.get("_args_6")); |
| assertEquals("g.addE(_args_0).from(new ReferenceVertex(_args_1,_args_2)).to(new ReferenceVertex(_args_3,_args_4)).property(_args_5,_args_6)", script4.getScript()); |
| bindings.clear(); |
| |
| final Script script5 = translator.translate(g.V().has("age").asAdmin().getBytecode()); |
| script5.getParameters().ifPresent(bindings::putAll); |
| assertEquals(1, bindings.size()); |
| assertEquals("age", bindings.get("_args_0")); |
| assertEquals("g.V().has(_args_0)", script5.getScript()); |
| } |
| |
| public static class ParameterizedSillyClass { |
| |
| private final String x; |
| private final int y; |
| |
| private ParameterizedSillyClass(final String x, final int y) { |
| this.x = x; |
| this.y = y; |
| } |
| |
| public static ParameterizedSillyClass from(final String x, final int y) { |
| return new ParameterizedSillyClass(x, y); |
| } |
| |
| public String getX() { |
| return x; |
| } |
| |
| public int getY() { |
| return y; |
| } |
| |
| public Object[] getArguments() { |
| return new Object[] {x,y}; |
| } |
| |
| @Override |
| public String toString() { |
| return String.format("org.apache.tinkerpop.gremlin.groovy.jsr223.ParameterizedGroovyTranslatorTest.ParameterizedSillyClass.from('%s', (int) %s)", getX(), getY()); |
| } |
| } |
| |
| public static class ParameterizedSillyClassTranslator extends GroovyTranslator.DefaultTypeTranslator { |
| public ParameterizedSillyClassTranslator(final boolean withParameters) { |
| super(withParameters); |
| } |
| |
| @Override |
| protected Script convertToScript(final Object object) { |
| if (object instanceof ParameterizedSillyClass) { |
| ParameterizedSillyClass obj = (ParameterizedSillyClass) object; |
| script.append(obj.getClass().getCanonicalName()); |
| if (0 == obj.getArguments().length) { |
| script.append(".").append("from").append("()"); |
| } else { |
| script.append(".").append("from").append("("); |
| for (final Object argument: obj.getArguments()) { |
| convertToScript(argument); |
| script.append(","); |
| } |
| script.setCharAtEnd(')'); |
| } |
| return script; |
| } else { |
| return super.convertToScript(object); |
| } |
| } |
| } |
| |
| public static class ParameterizedSillyClassTranslatorCustomizer implements TranslatorCustomizer { |
| |
| @Override |
| public Translator.ScriptTranslator.TypeTranslator createTypeTranslator() { |
| return new ParameterizedSillyClassTranslator(true); |
| } |
| } |
| } |
| |