This is an automated email from the ASF dual-hosted git repository. spmallette pushed a commit to branch gvalue-3.7 in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 216b8b10c32f79ca3d875120af6308d975faed52 Author: Stephen Mallette <stepm...@amazon.com> AuthorDate: Fri Jul 12 10:48:20 2024 -0400 wip - params --- .../gremlin/jsr223/GremlinLangScriptEngine.java | 17 ++- .../gremlin/jsr223/VariableResolverCustomizer.java | 40 ++++++ .../gremlin/jsr223/VariableResolverPlugin.java | 95 +++++++++++++ .../language/grammar/TraversalMethodVisitor.java | 11 +- .../gremlin/language/grammar/VariableResolver.java | 37 +++++- .../tinkerpop/gremlin/process/traversal/P.java | 6 +- .../traversal/dsl/graph/GraphTraversal.java | 42 ++++++ .../gremlin/process/traversal/step/GType.java | 81 +++++++++++ .../gremlin/process/traversal/step/GValue.java | 148 +++++++++++++++++++++ .../process/traversal/step/filter/CoinStep.java | 26 +++- .../gremlin/structure/VertexProperty.java | 3 - .../gremlin/jsr223/VariableResolverPluginTest.java | 117 ++++++++++++++++ .../language/grammar/ArgumentVisitorTest.java | 116 ++++++++-------- .../language/grammar/GremlinQueryParserTest.java | 8 +- .../tinkergraph/structure/TinkerGraphPlayTest.java | 33 +++-- 15 files changed, 691 insertions(+), 89 deletions(-) diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinLangScriptEngine.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinLangScriptEngine.java index 842d4daa32..6ef141e8e5 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinLangScriptEngine.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinLangScriptEngine.java @@ -33,7 +33,11 @@ import javax.script.ScriptException; import javax.script.SimpleBindings; import java.io.IOException; import java.io.Reader; +import java.util.Arrays; +import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.function.Function; /** * A {@link GremlinScriptEngine} implementation that evaluates Gremlin scripts using {@code gremlin-language}. As it @@ -53,6 +57,8 @@ import java.util.Map; public class GremlinLangScriptEngine extends AbstractScriptEngine implements GremlinScriptEngine { private volatile GremlinScriptEngineFactory factory; + private final Function<Map<String, Object>, VariableResolver> variableResolverMaker; + /** * Creates a new instance using no {@link Customizer}. */ @@ -61,6 +67,15 @@ public class GremlinLangScriptEngine extends AbstractScriptEngine implements Gre } public GremlinLangScriptEngine(final Customizer... customizers) { + final List<Customizer> listOfCustomizers = Arrays.asList(customizers); + + // this ScriptEngine really only supports the VariableResolverCustomizer to configure the VariableResolver + // and can't configure it more than once. first one wins + final Optional<Customizer> opt = listOfCustomizers.stream().filter(c -> c instanceof VariableResolverCustomizer).findFirst(); + variableResolverMaker = opt.isPresent() ? + ((VariableResolverCustomizer) opt.get()).getVariableResolverMaker() : + VariableResolver.DirectVariableResolver::new; + } @Override @@ -108,7 +123,7 @@ public class GremlinLangScriptEngine extends AbstractScriptEngine implements Gre final Map<String, Object> m = context.getBindings(ScriptContext.ENGINE_SCOPE); final GremlinAntlrToJava antlr = new GremlinAntlrToJava((GraphTraversalSource) o, - new VariableResolver.DefaultVariableResolver(m)); + variableResolverMaker.apply(m)); try { return GremlinQueryParser.parse(script, antlr); diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/VariableResolverCustomizer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/VariableResolverCustomizer.java new file mode 100644 index 0000000000..1be1f3d448 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/VariableResolverCustomizer.java @@ -0,0 +1,40 @@ +/* + * 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.language.grammar.VariableResolver; + +import java.util.Map; +import java.util.function.Function; + +/** + * Supplies a {@link VariableResolver} implementation to the {@link GremlinLangScriptEngine}. This {@link Customizer} + * is not relevant to any other {@link GremlinScriptEngine} implementation. + */ +public class VariableResolverCustomizer implements Customizer { + private final Function<Map<String,Object>, VariableResolver> variableResolverMaker; + + public VariableResolverCustomizer(final Function<Map<String, Object>, VariableResolver> variableResolverMaker) { + this.variableResolverMaker = variableResolverMaker; + } + + public Function<Map<String, Object>, VariableResolver> getVariableResolverMaker() { + return variableResolverMaker; + } +} diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/VariableResolverPlugin.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/VariableResolverPlugin.java new file mode 100644 index 0000000000..587f9666a1 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/VariableResolverPlugin.java @@ -0,0 +1,95 @@ +/* + * 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.language.grammar.VariableResolver; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +/** + * A plugin that allows for the configuration of a {@link VariableResolver} implementation to be used by the + * {@link GremlinLangScriptEngine}. By default, it will use the {@link VariableResolver.DirectVariableResolver} which + * directly resolves variable name to a value from the binding in the script engine context. This is the most common + * usage relevant for most users and providers. Other options are reserved for more advanced use cases. + */ +public class VariableResolverPlugin extends AbstractGremlinPlugin { + private static final String NAME = "tinkerpop.variableResolver"; + + public static final Map<String, Function<Map<String, Object>, VariableResolver>> VARIABLE_RESOLVERS = + new HashMap<String, Function<Map<String, Object>, VariableResolver>>() {{ + put(VariableResolver.DirectVariableResolver.class.getSimpleName(), VariableResolver.DirectVariableResolver::new); + put(VariableResolver.DefaultVariableResolver.class.getSimpleName(), VariableResolver.DefaultVariableResolver::new); + put(VariableResolver.NoVariableResolver.class.getSimpleName(), m -> VariableResolver.NoVariableResolver.instance()); + put(VariableResolver.NullVariableResolver.class.getSimpleName(), m -> VariableResolver.NullVariableResolver.instance()); + }}; + + private VariableResolverPlugin(final VariableResolverPlugin.Builder builder) { + super(NAME, new VariableResolverCustomizer(builder.variableResolverMaker)); + } + + /** + * Builds a set of static bindings. + */ + public static VariableResolverPlugin.Builder build() { + return new VariableResolverPlugin.Builder(); + } + + public static final class Builder { + + Function<Map<String,Object>, VariableResolver> variableResolverMaker = VariableResolver.DirectVariableResolver::new; + + private Builder() {} + + /** + * Sets the type of {@link VariableResolver} to use by specifying a simple class name associated with the + * inner classes in that interface or a fully qualified class name. The assumption is that implementations + * will allow a constructor that takes a {@code Map} which contains the bindings from the script engine context. + * Implementations are can then decide how to resolve variables in the script based on that {@code Map} or some + * other mechanism. + */ + public VariableResolverPlugin.Builder resolver(final String resolverName) { + if (VARIABLE_RESOLVERS.containsKey(resolverName)) { + this.variableResolverMaker = VARIABLE_RESOLVERS.get(resolverName); + } else { + try { + // Assuming resolverName is a fully qualified class name if it's not in the simple name map + final Class<?> clazz = Class.forName(resolverName); + if (VariableResolver.class.isAssignableFrom(clazz)) { + this.variableResolverMaker = map -> { + try { + return (VariableResolver) clazz.getConstructor(Map.class).newInstance(map); + } catch (Exception e) { + throw new RuntimeException("Error instantiating VariableResolver", e); + } + }; + } + } catch (ClassNotFoundException e) { + throw new RuntimeException("VariableResolver class not found: " + resolverName, e); + } + } + return this; + } + + public VariableResolverPlugin create() { + return new VariableResolverPlugin(this); + } + } +} diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java index d5124e20b3..536dd13e55 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java @@ -23,6 +23,8 @@ import org.apache.tinkerpop.gremlin.process.traversal.Operator; import org.apache.tinkerpop.gremlin.process.traversal.Order; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; +import org.apache.tinkerpop.gremlin.process.traversal.step.GType; +import org.apache.tinkerpop.gremlin.process.traversal.step.GValue; import org.apache.tinkerpop.gremlin.structure.VertexProperty.Cardinality; import java.util.LinkedHashMap; @@ -445,7 +447,14 @@ public class TraversalMethodVisitor extends TraversalRootVisitor<GraphTraversal> */ @Override public GraphTraversal visitTraversalMethod_coin(final GremlinParser.TraversalMethod_coinContext ctx) { - return graphTraversal.coin(((Number) antlr.argumentVisitor.visitFloatArgument(ctx.floatArgument())).doubleValue()); + final Object literalOrVar = antlr.argumentVisitor.visitFloatArgument(ctx.floatArgument()); + if (literalOrVar instanceof Number) + return graphTraversal.coin(((Number) literalOrVar).doubleValue()); + else if (literalOrVar instanceof GValue && ((GValue) literalOrVar).getType().isNumeric()) + return graphTraversal.asAdmin().coin((GValue<Double>) literalOrVar); + else + throw new IllegalArgumentException("coin() argument must be a double"); + } /** diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/VariableResolver.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/VariableResolver.java index a97bd4b646..e017808faf 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/VariableResolver.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/VariableResolver.java @@ -18,24 +18,25 @@ */ package org.apache.tinkerpop.gremlin.language.grammar; +import org.apache.tinkerpop.gremlin.process.traversal.step.GValue; import java.util.Map; import java.util.function.BiFunction; /** * Resolves parameters in Gremlin to objects. */ -public interface VariableResolver extends BiFunction<String, GremlinParser.VariableContext, Object> { +public interface VariableResolver<T> extends BiFunction<String, GremlinParser.VariableContext, T> { /** * This function resolves a variable name and the given parsers context to an object. */ @Override - Object apply(final String varName, final GremlinParser.VariableContext variableContext); + T apply(final String varName, final GremlinParser.VariableContext variableContext); /** * This {@link VariableResolver} implementation throws exceptions for all variable names. */ - class NoVariableResolver implements VariableResolver { + class NoVariableResolver implements VariableResolver<Object> { private static NoVariableResolver instance = new NoVariableResolver(); public static VariableResolver instance() { @@ -48,12 +49,35 @@ public interface VariableResolver extends BiFunction<String, GremlinParser.Varia } } + /** + * Allows for a provided variable set in the form of a {@code Map}, where the keys are variable names and the + * values are the parameter values to be injected into the traversal in their place. The value is provided to a + * {@link GValue} object along with the variable name for further reference. + */ + class DefaultVariableResolver implements VariableResolver<GValue<?>> { + + private final Map<String, Object> variables; + + public DefaultVariableResolver(final Map<String, Object> variables) { + this.variables = variables; + } + + @Override + public GValue<?> apply(final String s, final GremlinParser.VariableContext variableContext) { + if (!variables.containsKey(s)) { + throw new VariableResolverException(String.format("No variable found for %s", s)); + } + + return GValue.of(s, variables.get(s)); + } + } + /** * This {@link VariableResolver} simply provides a {@code null} value for all variable names. It's typical use * is for when you really don't intend to execute the traversal and just want to get an instance of one when * bindings are being used as with {@link NoOpTerminalVisitor}. */ - class NullVariableResolver implements VariableResolver { + class NullVariableResolver implements VariableResolver<Object> { private static NullVariableResolver instance = new NullVariableResolver(); public static VariableResolver instance() { @@ -70,11 +94,11 @@ public interface VariableResolver extends BiFunction<String, GremlinParser.Varia * Allows for a provided variable set in the form of a {@code Map}, where the keys are variable names and the * values are the parameter values to be injected into the traversal in their place. */ - class DefaultVariableResolver implements VariableResolver { + class DirectVariableResolver implements VariableResolver<Object> { private final Map<String, Object> variables; - public DefaultVariableResolver(final Map<String, Object> variables) { + public DirectVariableResolver(final Map<String, Object> variables) { this.variables = variables; } @@ -87,4 +111,5 @@ public interface VariableResolver extends BiFunction<String, GremlinParser.Varia return variables.get(s); } } + } \ No newline at end of file diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java index 917a13891d..56e1946368 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java @@ -18,6 +18,7 @@ */ package org.apache.tinkerpop.gremlin.process.traversal; +import org.apache.tinkerpop.gremlin.process.traversal.step.GValue; import org.apache.tinkerpop.gremlin.process.traversal.util.AndP; import org.apache.tinkerpop.gremlin.process.traversal.util.OrP; @@ -74,7 +75,10 @@ public class P<V> implements Predicate<V>, Serializable, Cloneable { @Override public boolean test(final V testValue) { - return this.biPredicate.test(testValue, this.value); + if (this.value instanceof GValue) + return this.biPredicate.test(testValue, ((GValue<V>) this.value).get()); + else + return this.biPredicate.test(testValue, this.value); } @Override diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java index f01afa8197..103ad64255 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java @@ -47,6 +47,8 @@ import org.apache.tinkerpop.gremlin.process.traversal.lambda.TrueTraversal; import org.apache.tinkerpop.gremlin.process.traversal.step.ByModulating; import org.apache.tinkerpop.gremlin.process.traversal.step.Configuring; import org.apache.tinkerpop.gremlin.process.traversal.step.FromToModulating; +import org.apache.tinkerpop.gremlin.process.traversal.step.GType; +import org.apache.tinkerpop.gremlin.process.traversal.step.GValue; import org.apache.tinkerpop.gremlin.process.traversal.step.Mutating; import org.apache.tinkerpop.gremlin.process.traversal.step.ReadWriting; import org.apache.tinkerpop.gremlin.process.traversal.step.TimesModulating; @@ -229,8 +231,48 @@ import static org.apache.tinkerpop.gremlin.structure.VertexProperty.Cardinality. */ public interface GraphTraversal<S, E> extends Traversal<S, E> { + /** + * Exposes administrative methods that are either internal to TinkerPop or for users with advanced needs. This + * separation helps keep the Gremlin API more concise. Any {@code GraphTraversal} can get an instance of its + * administrative form by way of {@link GraphTraversal#asAdmin()}. + * <p/> + * Note that step overloads allowing use of {@link GValue} objects do not construct bytecode with that object. + * Bytecode does not make use of parameterization in that fashion so it there isn't a need to really preserve that + * functionality there (doing so would require changes to serializers). + */ public interface Admin<S, E> extends Traversal.Admin<S, E>, GraphTraversal<S, E> { + /** + * Filter the <code>E</code> object given a biased coin toss. + * + * @param probability the probability that the object will pass through the filter + * @return the traversal with an appended {@link CoinStep}. + * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#coin-step" target="_blank">Reference Documentation - Coin Step</a> + * @since 3.7.3 + */ + public default GraphTraversal<S, E> coin(final GValue<Double> probability) { + this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.coin, probability.get()); + return this.asAdmin().addStep(new CoinStep<>(this.asAdmin(), probability)); + } + + /** + * Filter the <code>E</code> object given a biased coin toss where the argument can be either a {@link GValue} + * object or a double. + * + * @param probability the probability that the object will pass through and must be a {@link GValue} object or a double. + * @return the traversal with an appended {@link CoinStep}. + * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#coin-step" target="_blank">Reference Documentation - Coin Step</a> + * @since 3.7.3 + */ + public default GraphTraversal<S, E> coin(final Object probability) { + if (probability instanceof Number) + return this.coin(((Number) probability).doubleValue()); + else if (probability instanceof GValue && ((GValue) probability).getType() == GType.DOUBLE) + return this.coin(probability); + else + throw new IllegalArgumentException("The probability must be a GValue<Double> or a double"); + } + @Override public default <E2> GraphTraversal.Admin<S, E2> addStep(final Step<?, E2> step) { return (GraphTraversal.Admin<S, E2>) Traversal.Admin.super.addStep((Step) step); diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/GType.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/GType.java new file mode 100644 index 0000000000..89a8b79098 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/GType.java @@ -0,0 +1,81 @@ +/* + * 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.step; + +import org.apache.tinkerpop.gremlin.process.traversal.Path; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Property; +import org.apache.tinkerpop.gremlin.structure.Vertex; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * An enum that describes types that are used in the Gremlin language. + */ +public enum GType { + BIG_DECIMAL, + BIG_INTEGER, + BOOLEAN, + DOUBLE, + EDGE, + INTEGER, + LIST, + LONG, + MAP, + PATH, + PROPERTY, + SET, + STRING, + UNKNOWN, + VERTEX; + + GType() {} + + /** + * Returns true if the type is a number. + */ + public boolean isNumeric() { + return this == INTEGER || this == DOUBLE || this == LONG || this == BIG_INTEGER || this == BIG_DECIMAL; + } + + /** + * Convert an object to a matching {@link GType} and if not matched return {@link GType#UNKNOWN}. + */ + public static GType getType(final Object object) { + if (object instanceof String) return STRING; + else if (object instanceof Integer) return INTEGER; + else if (object instanceof Boolean) return BOOLEAN; + else if (object instanceof Double) return DOUBLE; + else if (object instanceof Long) return LONG; + else if (object instanceof Map) return MAP; + else if (object instanceof List) return LIST; + else if (object instanceof Set) return SET; + else if (object instanceof Vertex) return VERTEX; + else if (object instanceof Edge) return EDGE; + else if (object instanceof Path) return PATH; + else if (object instanceof Property) return PROPERTY; + else if (object instanceof BigInteger) return BIG_INTEGER; + else if (object instanceof BigDecimal) return BIG_DECIMAL; + else return UNKNOWN; + } +} diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/GValue.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/GValue.java new file mode 100644 index 0000000000..cb6930e432 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/GValue.java @@ -0,0 +1,148 @@ +/* + * 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.step; + +import org.apache.tinkerpop.gremlin.process.traversal.Path; +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Property; +import org.apache.tinkerpop.gremlin.structure.Vertex; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; + +/** + * A {@code GValue} is a variable or literal value that is used in a {@link Traversal}. It is composed of a key-value + * pair where the key is the name given to the variable and the value is the object that the variable resolved to. If + * the name is not given, the value was provided literally in the traversal. The value of the variable can be any + * object. The {@code GValue} also includes the {@link GType} that describes the type it contains. + */ +public class GValue<V> { + private final String name; + private final GType type; + + private final V value; + + private GValue(final GType type, final V value) { + this(null, type, value); + } + + private GValue(final String name, final GType type, final V value) { + this.name = name; + this.type = type; + this.value = value; + } + + public boolean isVariable() { + return this.name != null; + } + + public Optional<String> getName() { + return Optional.ofNullable(this.name); + } + + public GType getType() { + return this.type; + } + + public V get() { + return this.value; + } + + @Override + public String toString() { + return isVariable() ? + String.format("%s&%s", name, value) : Objects.toString(value); + } + + /** + * Create a new {@code Var} with the specified name and value. + * + * @param name the name of the variable + * @param value the value of the variable + */ + public static <V> GValue<V> of(final String name, final V value) { + return new GValue<>(name, GType.getType(value), value); + } + + public static GValue<String> ofString(final String name, final String value) { + return new GValue<>(name, GType.STRING, value); + } + + public static GValue<Integer> ofInteger(final String name, final Integer value) { + return new GValue<>(name, GType.INTEGER, value); + } + + public static GValue<Boolean> ofBoolean(final String name, final Boolean value) { + return new GValue<>(name, GType.BOOLEAN, value); + } + + public static GValue<Double> ofDouble(final String name, final Double value) { + return new GValue<>(name, GType.DOUBLE, value); + } + + public static GValue<Double> ofDouble(final Double value) { + return new GValue<>(GType.DOUBLE, value); + } + + public static GValue<BigInteger> ofBigInteger(final String name, final BigInteger value) { + return new GValue<>(name, GType.BIG_INTEGER, value); + } + + public static GValue<BigDecimal> ofBigDecimal(final String name, final BigDecimal value) { + return new GValue<>(name, GType.BIG_DECIMAL, value); + } + + public static GValue<Long> ofLong(final String name, final Long value) { + return new GValue<>(name, GType.LONG, value); + } + + public static GValue<Map> ofMap(final String name, final Map value) { + return new GValue<>(name, GType.MAP, value); + } + + public static GValue<List> ofList(final String name, final List value) { + return new GValue<>(name, GType.LIST, value); + } + + public static GValue<Set> ofSet(final String name, final Set value) { + return new GValue<>(name, GType.SET, value); + } + + public static GValue<Vertex> ofVertex(final String name, final Vertex value) { + return new GValue<>(name, GType.VERTEX, value); + } + + public static GValue<Edge> ofEdge(final String name, final Edge value) { + return new GValue<>(name, GType.EDGE, value); + } + + public static GValue<Path> ofPath(final String name, final Path value) { + return new GValue<>(name, GType.PATH, value); + } + + public static GValue<Property> ofProperty(final String name, final Property value) { + return new GValue<>(name, GType.PROPERTY, value); + } +} diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/CoinStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/CoinStep.java index e4a44abe93..7395dd8544 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/CoinStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/CoinStep.java @@ -21,6 +21,7 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.filter; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.process.traversal.Traverser; import org.apache.tinkerpop.gremlin.process.traversal.step.Seedable; +import org.apache.tinkerpop.gremlin.process.traversal.step.GValue; import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement; import org.apache.tinkerpop.gremlin.structure.util.StringFactory; @@ -29,14 +30,25 @@ import java.util.Random; import java.util.Set; /** + * A filter step that will randomly allow traversers to pass through the step based on a probability value. + * * @author Marko A. Rodriguez (http://markorodriguez.com) */ public final class CoinStep<S> extends FilterStep<S> implements Seedable { private final Random random = new Random(); - private final double probability; + private final GValue<Double> probability; + /** + * @deprecated As of release 3.7.3, replaced by {@link #CoinStep(Traversal.Admin, GValue)} + */ + @Deprecated public CoinStep(final Traversal.Admin traversal, final double probability) { + super(traversal); + this.probability = GValue.ofDouble(probability); + } + + public CoinStep(final Traversal.Admin traversal, final GValue<Double> probability) { super(traversal); this.probability = probability; } @@ -46,8 +58,12 @@ public final class CoinStep<S> extends FilterStep<S> implements Seedable { random.setSeed(seed); } + public GValue<Double> getProbabilityGValue() { + return probability; + } + public double getProbability() { - return this.probability; + return this.probability.get(); } @Override @@ -55,11 +71,11 @@ public final class CoinStep<S> extends FilterStep<S> implements Seedable { long newBulk = 0l; if (traverser.bulk() < 100) { for (int i = 0; i < traverser.bulk(); i++) { - if (this.probability >= random.nextDouble()) + if (this.probability.get() >= random.nextDouble()) newBulk++; } } else { - newBulk = Math.round(this.probability * traverser.bulk()); + newBulk = Math.round(this.probability.get() * traverser.bulk()); } if (0 == newBulk) return false; traverser.setBulk(newBulk); @@ -73,7 +89,7 @@ public final class CoinStep<S> extends FilterStep<S> implements Seedable { @Override public int hashCode() { - return super.hashCode() ^ Double.hashCode(this.probability); + return super.hashCode() ^ Double.hashCode(this.probability.get()); } @Override diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/VertexProperty.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/VertexProperty.java index af7a18a1d9..962140adb5 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/VertexProperty.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/VertexProperty.java @@ -18,9 +18,6 @@ */ package org.apache.tinkerpop.gremlin.structure; -import org.apache.tinkerpop.gremlin.process.traversal.Traversal; -import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; -import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; import org.apache.tinkerpop.gremlin.process.traversal.lambda.CardinalityValueTraversal; import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyVertexProperty; diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/VariableResolverPluginTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/VariableResolverPluginTest.java new file mode 100644 index 0000000000..0d3dc5fd96 --- /dev/null +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/VariableResolverPluginTest.java @@ -0,0 +1,117 @@ +/* + * 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.language.grammar.GremlinParser; +import org.apache.tinkerpop.gremlin.language.grammar.VariableResolver; +import org.junit.Test; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsInstanceOf.instanceOf; +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.fail; + +import java.util.HashMap; +import java.util.Map; + +public class VariableResolverPluginTest { + + @Test + public void shouldProduceNullVariableResolver() { + final Map<String, Object> bindings = new HashMap<>(); + final VariableResolverPlugin plugin = VariableResolverPlugin.build(). + resolver(VariableResolver.NullVariableResolver.class.getSimpleName()).create(); + final VariableResolver resolver = ((VariableResolverCustomizer) plugin.getCustomizers(). + get()[0]).getVariableResolverMaker().apply(bindings); + assertThat(resolver, instanceOf(VariableResolver.NullVariableResolver.class)); + } + + @Test + public void shouldProduceNoVariableResolver() { + final Map<String, Object> bindings = new HashMap<>(); + final VariableResolverPlugin plugin = VariableResolverPlugin.build(). + resolver(VariableResolver.NoVariableResolver.class.getSimpleName()).create(); + final VariableResolver resolver = ((VariableResolverCustomizer) plugin.getCustomizers(). + get()[0]).getVariableResolverMaker().apply(bindings); + assertThat(resolver, instanceOf(VariableResolver.NoVariableResolver.class)); + } + + @Test + public void shouldProduceDefaultVariableResolver() { + final Map<String, Object> bindings = new HashMap<>(); + final VariableResolverPlugin plugin = VariableResolverPlugin.build(). + resolver(VariableResolver.DefaultVariableResolver.class.getSimpleName()).create(); + final VariableResolver resolver = ((VariableResolverCustomizer) plugin.getCustomizers(). + get()[0]).getVariableResolverMaker().apply(bindings); + assertThat(resolver, instanceOf(VariableResolver.DefaultVariableResolver.class)); + } + + @Test + public void shouldProduceDirectVariableResolver() { + final Map<String, Object> bindings = new HashMap<>(); + final VariableResolverPlugin plugin = VariableResolverPlugin.build(). + resolver(VariableResolver.DirectVariableResolver.class.getSimpleName()).create(); + final VariableResolver resolver = ((VariableResolverCustomizer) plugin.getCustomizers(). + get()[0]).getVariableResolverMaker().apply(bindings); + assertThat(resolver, instanceOf(VariableResolver.DirectVariableResolver.class)); + } + + @Test + public void shouldProduceDefaultedVariableResolverWhenNoSpecified() { + final Map<String, Object> bindings = new HashMap<>(); + final VariableResolverPlugin plugin = VariableResolverPlugin.build().create(); + final VariableResolver resolver = ((VariableResolverCustomizer) plugin.getCustomizers(). + get()[0]).getVariableResolverMaker().apply(bindings); + assertThat(resolver, instanceOf(VariableResolver.DirectVariableResolver.class)); + } + + @Test + public void shouldProduceCustomVariableResolverForFullyQualifiedClassName() { + final Map<String, Object> bindings = new HashMap<>(); + final VariableResolverPlugin plugin = VariableResolverPlugin.build(). + resolver(CustomVariableResolver.class.getName()).create(); + final VariableResolver resolver = ((VariableResolverCustomizer) plugin.getCustomizers(). + get()[0]).getVariableResolverMaker().apply(bindings); + assertThat(resolver, instanceOf(CustomVariableResolver.class)); + } + + @Test + public void shouldProduceExceptionVariableResolverIfResolverNotFound() { + try { + final VariableResolverPlugin plugin = VariableResolverPlugin.build(). + resolver("NotAResolver").create(); + fail("The resolver does not exist and should have failed"); + } catch (Exception re) { + assertThat(re.getMessage(), containsString("VariableResolver class not found: NotAResolver")); + } + } + + /** + * Custom test implementation of {@link VariableResolver} that is not part of the standard set of implementations. + * This class does nothing. + */ + public static class CustomVariableResolver implements VariableResolver<Object> { + public CustomVariableResolver(final Map<String, Object> m) { + } + + @Override + public Object apply(final String varName, final GremlinParser.VariableContext variableContext) { + return null; + } + } +} \ No newline at end of file diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/ArgumentVisitorTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/ArgumentVisitorTest.java index 43f33054b5..e9c98cd2c3 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/ArgumentVisitorTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/ArgumentVisitorTest.java @@ -84,81 +84,81 @@ public class ArgumentVisitorTest { public static Iterable<Object[]> generateTestParameters() { return Arrays.asList(new Object[][]{ {Boolean.class, "x", new VariableResolverException("x"), createAntlr(VariableResolver.NoVariableResolver.instance())}, - {Boolean.class, "true", true, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", true)))}, - {Boolean.class, "false", false, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", true)))}, - {Boolean.class, "x", true, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", true)))}, + {Boolean.class, "true", true, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", true)))}, + {Boolean.class, "false", false, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", true)))}, + {Boolean.class, "x", true, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", true)))}, {Integer.class, "x", new VariableResolverException("x"), createAntlr(VariableResolver.NoVariableResolver.instance())}, - {Integer.class, "0", 0, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", 100)))}, - {Integer.class, "0i", 0, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", 100)))}, - {Integer.class, "0L", 0L, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", 100)))}, - {Integer.class, "x", 0, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", 0)))}, - {Integer.class, "x", 0L, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", 0L)))}, + {Integer.class, "0", 0, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", 100)))}, + {Integer.class, "0i", 0, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", 100)))}, + {Integer.class, "0L", 0L, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", 100)))}, + {Integer.class, "x", 0, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", 0)))}, + {Integer.class, "x", 0L, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", 0L)))}, {Float.class, "x", new VariableResolverException("x"), createAntlr(VariableResolver.NoVariableResolver.instance())}, - {Float.class, "0.0d", 0.0, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", 1000.0)))}, - {Float.class, "0d", 0.0, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", 1000.0)))}, - {Float.class, "0F", 0.0F, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", 1000.0F)))}, - {Float.class, "x", 0.0, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", 0.0)))}, - {Float.class, "x", 0.0F, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", 0.0F)))}, + {Float.class, "0.0d", 0.0, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", 1000.0)))}, + {Float.class, "0d", 0.0, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", 1000.0)))}, + {Float.class, "0F", 0.0F, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", 1000.0F)))}, + {Float.class, "x", 0.0, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", 0.0)))}, + {Float.class, "x", 0.0F, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", 0.0F)))}, {String.class, "x", new VariableResolverException("x"), createAntlr(VariableResolver.NoVariableResolver.instance())}, - {String.class, "'test'", "test", createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", "nope")))}, - {String.class, "x", "test", createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", "test")))}, - {String.class, "x", "graphson", createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", IO.graphson)))}, + {String.class, "'test'", "test", createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", "nope")))}, + {String.class, "x", "test", createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", "test")))}, + {String.class, "x", "graphson", createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", IO.graphson)))}, {StringNullable.class, "x", new VariableResolverException("x"), createAntlr(VariableResolver.NoVariableResolver.instance())}, - {StringNullable.class, "null", null, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", "nope")))}, - {StringNullable.class, "x", null, createAntlr(new VariableResolver.DefaultVariableResolver(nullMap))}, + {StringNullable.class, "null", null, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", "nope")))}, + {StringNullable.class, "x", null, createAntlr(new VariableResolver.DirectVariableResolver(nullMap))}, {Object.class, "x", new VariableResolverException("x"), createAntlr(VariableResolver.NoVariableResolver.instance())}, - {Object.class, "'test'", "test", createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", "nope")))}, - {Object.class, "x", "test", createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", "test")))}, - {Object.class, "x", now, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", now)))}, - {Object.class, "[1,2,3]", Arrays.asList(1, 2, 3), createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", now)))}, - {Object.class, "x", P.eq(100), createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", P.eq(100))))}, + {Object.class, "'test'", "test", createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", "nope")))}, + {Object.class, "x", "test", createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", "test")))}, + {Object.class, "x", now, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", now)))}, + {Object.class, "[1,2,3]", Arrays.asList(1, 2, 3), createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", now)))}, + {Object.class, "x", P.eq(100), createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", P.eq(100))))}, {Direction.class, "x", new VariableResolverException("x"), createAntlr(VariableResolver.NoVariableResolver.instance())}, - {Direction.class, "Direction.OUT", Direction.OUT, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", "nope")))}, - {Direction.class, "OUT", Direction.OUT, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", "nope")))}, - {Direction.class, "x", Direction.OUT, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", Direction.OUT)))}, - {Direction.class, "x", Direction.from, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", Direction.from)))}, + {Direction.class, "Direction.OUT", Direction.OUT, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", "nope")))}, + {Direction.class, "OUT", Direction.OUT, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", "nope")))}, + {Direction.class, "x", Direction.OUT, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", Direction.OUT)))}, + {Direction.class, "x", Direction.from, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", Direction.from)))}, {Vertex.class, "x", new VariableResolverException("x"), createAntlr(VariableResolver.NoVariableResolver.instance())}, - {Vertex.class, "new Vertex(1i,'person')", new ReferenceVertex(1, "person"), createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", Direction.from)))}, - {Vertex.class, "x", new ReferenceVertex(1, "person"), createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", new ReferenceVertex(1, "person"))))}, + {Vertex.class, "new Vertex(1i,'person')", new ReferenceVertex(1, "person"), createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", Direction.from)))}, + {Vertex.class, "x", new ReferenceVertex(1, "person"), createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", new ReferenceVertex(1, "person"))))}, {Order.class, "x", new VariableResolverException("x"), createAntlr(VariableResolver.NoVariableResolver.instance())}, - {Order.class, "Order.desc", Order.desc, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", "nope")))}, - {Order.class, "x", Order.desc, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", Order.desc)))}, + {Order.class, "Order.desc", Order.desc, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", "nope")))}, + {Order.class, "x", Order.desc, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", Order.desc)))}, {Scope.class, "x", new VariableResolverException("x"), createAntlr(VariableResolver.NoVariableResolver.instance())}, - {Scope.class, "Scope.local", Scope.local, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", "nope")))}, - {Scope.class, "local", Scope.local, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", "nope")))}, - {Scope.class, "x", Scope.local, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", Scope.local)))}, + {Scope.class, "Scope.local", Scope.local, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", "nope")))}, + {Scope.class, "local", Scope.local, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", "nope")))}, + {Scope.class, "x", Scope.local, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", Scope.local)))}, {T.class, "x", new VariableResolverException("x"), createAntlr(VariableResolver.NoVariableResolver.instance())}, - {T.class, "T.label", T.label, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", "nope")))}, - {T.class, "label", T.label, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", "nope")))}, - {T.class, "x", T.label, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", T.label)))}, + {T.class, "T.label", T.label, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", "nope")))}, + {T.class, "label", T.label, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", "nope")))}, + {T.class, "x", T.label, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", T.label)))}, {VertexProperty.Cardinality.class, "x", new VariableResolverException("x"), createAntlr(VariableResolver.NoVariableResolver.instance())}, - {VertexProperty.Cardinality.class, "Cardinality.list", VertexProperty.Cardinality.list, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", "nope")))}, - {VertexProperty.Cardinality.class, "list", VertexProperty.Cardinality.list, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", "nope")))}, - {VertexProperty.Cardinality.class, "x", VertexProperty.Cardinality.list, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", VertexProperty.Cardinality.list)))}, + {VertexProperty.Cardinality.class, "Cardinality.list", VertexProperty.Cardinality.list, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", "nope")))}, + {VertexProperty.Cardinality.class, "list", VertexProperty.Cardinality.list, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", "nope")))}, + {VertexProperty.Cardinality.class, "x", VertexProperty.Cardinality.list, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", VertexProperty.Cardinality.list)))}, {DT.class, "x", new VariableResolverException("x"), createAntlr(VariableResolver.NoVariableResolver.instance())}, - {DT.class, "DT.hour", DT.hour, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", "nope")))}, - {DT.class, "hour", DT.hour, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", "nope")))}, - {DT.class, "x", DT.hour, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", DT.hour)))}, + {DT.class, "DT.hour", DT.hour, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", "nope")))}, + {DT.class, "hour", DT.hour, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", "nope")))}, + {DT.class, "x", DT.hour, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", DT.hour)))}, {Merge.class, "x", new VariableResolverException("x"), createAntlr(VariableResolver.NoVariableResolver.instance())}, - {Merge.class, "Merge.onMatch", Merge.onMatch, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", "nope")))}, - {Merge.class, "onMatch", Merge.onMatch, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", "nope")))}, - {Merge.class, "x", Merge.onMatch, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", Merge.onMatch)))}, + {Merge.class, "Merge.onMatch", Merge.onMatch, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", "nope")))}, + {Merge.class, "onMatch", Merge.onMatch, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", "nope")))}, + {Merge.class, "x", Merge.onMatch, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", Merge.onMatch)))}, {Pop.class, "x", new VariableResolverException("x"), createAntlr(VariableResolver.NoVariableResolver.instance())}, - {Pop.class, "Pop.last", Pop.last, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", "nope")))}, - {Pop.class, "last", Pop.last, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", "nope")))}, - {Pop.class, "x", Pop.last, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", Pop.last)))}, + {Pop.class, "Pop.last", Pop.last, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", "nope")))}, + {Pop.class, "last", Pop.last, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", "nope")))}, + {Pop.class, "x", Pop.last, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", Pop.last)))}, {Operator.class, "x", new VariableResolverException("x"), createAntlr(VariableResolver.NoVariableResolver.instance())}, - {Operator.class, "Operator.sum", Operator.sum, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", "nope")))}, - {Operator.class, "sum", Operator.sum, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", "nope")))}, - {Operator.class, "x", Operator.sum, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", Operator.sum)))}, + {Operator.class, "Operator.sum", Operator.sum, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", "nope")))}, + {Operator.class, "sum", Operator.sum, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", "nope")))}, + {Operator.class, "x", Operator.sum, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", Operator.sum)))}, {Column.class, "x", new VariableResolverException("x"), createAntlr(VariableResolver.NoVariableResolver.instance())}, - {Column.class, "Column.keys", Column.keys, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", "nope")))}, - {Column.class, "keys", Column.keys, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", "nope")))}, - {Column.class, "x", Column.keys, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", Column.keys)))}, + {Column.class, "Column.keys", Column.keys, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", "nope")))}, + {Column.class, "keys", Column.keys, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", "nope")))}, + {Column.class, "x", Column.keys, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", Column.keys)))}, {SackFunctions.Barrier.class, "x", new VariableResolverException("x"), createAntlr(VariableResolver.NoVariableResolver.instance())}, - {SackFunctions.Barrier.class, "Barrier.normSack", SackFunctions.Barrier.normSack, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", "nope")))}, - {SackFunctions.Barrier.class, "Barrier.normSack", SackFunctions.Barrier.normSack, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", "nope")))}, - {SackFunctions.Barrier.class, "x", SackFunctions.Barrier.normSack, createAntlr(new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", SackFunctions.Barrier.normSack)))}, + {SackFunctions.Barrier.class, "Barrier.normSack", SackFunctions.Barrier.normSack, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", "nope")))}, + {SackFunctions.Barrier.class, "Barrier.normSack", SackFunctions.Barrier.normSack, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", "nope")))}, + {SackFunctions.Barrier.class, "x", SackFunctions.Barrier.normSack, createAntlr(new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", SackFunctions.Barrier.normSack)))}, }); } diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinQueryParserTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinQueryParserTest.java index 9b67393e66..8439e56878 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinQueryParserTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/GremlinQueryParserTest.java @@ -41,7 +41,7 @@ public class GremlinQueryParserTest { public void shouldParseVariables() { final GremlinAntlrToJava gremlinAntlrToJava = new GremlinAntlrToJava("g", EmptyGraph.instance(), __::start, g, - new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("z", 50))); + new VariableResolver.DirectVariableResolver(ElementHelper.asMap("z", 50))); final GraphTraversal<?, ?> t = (GraphTraversal<?, ?>) GremlinQueryParser.parse("g.V().has('name',gt(z))", gremlinAntlrToJava); assertEquals(g.V().has("name", P.gt(50)).asAdmin().getBytecode(), @@ -52,7 +52,7 @@ public class GremlinQueryParserTest { public void shouldParseVariablesInVarargs() { final GremlinAntlrToJava gremlinAntlrToJava = new GremlinAntlrToJava("g", EmptyGraph.instance(), __::start, g, - new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", 100, + new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", 100, "y", 200, "z", 50))); @@ -73,7 +73,7 @@ public class GremlinQueryParserTest { public void shouldNotParseVariablesInList() { final GremlinAntlrToJava gremlinAntlrToJava = new GremlinAntlrToJava("g", EmptyGraph.instance(), __::start, g, - new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", 100, + new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", 100, "y", 200, "z", 50))); GremlinQueryParser.parse("g.V([x, y, 300]).has('name',gt(z))", gremlinAntlrToJava); @@ -83,7 +83,7 @@ public class GremlinQueryParserTest { public void shouldNotParseVariablesWhichAreTraversalBased() { final GremlinAntlrToJava gremlinAntlrToJava = new GremlinAntlrToJava("g", EmptyGraph.instance(), __::start, g, - new VariableResolver.DefaultVariableResolver(ElementHelper.asMap("x", 100, + new VariableResolver.DirectVariableResolver(ElementHelper.asMap("x", 100, "y", 200, "z", __.out()))); GremlinQueryParser.parse("g.V([x, y, 300]).where(z)", gremlinAntlrToJava); diff --git a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java index 3560027d2f..d853acfaee 100644 --- a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java +++ b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphPlayTest.java @@ -18,8 +18,12 @@ */ package org.apache.tinkerpop.gremlin.tinkergraph.structure; +import org.apache.tinkerpop.gremlin.jsr223.GremlinLangScriptEngine; +import org.apache.tinkerpop.gremlin.jsr223.VariableResolverCustomizer; +import org.apache.tinkerpop.gremlin.language.grammar.VariableResolver; import org.apache.tinkerpop.gremlin.process.computer.Computer; import org.apache.tinkerpop.gremlin.process.traversal.P; +import org.apache.tinkerpop.gremlin.process.traversal.Scope; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; @@ -39,6 +43,7 @@ import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.script.Bindings; import java.util.Arrays; import java.util.List; import java.util.function.BiFunction; @@ -273,17 +278,25 @@ public class TinkerGraphPlayTest { } @Test - @Ignore - public void testBugs() { + public void testBugs() throws Exception { final GraphTraversalSource g = TinkerFactory.createModern().traversal(); - Object o1 = g.V().map(__.V(1)); - System.out.println(g.V().as("a").both().as("b").dedup("a", "b").by(T.label).select("a", "b").explain()); - System.out.println(g.V().as("a").both().as("b").dedup("a", "b").by(T.label).select("a", "b").toList()); - - Traversal<?,?> t = - g.V("3"). - union(__.repeat(out().simplePath()).times(2).count(), - __.repeat(in().simplePath()).times(2).count()); + final GremlinLangScriptEngine se = new GremlinLangScriptEngine( + new VariableResolverCustomizer(VariableResolver.DefaultVariableResolver::new)); + + final Bindings b = se.createBindings(); + b.put("g", g); + System.out.println(((GraphTraversal) se.eval("g.V().coin(0.5).count()", b)).toList()); + + b.clear(); + b.put("g", g); + b.put("x", 0.5d); + System.out.println(((GraphTraversal) se.eval("g.V().coin(x).count()", b)).toList()); + + b.clear(); + b.put("g", g); + b.put("x", "josh"); + b.put("y", 32); + System.out.println(((GraphTraversal) se.eval("g.V().has('name', x).has('age', y).count()", b)).toList()); } @Test