You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by co...@apache.org on 2024/11/19 03:33:11 UTC
(tinkerpop) 01/19: wip - params
This is an automated email from the ASF dual-hosted git repository.
colegreer pushed a commit to branch cole-gvalue
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 39c31aa131772c40f2b0c2ea2a62e64f3f505a3a
Author: Stephen Mallette <st...@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 ++-
.../gremlin/process/traversal/Bytecode.java | 10 +-
.../tinkerpop/gremlin/process/traversal/P.java | 6 +-
.../traversal/dsl/graph/GraphTraversal.java | 270 +++++++++++++++++
.../traversal/lambda/ConstantTraversal.java | 15 +-
.../gremlin/process/traversal/step/GType.java | 81 ++++++
.../gremlin/process/traversal/step/GValue.java | 321 +++++++++++++++++++++
.../process/traversal/step/filter/CoinStep.java | 28 +-
.../process/traversal/step/map/AddEdgeStep.java | 6 +
.../process/traversal/step/map/AddVertexStep.java | 5 +
.../process/traversal/step/map/ConstantStep.java | 19 +-
.../process/traversal/step/map/GraphStep.java | 30 +-
.../process/traversal/step/map/MergeEdgeStep.java | 5 +
.../traversal/step/map/MergeVertexStep.java | 5 +
.../process/traversal/step/map/VertexStep.java | 37 ++-
.../process/traversal/step/util/Parameters.java | 21 +-
.../strategy/decoration/SubgraphStrategy.java | 2 +-
.../optimization/AdjacentToIncidentStrategy.java | 2 +-
.../optimization/IncidentToAdjacentStrategy.java | 2 +-
.../optimization/InlineFilterStrategy.java | 20 +-
.../gremlin/structure/VertexProperty.java | 3 -
.../gremlin/structure/io/gryo/GryoVersion.java | 12 +-
.../gremlin/jsr223/VariableResolverPluginTest.java | 117 ++++++++
.../language/grammar/ArgumentVisitorTest.java | 116 ++++----
.../language/grammar/GremlinQueryParserTest.java | 8 +-
.../gremlin/process/traversal/step/GTypeTest.java | 77 +++++
.../gremlin/process/traversal/step/GValueTest.java | 316 ++++++++++++++++++++
.../traversal/step/util/ParametersTest.java | 11 +
.../translator/JavascriptTranslatorTest.java | 8 +
.../tinkergraph/structure/TinkerGraphPlayTest.java | 33 ++-
34 files changed, 1666 insertions(+), 120 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 440bd502c8..4f523c092f 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.Order;
import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
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.Map;
@@ -441,7 +443,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/Bytecode.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Bytecode.java
index 5b88cea5fc..862ee98bd9 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Bytecode.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Bytecode.java
@@ -19,6 +19,7 @@
package org.apache.tinkerpop.gremlin.process.traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
@@ -295,8 +296,13 @@ public class Bytecode implements Cloneable, Serializable {
if (null != variable)
return new Binding<>(variable, convertArgument(argument, false));
}
- //
- if (argument instanceof Traversal) {
+
+ // a GValue gets converted to its value because we don't serialize it. it really doesn't have anything to do
+ // with bytecode/remoting. it's an internal construct related to parsing with the grammar that is leaking
+ // over here. in 4.x with bytecode removed, we won't need to worry about this.
+ if (argument instanceof GValue)
+ return convertArgument(((GValue) argument).get(), false);
+ else if (argument instanceof Traversal) {
// prevent use of "g" to spawn child traversals
if (((Traversal) argument).asAdmin().getTraversalSource().isPresent())
throw new IllegalStateException(String.format(
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 09e5f5eeb8..906029a173 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,7 @@ 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.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;
@@ -230,8 +231,277 @@ 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. For internal use for parameterization features.
+ *
+ * @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);
+ return this.asAdmin().addStep(new CoinStep<>(this.asAdmin(), probability));
+ }
+
+ /**
+ * Map any object to a fixed <code>E</code> value. For internal use for parameterization features.
+ *
+ * @return the traversal with an appended {@link ConstantStep}.
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#constant-step" target="_blank">Reference Documentation - Constant Step</a>
+ * @since 3.7.3
+ */
+ public default <E2> GraphTraversal<S, E2> constant(final GValue<E2> e) {
+ this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.constant, e);
+ return this.asAdmin().addStep(new ConstantStep<E, E2>(this.asAdmin(), e));
+ }
+
+ /**
+ * Map the {@link Vertex} to its adjacent vertices given a direction and edge labels. The arguments for the
+ * labels must be either a {@code String} or a {@link GValue<String>}. For internal use for parameterization.
+ *
+ * @param direction the direction to traverse from the current vertex
+ * @param edgeLabels the edge labels to traverse
+ * @return the traversal with an appended {@link VertexStep}.
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#vertex-steps" target="_blank">Reference Documentation - Vertex Step</a>
+ * @since 3.7.3
+ */
+ public default GraphTraversal<S, Vertex> to(final Direction direction, final Object... edgeLabels) {
+ this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.to, direction, edgeLabels);
+ return this.asAdmin().addStep(new VertexStep<>(this.asAdmin(), Vertex.class, direction, edgeLabels));
+ }
+
+ /**
+ * Map the {@link Vertex} to its outgoing adjacent vertices given the edge labels. The arguments for the
+ * labels must be either a {@code String} or a {@link GValue<String>}. For internal use for parameterization.
+ *
+ * @param edgeLabels the edge labels to traverse
+ * @return the traversal with an appended {@link VertexStep}.
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#vertex-steps" target="_blank">Reference Documentation - Vertex Step</a>
+ * @since 3.7.3
+ */
+ public default GraphTraversal<S, Vertex> out(final Object... edgeLabels) {
+ this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.out, edgeLabels);
+ return this.asAdmin().addStep(new VertexStep<>(this.asAdmin(), Vertex.class, Direction.OUT, edgeLabels));
+ }
+
+ /**
+ * Map the {@link Vertex} to its incoming adjacent vertices given the edge labels. The arguments for the
+ * labels must be either a {@code String} or a {@link GValue<String>}. For internal use for parameterization.
+ *
+ * @param edgeLabels the edge labels to traverse
+ * @return the traversal with an appended {@link VertexStep}.
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#vertex-steps" target="_blank">Reference Documentation - Vertex Step</a>
+ * @since 3.7.3
+ */
+ public default GraphTraversal<S, Vertex> in(final Object... edgeLabels) {
+ this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.in, edgeLabels);
+ return this.asAdmin().addStep(new VertexStep<>(this.asAdmin(), Vertex.class, Direction.IN, edgeLabels));
+ }
+
+ /**
+ * Map the {@link Vertex} to its adjacent vertices given the edge labels. The arguments for the labels must be
+ * either a {@code String} or a {@link GValue<String>}. For internal use for parameterization.
+ *
+ * @param edgeLabels the edge labels to traverse
+ * @return the traversal with an appended {@link VertexStep}.
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#vertex-steps" target="_blank">Reference Documentation - Vertex Step</a>
+ * @since 3.7.3
+ */
+ public default GraphTraversal<S, Vertex> both(final Object... edgeLabels) {
+ this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.both, edgeLabels);
+ return this.asAdmin().addStep(new VertexStep<>(this.asAdmin(), Vertex.class, Direction.BOTH, edgeLabels));
+ }
+
+ /**
+ * Map the {@link Vertex} to its incident edges given the direction and edge labels. The arguments for the
+ * labels must be either a {@code String} or a {@link GValue<String>}. For internal use for parameterization.
+ *
+ * @param direction the direction to traverse from the current vertex
+ * @param edgeLabels the edge labels to traverse
+ * @return the traversal with an appended {@link VertexStep}.
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#vertex-steps" target="_blank">Reference Documentation - Vertex Step</a>
+ * @since 3.7.3
+ */
+ public default GraphTraversal<S, Edge> toE(final Direction direction, final Object... edgeLabels) {
+ this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.toE, direction, edgeLabels);
+ return this.asAdmin().addStep(new VertexStep<>(this.asAdmin(), Edge.class, direction, edgeLabels));
+ }
+
+ /**
+ * Map the {@link Vertex} to its outgoing incident edges given the edge labels. The arguments for the labels
+ * must be either a {@code String} or a {@link GValue<String>}. For internal use for parameterization.
+ *
+ * @param edgeLabels the edge labels to traverse
+ * @return the traversal with an appended {@link VertexStep}.
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#vertex-steps" target="_blank">Reference Documentation - Vertex Step</a>
+ * @since 3.7.3
+ */
+ public default GraphTraversal<S, Edge> outE(final Object... edgeLabels) {
+ this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.outE, edgeLabels);
+ return this.asAdmin().addStep(new VertexStep<>(this.asAdmin(), Edge.class, Direction.OUT, edgeLabels));
+ }
+
+ /**
+ * Map the {@link Vertex} to its incoming incident edges given the edge labels. The arguments for the labels
+ * must be either a {@code String} or a {@link GValue<String>}. For internal use for parameterization.
+ *
+ * @param edgeLabels the edge labels to traverse
+ * @return the traversal with an appended {@link VertexStep}.
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#vertex-steps" target="_blank">Reference Documentation - Vertex Step</a>
+ * @since 3.7.3
+ */
+ public default GraphTraversal<S, Edge> inE(final Object... edgeLabels) {
+ this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.inE, edgeLabels);
+ return this.asAdmin().addStep(new VertexStep<>(this.asAdmin(), Edge.class, Direction.IN, edgeLabels));
+ }
+
+ /**
+ * Map the {@link Vertex} to its incident edges given the edge labels. The arguments for the labels must be
+ * either a {@code String} or a {@link GValue<String>}. For internal use for parameterization.
+ *
+ * @param edgeLabels the edge labels to traverse
+ * @return the traversal with an appended {@link VertexStep}.
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#vertex-steps" target="_blank">Reference Documentation - Vertex Step</a>
+ * @since 3.7.3
+ */
+ public default GraphTraversal<S, Edge> bothE(final Object... edgeLabels) {
+ this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.bothE, edgeLabels);
+ return this.asAdmin().addStep(new VertexStep<>(this.asAdmin(), Edge.class, Direction.BOTH, edgeLabels));
+ }
+
+ /**
+ * Adds a {@link Vertex}.
+ *
+ * @param vertexLabel the label of the {@link Vertex} to add
+ * @return the traversal with the {@link AddVertexStep} added
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#addvertex-step" target="_blank">Reference Documentation - AddVertex Step</a>
+ * @since 3.7.3
+ */
+ public default GraphTraversal<S, Vertex> addV(final GValue<String> vertexLabel) {
+ if (null == vertexLabel || null == vertexLabel.get()) throw new IllegalArgumentException("vertexLabel cannot be null");
+ this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.addV, vertexLabel);
+ return this.asAdmin().addStep(new AddVertexStep<>(this.asAdmin(), vertexLabel));
+ }
+
+ /**
+ * Performs a merge (i.e. upsert) style operation for an {@link Vertex} using a {@code Map} as an argument.
+ * The {@code Map} represents search criteria and will match each of the supplied key/value pairs where the keys
+ * may be {@code String} property values or a value of {@link T}. If a match is not made it will use that search
+ * criteria to create the new {@link Vertex}.
+ *
+ * @param searchCreate This {@code Map} can have a key of {@link T} or a {@code String}.
+ * @since 3.7.3
+ */
+ public default GraphTraversal<S, Vertex> mergeV(final GValue<Map<Object, Object>> searchCreate) {
+ this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.mergeV, searchCreate);
+ final MergeVertexStep<S> step = new MergeVertexStep(this.asAdmin(), false, null == searchCreate ? GValue.ofMap(null) : searchCreate);
+ return this.asAdmin().addStep(step);
+ }
+
+ /**
+ * Spawns a {@link GraphTraversal} by doing a merge (i.e. upsert) style operation for an {@link Edge} using a
+ * {@code Map} as an argument.
+ *
+ * @param searchCreate This {@code Map} can have a key of {@link T} {@link Direction} or a {@code String}.
+ * @since 3.7.3
+ */
+ public default GraphTraversal<S, Edge> mergeE(final GValue<Map<Object, Object>> searchCreate) {
+ // get a construction time exception if the Map is bad
+ this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.mergeE, searchCreate);
+ final MergeEdgeStep<S> step = new MergeEdgeStep(this.asAdmin(), false, null == searchCreate ? GValue.ofMap(null) : searchCreate);
+ return this.asAdmin().addStep(step);
+ }
+
+ /**
+ * Adds an {@link Edge} with the specified edge label.
+ *
+ * @param edgeLabel the label of the newly added edge
+ * @return the traversal with the {@link AddEdgeStep} added
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#addedge-step" target="_blank">Reference Documentation - AddEdge Step</a>
+ * @since 3.7.3
+ */
+ public default GraphTraversal<S, Edge> addE(final GValue<String> edgeLabel) {
+ this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.addE, edgeLabel);
+ return this.asAdmin().addStep(new AddEdgeStep<>(this.asAdmin(), edgeLabel));
+ }
+
+ /**
+ * Filters vertices, edges and vertex properties based on their label.
+ *
+ * @param label the label of the {@link Element}
+ * @param otherLabels additional labels of the {@link Element}
+ * @return the traversal with an appended {@link HasStep}
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#has-step" target="_blank">Reference Documentation - Has Step</a>
+ * @since 3.2.2
+ */
+ public default GraphTraversal<S, E> hasLabel(final Object label, final Object... otherLabels) {
+ this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.hasLabel, label, otherLabels);
+
+ // groovy evaluation seems to do strange things with varargs given hasLabel(null, null). odd someone would
+ // do this but the failure is ugly if not handled.
+ final int otherLabelsLength = null == otherLabels ? 0 : otherLabels.length;
+ final Object[] labels = new Object[otherLabelsLength + 1];
+ labels[0] = label;
+ if (otherLabelsLength > 0)
+ System.arraycopy(otherLabels, 0, labels, 1, otherLabelsLength);
+ return TraversalHelper.addHasContainer(this.asAdmin(), new HasContainer(T.label.getAccessor(), labels.length == 1 ? P.eq(labels[0]) : P.within(labels)));
+ }
+
+ /**
+ * This is a step modulator to a {@link TraversalOptionParent} like {@code choose()} or {@code mergeV()} where the
+ * provided argument associated to the {@code token} is applied according to the semantics of the step. Please see
+ * the documentation of such steps to understand the usage context.
+ *
+ * @param m Provides a {@code Map} as the option which is the same as doing {@code constant(m)}.
+ * @return the traversal with the modulated step
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#mergev-step" target="_blank">Reference Documentation - MergeV Step</a>
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#mergee-step" target="_blank">Reference Documentation - MergeE Step</a>
+ * @since 3.7.3
+ */
+ public default <M, E2> GraphTraversal<S, E> option(final M token, final GValue<Map<Object, Object>> m) {
+ this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.option, token, m);
+ ((TraversalOptionParent<M, E, E2>) this.asAdmin().getEndStep()).addChildOption(token, (Traversal.Admin<E, E2>) new ConstantTraversal<>(m).asAdmin());
+ return this;
+ }
+
+ /**
+ * This is a step modulator to a {@link TraversalOptionParent} like {@code choose()} or {@code mergeV()} where the
+ * provided argument associated to the {@code token} is applied according to the semantics of the step. Please see
+ * the documentation of such steps to understand the usage context.
+ *
+ * @param m Provides a {@code Map} as the option which is the same as doing {@code constant(m)}.
+ * @return the traversal with the modulated step
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#mergev-step" target="_blank">Reference Documentation - MergeV Step</a>
+ * @see <a href="http://tinkerpop.apache.org/docs/${project.version}/reference/#mergee-step" target="_blank">Reference Documentation - MergeE Step</a>
+ * @since 3.7.3
+ */
+ public default <M, E2> GraphTraversal<S, E> option(final Merge merge, final GValue<Map<Object, Object>> m, final VertexProperty.Cardinality cardinality) {
+ this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.option, merge, m, cardinality);
+
+ final Map<Object, Object> map = m.get();
+
+ // do explicit cardinality for every single pair in the map
+ for (Object k : map.keySet()) {
+ final Object o = map.get(k);
+ if (!(o instanceof CardinalityValueTraversal))
+ map.put(k, new CardinalityValueTraversal(cardinality, o));
+ }
+ ((TraversalOptionParent<M, E, E2>) this.asAdmin().getEndStep()).addChildOption((M) merge, (Traversal.Admin<E, E2>) new ConstantTraversal<>(m).asAdmin());
+ return this;
+ }
+
@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/lambda/ConstantTraversal.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/ConstantTraversal.java
index b22d88e66c..3bac69ce48 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/ConstantTraversal.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/ConstantTraversal.java
@@ -19,6 +19,7 @@
package org.apache.tinkerpop.gremlin.process.traversal.lambda;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
import java.util.Objects;
@@ -29,15 +30,23 @@ import java.util.Objects;
*/
public final class ConstantTraversal<S, E> extends AbstractLambdaTraversal<S, E> {
- private final E end;
+ private final GValue<E> end;
public ConstantTraversal(final E end) {
- this.end = end;
+ this.end = null == end ? GValue.of(null) : GValue.of(end);
+ }
+
+ public ConstantTraversal(final GValue<E> end) {
+ this.end = null == end ? GValue.of(null) : end;
}
@Override
public E next() {
- return this.end;
+ return this.end.get();
+ }
+
+ public GValue<E> getEnd() {
+ return end;
}
@Override
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..4de2ca1f8e
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/GValue.java
@@ -0,0 +1,321 @@
+/*
+ * 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.io.Serializable;
+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> implements Cloneable, Serializable {
+ 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;
+ }
+
+ /**
+ * Determines if the value held by this object was defined as a variable or a literal value. Literal values simply
+ * have no name.
+ */
+ public boolean isVariable() {
+ return this.name != null;
+ }
+
+ /**
+ * Gets the name of the variable if it was defined as such and returns empty if the value was a literal.
+ */
+ public Optional<String> getName() {
+ return Optional.ofNullable(this.name);
+ }
+
+ /**
+ * Gets the type of the value. The explicit type could be determined with {@code instanceof} on the value, but this
+ * might be helpful for cases where the value was constructed with a {@code null} value which might just return as
+ * {@code Object}.
+ */
+ public GType getType() {
+ return this.type;
+ }
+
+ /**
+ * Gets the value.
+ */
+ public V get() {
+ return this.value;
+ }
+
+ @Override
+ public String toString() {
+ return isVariable() ?
+ String.format("%s&%s", name, value) : Objects.toString(value);
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ GValue<?> gValue = (GValue<?>) o;
+ return Objects.equals(name, gValue.name) && type == gValue.type && Objects.equals(value, gValue.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, type, value);
+ }
+
+ /**
+ * Create a new {@code Var} from a particular value but without the specified name.
+ *
+ * @param value the value of the variable
+ */
+ public static <V> GValue<V> of(final V value) {
+ return new GValue<>(GType.getType(value), 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);
+ }
+ /**
+ * Create a new {@code GValue} for a string value.
+ */
+ public static GValue<String> ofString(final String value) {
+ return new GValue<>(GType.STRING, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for a string value with a specified name.
+ */
+ public static GValue<String> ofString(final String name, final String value) {
+ return new GValue<>(name, GType.STRING, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for an integer value.
+ */
+ public static GValue<Integer> ofInteger(final Integer value) {
+ return new GValue<>(GType.INTEGER, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for an integer value with a specified name.
+ */
+ public static GValue<Integer> ofInteger(final String name, final Integer value) {
+ return new GValue<>(name, GType.INTEGER, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for a boolean value.
+ */
+ public static GValue<Boolean> ofBoolean(final Boolean value) {
+ return new GValue<>(GType.BOOLEAN, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for a boolean value with a specified name.
+ */
+ public static GValue<Boolean> ofBoolean(final String name, final Boolean value) {
+ return new GValue<>(name, GType.BOOLEAN, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for a double value.
+ */
+ public static GValue<Double> ofDouble(final Double value) {
+ return new GValue<>(GType.DOUBLE, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for a double value with a specified name.
+ */
+ public static GValue<Double> ofDouble(final String name, final Double value) {
+ return new GValue<>(name, GType.DOUBLE, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for a BigInteger value.
+ */
+ public static GValue<BigInteger> ofBigInteger(final BigInteger value) {
+ return new GValue<>(GType.BIG_INTEGER, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for a BigInteger value with a specified name.
+ */
+ public static GValue<BigInteger> ofBigInteger(final String name, final BigInteger value) {
+ return new GValue<>(name, GType.BIG_INTEGER, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for a BigDecimal value.
+ */
+ public static GValue<BigDecimal> ofBigDecimal(final BigDecimal value) {
+ return new GValue<>(GType.BIG_DECIMAL, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for a BigDecimal value with a specified name.
+ */
+ public static GValue<BigDecimal> ofBigDecimal(final String name, final BigDecimal value) {
+ return new GValue<>(name, GType.BIG_DECIMAL, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for a long value.
+ */
+ public static GValue<Long> ofLong(final Long value) {
+ return new GValue<>(GType.LONG, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for a long value with a specified name.
+ */
+ public static GValue<Long> ofLong(final String name, final Long value) {
+ return new GValue<>(name, GType.LONG, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for a map value.
+ */
+ public static GValue<Map> ofMap(final Map value) {
+ return new GValue<>(GType.MAP, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for a map value with a specified name.
+ */
+ public static GValue<Map> ofMap(final String name, final Map value) {
+ return new GValue<>(name, GType.MAP, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for a list value.
+ */
+ public static GValue<List> ofList(final List value) {
+ return new GValue<>(GType.LIST, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for a list value with a specified name.
+ */
+ public static GValue<List> ofList(final String name, final List value) {
+ return new GValue<>(name, GType.LIST, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for a set value.
+ */
+ public static GValue<Set> ofSet(final Set value) {
+ return new GValue<>(GType.SET, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for a set value with a specified name.
+ */
+ public static GValue<Set> ofSet(final String name, final Set value) {
+ return new GValue<>(name, GType.SET, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for a vertex value.
+ */
+ public static GValue<Vertex> ofVertex(final Vertex value) {
+ return new GValue<>(GType.VERTEX, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for a vertex value with a specified name.
+ */
+ public static GValue<Vertex> ofVertex(final String name, final Vertex value) {
+ return new GValue<>(name, GType.VERTEX, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for an edge value.
+ */
+ public static GValue<Edge> ofEdge(final Edge value) {
+ return new GValue<>(GType.EDGE, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for an edge value with a specified name.
+ */
+ public static GValue<Edge> ofEdge(final String name, final Edge value) {
+ return new GValue<>(name, GType.EDGE, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for a path value.
+ */
+ public static GValue<Path> ofPath(final Path value) {
+ return new GValue<>(GType.PATH, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for a path value with a specified name.
+ */
+ public static GValue<Path> ofPath(final String name, final Path value) {
+ return new GValue<>(name, GType.PATH, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for a property value.
+ */
+ public static GValue<Property> ofProperty(final Property value) {
+ return new GValue<>(GType.PROPERTY, value);
+ }
+
+ /**
+ * Create a new {@code GValue} for a property value with a specified name.
+ */
+ 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..e220952200 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,16 +30,27 @@ 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 = probability;
+ this.probability = GValue.ofDouble(probability);
+ }
+
+ public CoinStep(final Traversal.Admin traversal, final GValue<Double> probability) {
+ super(traversal);
+ this.probability = null == probability ? GValue.ofDouble(null) : probability;
}
@Override
@@ -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/process/traversal/step/map/AddEdgeStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java
index f5f7280764..9dfa4ce754 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java
@@ -21,6 +21,7 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.map;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
import org.apache.tinkerpop.gremlin.process.traversal.step.FromToModulating;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
import org.apache.tinkerpop.gremlin.process.traversal.step.Scoping;
import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
import org.apache.tinkerpop.gremlin.process.traversal.step.Writing;
@@ -59,6 +60,11 @@ public class AddEdgeStep<S> extends ScalarMapStep<S, Edge>
this.parameters.set(this, T.label, edgeLabel);
}
+ public AddEdgeStep(final Traversal.Admin traversal, final GValue<String> edgeLabel) {
+ super(traversal);
+ this.parameters.set(this, T.label, edgeLabel);
+ }
+
public AddEdgeStep(final Traversal.Admin traversal, final Traversal.Admin<S,String> edgeLabelTraversal) {
super(traversal);
this.parameters.set(this, T.label, edgeLabelTraversal);
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java
index 80597ca86c..3a433463e8 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java
@@ -20,6 +20,7 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.map;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
import org.apache.tinkerpop.gremlin.process.traversal.step.Scoping;
import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
import org.apache.tinkerpop.gremlin.process.traversal.step.Writing;
@@ -50,6 +51,10 @@ public class AddVertexStep<S> extends ScalarMapStep<S, Vertex>
super(traversal);
this.parameters.set(this, T.label, null == label ? Vertex.DEFAULT_LABEL : label);
}
+ public AddVertexStep(final Traversal.Admin traversal, final GValue<String> label) {
+ super(traversal);
+ this.parameters.set(this, T.label, null == label ? Vertex.DEFAULT_LABEL : label);
+ }
public AddVertexStep(final Traversal.Admin traversal, final Traversal.Admin<S,String> vertexLabelTraversal) {
super(traversal);
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConstantStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConstantStep.java
index 9434cc3b47..bf554d1aa9 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConstantStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConstantStep.java
@@ -20,6 +20,7 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.map;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+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,29 +30,37 @@ import java.util.Set;
public class ConstantStep<S, E> extends ScalarMapStep<S, E> {
- private final E constant;
+ private final GValue<E> constant;
public ConstantStep(final Traversal.Admin traversal, final E constant) {
+ this(traversal, GValue.of(constant));
+ }
+
+ public ConstantStep(final Traversal.Admin traversal, final GValue<E> constant) {
super(traversal);
- this.constant = constant;
+ this.constant = null == constant ? GValue.of(null) : constant;
}
public E getConstant() {
+ return this.constant.get();
+ }
+
+ public GValue<E> getConstantGValue() {
return this.constant;
}
@Override
protected E map(final Traverser.Admin<S> traverser) {
- return this.constant;
+ return this.constant.get();
}
@Override
public String toString() {
- return StringFactory.stepString(this, this.constant);
+ return StringFactory.stepString(this, this.constant.get());
}
@Override
public int hashCode() {
- return super.hashCode() ^ Objects.hashCode(this.constant);
+ return super.hashCode() ^ Objects.hashCode(this.constant.get());
}
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GraphStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GraphStep.java
index 3b1720ff9a..ee151aed12 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GraphStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GraphStep.java
@@ -25,6 +25,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.Step;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
import org.apache.tinkerpop.gremlin.process.traversal.step.Configuring;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
import org.apache.tinkerpop.gremlin.process.traversal.step.GraphComputing;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.AbstractStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.HasContainer;
@@ -66,11 +67,28 @@ public class GraphStep<S, E extends Element> extends AbstractStep<S, E> implemen
this.returnClass = returnClass;
this.ids = (ids != null && ids.length == 1 && ids[0] instanceof Collection) ? ((Collection) ids[0]).toArray(new Object[((Collection) ids[0]).size()]) : ids;
this.isStart = isStart;
+
this.iteratorSupplier = () -> (Iterator<E>) (Vertex.class.isAssignableFrom(this.returnClass) ?
- this.getTraversal().getGraph().get().vertices(this.ids) :
- this.getTraversal().getGraph().get().edges(this.ids));
+ this.getTraversal().getGraph().get().vertices(convertGValuesToIds()) :
+ this.getTraversal().getGraph().get().edges(convertGValuesToIds()));
}
+ /**
+ * Converts {@link GValue} objects the ids array to their values to prevent them from leaking to the Graph API.
+ */
+ private Object[] convertGValuesToIds() {
+ final Object[] newIds = new Object[this.ids.length];
+ for (int i = 0; i < this.ids.length; i++) {
+ if (newIds[i] instanceof GValue) {
+ newIds[i] = ((GValue) this.ids[i]).get();
+ } else {
+ newIds[i] = this.ids[i];
+ }
+ }
+ return newIds;
+ }
+
+
public String toString() {
return StringFactory.stepString(this, this.returnClass.getSimpleName().toLowerCase(), Arrays.toString(this.ids));
}
@@ -137,7 +155,13 @@ public class GraphStep<S, E extends Element> extends AbstractStep<S, E> implemen
public void convertElementsToIds() {
if (null != this.ids) {
- for (int i = 0; i < this.ids.length; i++) { // if this is going to OLAP, convert to ids so you don't serialize elements
+ // if this is going to OLAP, convert to ids so you don't serialize elements
+ for (int i = 0; i < this.ids.length; i++) {
+
+ // spare the Graph API from GValue objects, as they are Gremlin level objects
+ if (this.ids[i] instanceof GValue)
+ this.ids[i] = ((GValue) this.ids[i]).get();
+
if (this.ids[i] instanceof Element)
this.ids[i] = ((Element) this.ids[i]).id();
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeEdgeStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeEdgeStep.java
index bb22d218e5..5beb203a24 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeEdgeStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeEdgeStep.java
@@ -34,6 +34,7 @@ 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.lambda.ConstantTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.EventUtil;
import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil;
import org.apache.tinkerpop.gremlin.structure.Direction;
@@ -71,6 +72,10 @@ public class MergeEdgeStep<S> extends MergeElementStep<S, Edge, Object> {
super(traversal, isStart, merge);
}
+ public MergeEdgeStep(final Traversal.Admin traversal, final boolean isStart, final GValue<Map> merge) {
+ super(traversal, isStart, merge);
+ }
+
public MergeEdgeStep(final Traversal.Admin traversal, final boolean isStart, final Traversal.Admin<S,Map> mergeTraversal) {
super(traversal, isStart, mergeTraversal);
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeVertexStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeVertexStep.java
index 9aab3790ac..e15ff745bc 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeVertexStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeVertexStep.java
@@ -30,6 +30,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
import org.apache.tinkerpop.gremlin.process.traversal.lambda.CardinalityValueTraversal;
import org.apache.tinkerpop.gremlin.process.traversal.lambda.ConstantTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.EventUtil;
import org.apache.tinkerpop.gremlin.structure.Graph;
import org.apache.tinkerpop.gremlin.structure.T;
@@ -60,6 +61,10 @@ public class MergeVertexStep<S> extends MergeElementStep<S, Vertex, Map> {
super(traversal, isStart, merge);
}
+ public MergeVertexStep(final Traversal.Admin traversal, final boolean isStart, final GValue<Map> merge) {
+ super(traversal, isStart, merge);
+ }
+
public MergeVertexStep(final Traversal.Admin traversal, final boolean isStart, final Traversal.Admin<S,Map> mergeTraversal) {
super(traversal, isStart, mergeTraversal);
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/VertexStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/VertexStep.java
index bd429f049c..e27fbb3802 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/VertexStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/VertexStep.java
@@ -21,6 +21,7 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.map;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
import org.apache.tinkerpop.gremlin.process.traversal.step.Configuring;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters;
import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
import org.apache.tinkerpop.gremlin.structure.Direction;
@@ -39,20 +40,41 @@ import java.util.Set;
import java.util.stream.Collectors;
/**
+ * Handles the logic of traversing to adjacent vertices or edges given a direction and edge labels for steps like,
+ * {@code out}, {@code in}, {@code both}, {@code outE}, {@code inE}, and {@code bothE}.
+ *
* @author Marko A. Rodriguez (http://markorodriguez.com)
*/
public class VertexStep<E extends Element> extends FlatMapStep<Vertex, E> implements AutoCloseable, Configuring {
protected Parameters parameters = new Parameters();
private final String[] edgeLabels;
+ private final GValue<String>[] edgeLabelsGValue;
private Direction direction;
private final Class<E> returnClass;
public VertexStep(final Traversal.Admin traversal, final Class<E> returnClass, final Direction direction, final String... edgeLabels) {
+ this(traversal, returnClass, direction, (Object[]) edgeLabels);
+ }
+
+ public VertexStep(final Traversal.Admin traversal, final Class<E> returnClass, final Direction direction, final Object... edgeLabels) {
super(traversal);
this.direction = direction;
- this.edgeLabels = edgeLabels;
this.returnClass = returnClass;
+
+ // check each edgeLabel to ensure it is a string or a GValue with a GType.STRING in it. if it is just a string
+ // then convert edgeLabels to a GValue<String> or otherwise throw an exception
+ this.edgeLabelsGValue = Arrays.stream(edgeLabels).map(edgeLabel -> {
+ if (edgeLabel instanceof String)
+ return GValue.ofString((String) edgeLabel);
+ else if (edgeLabel instanceof GValue && ((GValue<String>) edgeLabel).get().getClass().equals(String.class))
+ return (GValue<String>) edgeLabel;
+ else
+ throw new IllegalArgumentException("All edge labels must be strings");
+ }).toArray(GValue[]::new);
+
+ // convert the GValue<String> to a String[] for the edgeLabels field to cache the values
+ this.edgeLabels = Arrays.stream(this.edgeLabelsGValue).map(GValue::get).toArray(String[]::new);
}
@Override
@@ -67,6 +89,9 @@ public class VertexStep<E extends Element> extends FlatMapStep<Vertex, E> implem
@Override
protected Iterator<E> flatMap(final Traverser.Admin<Vertex> traverser) {
+ // not passing GValue to graphs at this point. if a graph wants to support GValue, it should implement
+ // its own step to do so. in this way, we keep things backwards compatible and don't force folks to have
+ // deal with this until they are ready.
return Vertex.class.isAssignableFrom(this.returnClass) ?
(Iterator<E>) traverser.get().vertices(this.direction, this.edgeLabels) :
(Iterator<E>) traverser.get().edges(this.direction, this.edgeLabels);
@@ -80,6 +105,10 @@ public class VertexStep<E extends Element> extends FlatMapStep<Vertex, E> implem
return this.edgeLabels;
}
+ public GValue<String>[] getEdgeLabelsGValue() {
+ return this.edgeLabelsGValue;
+ }
+
public Class<E> getReturnClass() {
return this.returnClass;
}
@@ -88,10 +117,16 @@ public class VertexStep<E extends Element> extends FlatMapStep<Vertex, E> implem
this.direction = this.direction.opposite();
}
+ /**
+ * Determines if the step returns vertices.
+ */
public boolean returnsVertex() {
return this.returnClass.equals(Vertex.class);
}
+ /**
+ * Determines if the step returns edges.
+ */
public boolean returnsEdge() {
return this.returnClass.equals(Edge.class);
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/Parameters.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/Parameters.java
index d1654a344e..9b4a926c74 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/Parameters.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/Parameters.java
@@ -22,6 +22,7 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.util;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
import org.apache.tinkerpop.gremlin.process.traversal.step.Scoping;
import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil;
@@ -165,7 +166,7 @@ public class Parameters implements Cloneable, Serializable {
/**
* Gets the array of keys/values of the parameters while resolving parameter values that contain
- * {@link Traversal} instances.
+ * {@link Traversal} or {@link GValue} instances.
*/
public <S> Object[] getKeyValues(final Traverser.Admin<S> traverser, final Object... exceptKeys) {
if (this.parameters.isEmpty()) return EMPTY_ARRAY;
@@ -173,14 +174,28 @@ public class Parameters implements Cloneable, Serializable {
for (final Map.Entry<Object, List<Object>> entry : this.parameters.entrySet()) {
if (!ArrayUtils.contains(exceptKeys, entry.getKey())) {
for (final Object value : entry.getValue()) {
- keyValues.add(entry.getKey() instanceof Traversal.Admin ? TraversalUtil.apply(traverser, (Traversal.Admin<S, ?>) entry.getKey()) : entry.getKey());
- keyValues.add(value instanceof Traversal.Admin ? TraversalUtil.apply(traverser, (Traversal.Admin<S, ?>) value) : value);
+ keyValues.add(resolve(entry.getKey(), traverser));
+ keyValues.add(resolve(value, traverser));
}
}
}
return keyValues.toArray(new Object[keyValues.size()]);
}
+ /**
+ * Takes an object and tests if it is a {@link GValue} or a {@link Traversal} and if so, resolves it to its value
+ * otherwise it just returns itself.
+ */
+ private static <S> Object resolve(final Object object, final Traverser.Admin<S> traverser) {
+ if (object instanceof Traversal.Admin) {
+ return TraversalUtil.apply(traverser, (Traversal.Admin<S, ?>) object);
+ } else if (object instanceof GValue) {
+ return ((GValue) object).get();
+ } else {
+ return object;
+ }
+ }
+
/**
* Gets an immutable set of the parameters without evaluating them in the context of a {@link Traverser} as
* is done in {@link #getKeyValues(Traverser.Admin, Object...)}.
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/SubgraphStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/SubgraphStrategy.java
index 6f34d62226..b44b62f0e3 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/SubgraphStrategy.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/SubgraphStrategy.java
@@ -193,7 +193,7 @@ public final class SubgraphStrategy extends AbstractTraversalStrategy<TraversalS
continue;
if (edgeCriterion != null) {
- final VertexStep<Edge> someEStep = new VertexStep<>(traversal, Edge.class, step.getDirection(), step.getEdgeLabels());
+ final VertexStep<Edge> someEStep = new VertexStep<>(traversal, Edge.class, step.getDirection(), step.getEdgeLabelsGValue());
final Step<Edge, Vertex> someVStep = step.getDirection() == Direction.BOTH ?
new EdgeOtherVertexStep(traversal) :
new EdgeVertexStep(traversal, step.getDirection().opposite());
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/AdjacentToIncidentStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/AdjacentToIncidentStrategy.java
index d8e538ab37..b33d527c2c 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/AdjacentToIncidentStrategy.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/AdjacentToIncidentStrategy.java
@@ -118,7 +118,7 @@ public final class AdjacentToIncidentStrategy extends AbstractTraversalStrategy<
final Step newStep;
if (step instanceof VertexStep) {
final VertexStep vs = (VertexStep) step;
- newStep = new VertexStep<>(traversal, Edge.class, vs.getDirection(), vs.getEdgeLabels());
+ newStep = new VertexStep<>(traversal, Edge.class, vs.getDirection(), vs.getEdgeLabelsGValue());
} else if (step instanceof PropertiesStep) {
final PropertiesStep ps = (PropertiesStep) step;
newStep = new PropertiesStep(traversal, PropertyType.PROPERTY, ps.getPropertyKeys());
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/IncidentToAdjacentStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/IncidentToAdjacentStrategy.java
index 75d55adcd0..813b60b955 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/IncidentToAdjacentStrategy.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/IncidentToAdjacentStrategy.java
@@ -110,7 +110,7 @@ public final class IncidentToAdjacentStrategy extends AbstractTraversalStrategy<
* @param step2 the vertex-emitting step to replace
*/
private static void optimizeSteps(final Traversal.Admin traversal, final VertexStep step1, final Step step2) {
- final Step newStep = new VertexStep(traversal, Vertex.class, step1.getDirection(), step1.getEdgeLabels());
+ final Step newStep = new VertexStep(traversal, Vertex.class, step1.getDirection(), step1.getEdgeLabelsGValue());
for (final String label : (Iterable<String>) step2.getLabels()) {
newStep.addLabel(label);
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/InlineFilterStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/InlineFilterStrategy.java
index d2713218c5..5ff63b3e0f 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/InlineFilterStrategy.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/InlineFilterStrategy.java
@@ -26,6 +26,8 @@ import org.apache.tinkerpop.gremlin.process.traversal.P;
import org.apache.tinkerpop.gremlin.process.traversal.Step;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
+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.LambdaHolder;
import org.apache.tinkerpop.gremlin.process.traversal.step.filter.AndStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.filter.DedupGlobalStep;
@@ -124,26 +126,28 @@ public final class InlineFilterStrategy extends AbstractTraversalStrategy<Traver
&& ((VertexStep) step.getPreviousStep()).returnsEdge()
&& 0 == ((VertexStep) step.getPreviousStep()).getEdgeLabels().length) {
final VertexStep<Edge> previousStep = (VertexStep<Edge>) step.getPreviousStep();
- final List<String> edgeLabels = new ArrayList<>();
+ final List<Object> edgeLabels = new ArrayList<>();
for (final HasContainer hasContainer : new ArrayList<>(step.getHasContainers())) {
if (hasContainer.getKey().equals(T.label.getAccessor())) {
if (hasContainer.getBiPredicate() == Compare.eq &&
- hasContainer.getValue() instanceof String &&
+ (hasContainer.getValue() instanceof String ||
+ (hasContainer.getValue() instanceof GValue && ((GValue) hasContainer.getValue()).getType() == GType.STRING)) &&
edgeLabels.isEmpty()) {
- edgeLabels.add((String) hasContainer.getValue());
+ edgeLabels.add(hasContainer.getValue());
step.removeHasContainer(hasContainer);
} else if (hasContainer.getBiPredicate() == Contains.within &&
hasContainer.getValue() instanceof Collection &&
((Collection) hasContainer.getValue()).containsAll(edgeLabels)) {
- edgeLabels.addAll((Collection<String>) hasContainer.getValue());
+ edgeLabels.addAll((Collection) hasContainer.getValue());
step.removeHasContainer(hasContainer);
} else if (hasContainer.getPredicate() instanceof OrP && edgeLabels.isEmpty()) {
boolean removeContainer = true;
final List<P<?>> orps = ((OrP) hasContainer.getPredicate()).getPredicates();
- final List<String> newEdges = new ArrayList<>();
+ final List<Object> newEdges = new ArrayList<>();
for (int i = 0; i < orps.size(); i++) {
- if (orps.get(i).getBiPredicate() == Compare.eq && orps.get(i).getValue() instanceof String)
- newEdges.add((String) orps.get(i).getValue());
+ if (orps.get(i).getBiPredicate() == Compare.eq && (orps.get(i).getValue() instanceof String ||
+ (orps.get(i).getValue() instanceof GValue && ((GValue) orps.get(i).getValue()).getType() == GType.STRING)))
+ newEdges.add(orps.get(i).getValue());
else {
removeContainer = false;
break;
@@ -157,7 +161,7 @@ public final class InlineFilterStrategy extends AbstractTraversalStrategy<Traver
}
}
if (!edgeLabels.isEmpty()) {
- final VertexStep<Edge> newVertexStep = new VertexStep<>(traversal, Edge.class, previousStep.getDirection(), edgeLabels.toArray(new String[edgeLabels.size()]));
+ final VertexStep<Edge> newVertexStep = new VertexStep<>(traversal, Edge.class, previousStep.getDirection(), edgeLabels.toArray(new Object[edgeLabels.size()]));
TraversalHelper.replaceStep(previousStep, newVertexStep, traversal);
TraversalHelper.copyLabels(previousStep, newVertexStep, false);
if (step.getHasContainers().isEmpty()) {
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/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java
index f056c8ed2e..498626c3ad 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java
@@ -38,6 +38,8 @@ import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
import org.apache.tinkerpop.gremlin.process.traversal.Scope;
import org.apache.tinkerpop.gremlin.process.traversal.Text;
import org.apache.tinkerpop.gremlin.process.traversal.TextP;
+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.filter.RangeGlobalStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.FoldStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.GroupCountStep;
@@ -324,7 +326,7 @@ public enum GryoVersion {
add(GryoTypeReg.of(Bytecode.class, 122, new GryoSerializersV3.BytecodeSerializer()));
add(GryoTypeReg.of(P.class, 124, new GryoSerializersV3.PSerializer()));
add(GryoTypeReg.of(TextP.class, 186, new GryoSerializersV3.TextPSerializer()));
- add(GryoTypeReg.of(Text.RegexPredicate.class, 197)); // ***LAST ID***
+ add(GryoTypeReg.of(Text.RegexPredicate.class, 197));
add(GryoTypeReg.of(Lambda.class, 125, new GryoSerializersV3.LambdaSerializer()));
add(GryoTypeReg.of(Bytecode.Binding.class, 126, new GryoSerializersV3.BindingSerializer()));
add(GryoTypeReg.of(Order.class, 127));
@@ -422,6 +424,9 @@ public enum GryoVersion {
add(GryoTypeReg.of(Stack.class, 181));
add(GryoTypeReg.of(ReferenceMap.class, 182));
+ add(GryoTypeReg.of(GValue.class, 199, new JavaSerializer()));
+ add(GryoTypeReg.of(GType.class, 200, new JavaSerializer())); // ***LAST ID***
+
// placeholder serializers for classes that don't live here in core. this will allow them to be used if
// present or ignored if the class isn't available. either way the registration numbers are held as
// placeholders so that the format stays stable
@@ -537,7 +542,7 @@ public enum GryoVersion {
add(GryoTypeReg.of(Pop.class, 133));
add(GryoTypeReg.of(SackFunctions.Barrier.class, 135));
add(GryoTypeReg.of(Pick.class, 137));
- add(GryoTypeReg.of(DT.class, 198)); // ***LAST ID***
+ add(GryoTypeReg.of(DT.class, 198));
add(GryoTypeReg.of(Merge.class, 196));
add(GryoTypeReg.of(HashSetSupplier.class, 136, new UtilSerializers.HashSetSupplierSerializer()));
add(GryoTypeReg.of(MultiComparator.class, 165));
@@ -628,6 +633,9 @@ public enum GryoVersion {
add(GryoTypeReg.of(LabelledCounter.class, 180));
add(GryoTypeReg.of(Stack.class, 181));
add(GryoTypeReg.of(ReferenceMap.class, 182));
+
+ add(GryoTypeReg.of(GValue.class, 199, new JavaSerializer()));
+ add(GryoTypeReg.of(GType.class, 200, new JavaSerializer())); // ***LAST ID***
}};
}
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/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/GTypeTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/GTypeTest.java
new file mode 100644
index 0000000000..9596445860
--- /dev/null
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/GTypeTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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 org.junit.Test;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Collections;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+
+public class GTypeTest {
+
+ @Test
+ public void shouldReturnTrueForNumericTypes() {
+ assertThat(GType.INTEGER.isNumeric(), is(true));
+ assertThat(GType.DOUBLE.isNumeric(), is(true));
+ assertThat(GType.LONG.isNumeric(), is(true));
+ assertThat(GType.BIG_INTEGER.isNumeric(), is(true));
+ assertThat(GType.BIG_DECIMAL.isNumeric(), is(true));
+ }
+
+ @Test
+ public void shouldReturnFalseForNonNumericTypes() {
+ assertThat(GType.STRING.isNumeric(), is(false));
+ assertThat(GType.BOOLEAN.isNumeric(), is(false));
+ assertThat(GType.EDGE.isNumeric(), is(false));
+ assertThat(GType.VERTEX.isNumeric(), is(false));
+ }
+
+ @Test
+ public void shouldReturnCorrectGType() {
+ assertEquals(GType.STRING, GType.getType("test"));
+ assertEquals(GType.INTEGER, GType.getType(123));
+ assertEquals(GType.BOOLEAN, GType.getType(true));
+ assertEquals(GType.DOUBLE, GType.getType(123.45));
+ assertEquals(GType.LONG, GType.getType(123L));
+ assertEquals(GType.MAP, GType.getType(Collections.emptyMap()));
+ assertEquals(GType.LIST, GType.getType(Collections.emptyList()));
+ assertEquals(GType.SET, GType.getType(Collections.emptySet()));
+ assertEquals(GType.VERTEX, GType.getType(mock(Vertex.class)));
+ assertEquals(GType.EDGE, GType.getType(mock(Edge.class)));
+ assertEquals(GType.PATH, GType.getType(mock(Path.class)));
+ assertEquals(GType.PROPERTY, GType.getType(mock(Property.class)));
+ assertEquals(GType.BIG_INTEGER, GType.getType(BigInteger.ONE));
+ assertEquals(GType.BIG_DECIMAL, GType.getType(BigDecimal.ONE));
+ }
+
+ @Test
+ public void shouldReturnUnknownForUnmatchedTypes() {
+ assertEquals(GType.UNKNOWN, GType.getType(new Object()));
+ }
+}
\ No newline at end of file
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/GValueTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/GValueTest.java
new file mode 100644
index 0000000000..fcfac9b9d9
--- /dev/null
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/GValueTest.java
@@ -0,0 +1,316 @@
+/*
+ * 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.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.mockito.Mockito.mock;
+
+public class GValueTest {
+
+ @Test
+ public void shouldCreateGValueFromValue() {
+ final GValue<Integer> gValue = GValue.of(123);
+ assertEquals(123, gValue.get().intValue());
+ assertEquals(GType.INTEGER, gValue.getType());
+ assertThat(gValue.isVariable(), is(false));
+ }
+
+ @Test
+ public void shouldCreateGValueFromNameAndValue() {
+ final GValue<Integer> gValue = GValue.of("varName", 123);
+ assertEquals(123, gValue.get().intValue());
+ assertEquals(GType.INTEGER, gValue.getType());
+ assertEquals("varName", gValue.getName().get());
+ assertThat(gValue.isVariable(), is(true));
+ }
+
+ @Test
+ public void shouldCreateGValueFromString() {
+ final GValue<String> gValue = GValue.ofString("test");
+ assertEquals("test", gValue.get());
+ assertEquals(GType.STRING, gValue.getType());
+ assertThat(gValue.isVariable(), is(false));
+ }
+
+ @Test
+ public void shouldCreateGValueFromStringWithName() {
+ final GValue<String> gValue = GValue.ofString("varName", "test");
+ assertEquals("test", gValue.get());
+ assertEquals(GType.STRING, gValue.getType());
+ assertEquals("varName", gValue.getName().get());
+ assertThat(gValue.isVariable(), is(true));
+ }
+
+ @Test
+ public void shouldCreateGValueFromInteger() {
+ final GValue<Integer> gValue = GValue.ofInteger(123);
+ assertEquals(123, gValue.get().intValue());
+ assertEquals(GType.INTEGER, gValue.getType());
+ assertThat(gValue.isVariable(), is(false));
+ }
+
+ @Test
+ public void shouldCreateGValueFromIntegerWithName() {
+ final GValue<Integer> gValue = GValue.ofInteger("varName", 123);
+ assertEquals(123, gValue.get().intValue());
+ assertEquals(GType.INTEGER, gValue.getType());
+ assertEquals("varName", gValue.getName().get());
+ assertThat(gValue.isVariable(), is(true));
+ }
+
+ @Test
+ public void shouldCreateGValueFromBoolean() {
+ final GValue<Boolean> gValue = GValue.ofBoolean(true);
+ assertEquals(true, gValue.get());
+ assertEquals(GType.BOOLEAN, gValue.getType());
+ assertThat(gValue.isVariable(), is(false));
+ }
+
+ @Test
+ public void shouldCreateGValueFromBooleanWithName() {
+ final GValue<Boolean> gValue = GValue.ofBoolean("varName", true);
+ assertEquals(true, gValue.get());
+ assertEquals(GType.BOOLEAN, gValue.getType());
+ assertEquals("varName", gValue.getName().get());
+ assertThat(gValue.isVariable(), is(true));
+ }
+
+ @Test
+ public void shouldCreateGValueFromDouble() {
+ final GValue<Double> gValue = GValue.ofDouble(123.45);
+ assertEquals(123.45, gValue.get(), 0.0);
+ assertEquals(GType.DOUBLE, gValue.getType());
+ assertThat(gValue.isVariable(), is(false));
+ }
+
+ @Test
+ public void shouldCreateGValueFromDoubleWithName() {
+ final GValue<Double> gValue = GValue.ofDouble("varName", 123.45);
+ assertEquals(123.45, gValue.get(), 0.0);
+ assertEquals(GType.DOUBLE, gValue.getType());
+ assertEquals("varName", gValue.getName().get());
+ assertThat(gValue.isVariable(), is(true));
+ }
+
+ @Test
+ public void shouldCreateGValueFromBigInteger() {
+ final GValue<BigInteger> gValue = GValue.ofBigInteger(BigInteger.ONE);
+ assertEquals(BigInteger.ONE, gValue.get());
+ assertEquals(GType.BIG_INTEGER, gValue.getType());
+ assertThat(gValue.isVariable(), is(false));
+ }
+
+ @Test
+ public void shouldCreateGValueFromBigIntegerWithName() {
+ final GValue<BigInteger> gValue = GValue.ofBigInteger("varName", BigInteger.ONE);
+ assertEquals(BigInteger.ONE, gValue.get());
+ assertEquals(GType.BIG_INTEGER, gValue.getType());
+ assertEquals("varName", gValue.getName().get());
+ assertThat(gValue.isVariable(), is(true));
+ }
+
+ @Test
+ public void shouldCreateGValueFromBigDecimal() {
+ final GValue<BigDecimal> gValue = GValue.ofBigDecimal(BigDecimal.ONE);
+ assertEquals(BigDecimal.ONE, gValue.get());
+ assertEquals(GType.BIG_DECIMAL, gValue.getType());
+ assertThat(gValue.isVariable(), is(false));
+ }
+
+ @Test
+ public void shouldCreateGValueFromBigDecimalWithName() {
+ final GValue<BigDecimal> gValue = GValue.ofBigDecimal("varName", BigDecimal.ONE);
+ assertEquals(BigDecimal.ONE, gValue.get());
+ assertEquals(GType.BIG_DECIMAL, gValue.getType());
+ assertEquals("varName", gValue.getName().get());
+ assertThat(gValue.isVariable(), is(true));
+ }
+
+ @Test
+ public void shouldCreateGValueFromLong() {
+ final GValue<Long> gValue = GValue.ofLong(123L);
+ assertEquals(123L, gValue.get().longValue());
+ assertEquals(GType.LONG, gValue.getType());
+ assertThat(gValue.isVariable(), is(false));
+ }
+
+ @Test
+ public void shouldCreateGValueFromLongWithName() {
+ final GValue<Long> gValue = GValue.ofLong("varName", 123L);
+ assertEquals(123L, gValue.get().longValue());
+ assertEquals(GType.LONG, gValue.getType());
+ assertEquals("varName", gValue.getName().get());
+ assertThat(gValue.isVariable(), is(true));
+ }
+
+ @Test
+ public void shouldCreateGValueFromMap() {
+ final Map<String, String> map = new HashMap<String,String>() {{
+ put("key", "value");
+ }};
+ final GValue<Map> gValue = GValue.ofMap(map);
+ assertEquals(map, gValue.get());
+ assertEquals(GType.MAP, gValue.getType());
+ assertThat(gValue.isVariable(), is(false));
+ }
+
+ @Test
+ public void shouldCreateGValueFromMapWithName() {
+ final Map<String, String> map = new HashMap<String,String>() {{
+ put("key", "value");
+ }};
+ final GValue<Map> gValue = GValue.ofMap("varName", map);
+ assertEquals(map, gValue.get());
+ assertEquals(GType.MAP, gValue.getType());
+ assertEquals("varName", gValue.getName().get());
+ assertThat(gValue.isVariable(), is(true));
+ }
+
+ @Test
+ public void shouldCreateGValueFromList() {
+ final List<String> list = Arrays.asList("value1", "value2");
+ final GValue<List> gValue = GValue.ofList(list);
+ assertEquals(list, gValue.get());
+ assertEquals(GType.LIST, gValue.getType());
+ assertThat(gValue.isVariable(), is(false));
+ }
+
+ @Test
+ public void shouldCreateGValueFromListWithName() {
+ final List<String> list = Arrays.asList("value1", "value2");
+ final GValue<List> gValue = GValue.ofList("varName", list);
+ assertEquals(list, gValue.get());
+ assertEquals(GType.LIST, gValue.getType());
+ assertEquals("varName", gValue.getName().get());
+ assertThat(gValue.isVariable(), is(true));
+ }
+
+ @Test
+ public void shouldCreateGValueFromSet() {
+ final Set<String> set = new HashSet<>(Arrays.asList("value1", "value2"));
+ final GValue<Set> gValue = GValue.ofSet(set);
+ assertEquals(set, gValue.get());
+ assertEquals(GType.SET, gValue.getType());
+ assertThat(gValue.isVariable(), is(false));
+ }
+
+ @Test
+ public void shouldCreateGValueFromSetWithName() {
+ final Set<String> set = new HashSet<>(Arrays.asList("value1", "value2"));
+ final GValue<Set> gValue = GValue.ofSet("varName", set);
+ assertEquals(set, gValue.get());
+ assertEquals(GType.SET, gValue.getType());
+ assertEquals("varName", gValue.getName().get());
+ assertThat(gValue.isVariable(), is(true));
+ }
+
+ @Test
+ public void shouldCreateGValueFromVertex() {
+ final Vertex vertex = mock(Vertex.class);
+ final GValue<Vertex> gValue = GValue.ofVertex(vertex);
+ assertEquals(vertex, gValue.get());
+ assertEquals(GType.VERTEX, gValue.getType());
+ assertThat(gValue.isVariable(), is(false));
+ }
+
+ @Test
+ public void shouldCreateGValueFromVertexWithName() {
+ final Vertex vertex = mock(Vertex.class);
+ final GValue<Vertex> gValue = GValue.ofVertex("varName", vertex);
+ assertEquals(vertex, gValue.get());
+ assertEquals(GType.VERTEX, gValue.getType());
+ assertEquals("varName", gValue.getName().get());
+ assertThat(gValue.isVariable(), is(true));
+ }
+
+ @Test
+ public void shouldCreateGValueFromEdge() {
+ final Edge edge = mock(Edge.class);
+ final GValue<Edge> gValue = GValue.ofEdge(edge);
+ assertEquals(edge, gValue.get());
+ assertEquals(GType.EDGE, gValue.getType());
+ assertThat(gValue.isVariable(), is(false));
+ }
+
+ @Test
+ public void shouldCreateGValueFromEdgeWithName() {
+ final Edge edge = mock(Edge.class);
+ final GValue<Edge> gValue = GValue.ofEdge("varName", edge);
+ assertEquals(edge, gValue.get());
+ assertEquals(GType.EDGE, gValue.getType());
+ assertEquals("varName", gValue.getName().get());
+ assertThat(gValue.isVariable(), is(true));
+ }
+
+ @Test
+ public void shouldCreateGValueFromPath() {
+ final Path path = mock(Path.class);
+ final GValue<Path> gValue = GValue.ofPath(path);
+ assertEquals(path, gValue.get());
+ assertEquals(GType.PATH, gValue.getType());
+ assertThat(gValue.isVariable(), is(false));
+ }
+
+ @Test
+ public void shouldCreateGValueFromPathWithName() {
+ final Path path = mock(Path.class);
+ final GValue<Path> gValue = GValue.ofPath("varName", path);
+ assertEquals(path, gValue.get());
+ assertEquals(GType.PATH, gValue.getType());
+ assertEquals("varName", gValue.getName().get());
+ assertThat(gValue.isVariable(), is(true));
+ }
+
+ @Test
+ public void shouldCreateGValueFromProperty() {
+ final Property property = mock(Property.class);
+ final GValue<Property> gValue = GValue.ofProperty(property);
+ assertEquals(property, gValue.get());
+ assertEquals(GType.PROPERTY, gValue.getType());
+ assertThat(gValue.isVariable(), is(false));
+ }
+
+ @Test
+ public void shouldCreateGValueFromPropertyWithName() {
+ final Property property = mock(Property.class);
+ final GValue<Property> gValue = GValue.ofProperty("varName", property);
+ assertEquals(property, gValue.get());
+ assertEquals(GType.PROPERTY, gValue.getType());
+ assertEquals("varName", gValue.getName().get());
+ assertThat(gValue.isVariable(), is(true));
+ }
+}
\ No newline at end of file
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/ParametersTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/ParametersTest.java
index 27d8e44e16..dd36f4c8b5 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/ParametersTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/ParametersTest.java
@@ -21,6 +21,7 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.util;
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.__;
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
import org.junit.Test;
@@ -332,4 +333,14 @@ public class ParametersTest {
verify(mock).integrateChild(__.outE("knows").asAdmin());
}
+
+ @Test
+ public void shouldGetKeyValuesAndResolveGValues() {
+ final Parameters parameters = new Parameters();
+ parameters.set(null, "a", "axe", "b", GValue.of("B", "bat"), "c", GValue.of("C", "cat"));
+
+ final Object[] params = parameters.getKeyValues(mock(Traverser.Admin.class));
+ assertEquals(6, params.length);
+ assertThat(Arrays.equals(new Object[] {"a", "axe", "b", "bat", "c", "cat"}, params), is(true));
+ }
}
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/translator/JavascriptTranslatorTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/translator/JavascriptTranslatorTest.java
index d799fd2ddf..ad7f10635e 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/translator/JavascriptTranslatorTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/translator/JavascriptTranslatorTest.java
@@ -21,6 +21,7 @@ package org.apache.tinkerpop.gremlin.process.traversal.translator;
import org.apache.tinkerpop.gremlin.process.traversal.GraphOp;
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.Scope;
import org.apache.tinkerpop.gremlin.process.traversal.TextP;
@@ -110,6 +111,13 @@ public class JavascriptTranslatorTest {
assertTranslation("Pop.last", Pop.last);
}
+ @Test
+ public void shouldTranslateP() {
+ assertTranslation("P.eq(\"ark\")", P.eq("ark"));
+ assertTranslation("P.within([\"ark\"])", P.within("ark"));
+ assertTranslation("P.neq(\"ark\")", P.neq("ark"));
+ }
+
@Test
public void shouldTranslateTextP() {
assertTranslation("TextP.containing(\"ark\")", TextP.containing("ark"));
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