This is an automated email from the ASF dual-hosted git repository. spmallette pushed a commit to branch gvalue-redux-3.7 in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 02fbda155c27dea12225fd98b148399fdb46147f Author: Stephen Mallette <[email protected]> AuthorDate: Sun Aug 18 13:55:46 2024 -0400 wip --- .../tinkerpop/gremlin/jsr223/JavaTranslator.java | 6 + .../strategy/decoration/VertexProgramStrategy.java | 1 + .../gremlin/process/traversal/Bytecode.java | 8 +- .../process/traversal/TraversalStrategies.java | 2 + .../traversal/dsl/graph/GraphTraversal.java | 280 ++++++++++++- .../traversal/dsl/graph/GraphTraversalSource.java | 68 +++- .../ConstantGVTraversal.java} | 42 +- .../process/traversal/step/filter/HasStep.java | 3 +- .../process/traversal/step/filter/IsStep.java | 3 +- .../process/traversal/step/map/ConstantStep.java | 7 +- .../process/traversal/step/map/GraphStep.java | 4 +- .../process/traversal/step/map/MergeStep.java | 7 +- .../traversal/step/map/MergeVertexStep.java | 1 + .../traversal/step/sideEffect/AddPropertyStep.java | 4 +- .../traversal/step/sideEffect/InjectStep.java | 3 +- .../gremlin/process/traversal/step/util/GType.java | 95 +++++ .../process/traversal/step/util/GValue.java | 443 +++++++++++++++++++++ .../process/traversal/step/util/GValueStep.java | 93 +++++ .../process/traversal/step/util/HasContainer.java | 4 + .../step/util/structure/filter/HasStepGV.java | 74 ++++ .../util/structure/filter/HasStepStructure.java | 25 ++ .../structure/filter/IsStepGV.java} | 38 +- .../util/structure/filter/IsStepStructure.java | 25 ++ .../step/util/structure/map/AddEdgeStepGV.java | 59 +++ .../util/structure/map/AddEdgeStepStructure.java | 27 ++ .../structure/map/ConstantStepGV.java} | 37 +- .../util/structure/map/ConstantStepStructure.java | 23 ++ .../step/util/structure/map/GraphStepGV.java | 62 +++ .../util/structure/map/GraphStepStructure.java | 28 ++ .../step/util/structure/map/MergeEdgeStepGV.java | 94 +++++ .../structure/map/MergeElementStepStructure.java | 44 ++ .../step/util/structure/map/MergeVertexStepGV.java | 95 +++++ .../step/util/structure/map/VertexStepGV.java | 67 ++++ .../util/structure/map/VertexStepStructure.java | 30 ++ .../structure/sideEffect/AddPropertyStepGV.java | 54 +++ .../sideEffect/AddPropertyStepStructure.java | 28 ++ .../structure/sideEffect/InjectStepGV.java} | 38 +- .../structure/sideEffect/InjectStepStructure.java | 23 ++ .../strategy/decoration/ConnectiveStrategy.java | 1 - .../decoration/GValueReplacementStrategy.java | 97 +++++ .../process/traversal/util/TraversalHelper.java | 40 +- .../apache/tinkerpop/gremlin/util/ArrayUtil.java | 42 ++ 42 files changed, 1980 insertions(+), 145 deletions(-) diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/JavaTranslator.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/JavaTranslator.java index 1641d823bc..09e5818933 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/JavaTranslator.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/JavaTranslator.java @@ -28,6 +28,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; import org.apache.tinkerpop.gremlin.process.traversal.lambda.CardinalityValueTraversal; import org.apache.tinkerpop.gremlin.process.traversal.step.util.BulkSet; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.GValue; import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree; import org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy; import org.apache.tinkerpop.gremlin.process.traversal.util.BytecodeHelper; @@ -38,6 +39,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -336,6 +338,10 @@ public final class JavaTranslator<S extends TraversalSource, T extends Traversal private synchronized static void buildMethodCache(final Object delegate, final Map<String, List<ReflectedMethod>> methodCache) { if (methodCache.isEmpty()) { for (final Method method : delegate.getClass().getMethods()) { + // skip any methods that use GValue as it can't be used remotely or be translated + if (Arrays.stream(method.getParameters()).anyMatch(p -> p.getType().equals(GValue.class))) { + continue; + } final List<ReflectedMethod> list = methodCache.computeIfAbsent(method.getName(), k -> new ArrayList<>()); list.add(new ReflectedMethod(method)); } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/strategy/decoration/VertexProgramStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/strategy/decoration/VertexProgramStrategy.java index 7d920ab2f7..e8be03157f 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/strategy/decoration/VertexProgramStrategy.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/strategy/decoration/VertexProgramStrategy.java @@ -44,6 +44,7 @@ import org.apache.tinkerpop.gremlin.structure.Edge; import org.apache.tinkerpop.gremlin.structure.Vertex; import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; 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..a5e5551857 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.util.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,7 +296,12 @@ public class Bytecode implements Cloneable, Serializable { if (null != variable) return new Binding<>(variable, convertArgument(argument, false)); } - // + + // 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); if (argument instanceof Traversal) { // prevent use of "g" to spawn child traversals if (((Traversal) argument).asAdmin().getTraversalSource().isPresent()) diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategies.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategies.java index fc97198ac6..fe4ece057a 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategies.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategies.java @@ -23,6 +23,7 @@ import org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.finaliza import org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.optimization.GraphFilterStrategy; import org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.optimization.MessagePassingReductionStrategy; import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.ConnectiveStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.GValueReplacementStrategy; import org.apache.tinkerpop.gremlin.process.traversal.strategy.finalization.ProfileStrategy; import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.AdjacentToIncidentStrategy; import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.CountStrategy; @@ -217,6 +218,7 @@ public interface TraversalStrategies extends Serializable, Cloneable, Iterable<T static { final TraversalStrategies graphStrategies = new DefaultTraversalStrategies(); graphStrategies.addStrategies( + GValueReplacementStrategy.instance(), IdentityRemovalStrategy.instance(), ConnectiveStrategy.instance(), EarlyLimitStrategy.instance(), diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java index f01afa8197..a917dd76b7 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 @@ -18,6 +18,7 @@ */ package org.apache.tinkerpop.gremlin.process.traversal.dsl.graph; +import org.apache.commons.lang3.ArrayUtils; import org.apache.tinkerpop.gremlin.process.computer.VertexProgram; import org.apache.tinkerpop.gremlin.process.computer.clustering.connected.ConnectedComponentVertexProgram; import org.apache.tinkerpop.gremlin.process.computer.traversal.step.map.ConnectedComponentVertexProgramStep; @@ -39,6 +40,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.ColumnTraversal; +import org.apache.tinkerpop.gremlin.process.traversal.lambda.ConstantGVTraversal; import org.apache.tinkerpop.gremlin.process.traversal.lambda.ConstantTraversal; import org.apache.tinkerpop.gremlin.process.traversal.lambda.FunctionTraverser; import org.apache.tinkerpop.gremlin.process.traversal.lambda.LoopTraversal; @@ -126,6 +128,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.map.MaxLocalStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.MeanGlobalStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.MeanLocalStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.MergeEdgeStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.map.MergeStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.MergeVertexStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.MinGlobalStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.MinLocalStep; @@ -186,9 +189,20 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.AggregateL import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.SubgraphStep; import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.TraversalSideEffectStep; import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.TreeSideEffectStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.GValue; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.GValueStep; import org.apache.tinkerpop.gremlin.process.traversal.step.util.HasContainer; import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree; import org.apache.tinkerpop.gremlin.process.traversal.step.util.WithOptions; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.structure.filter.IsStepGV; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.structure.map.ConstantStepGV; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.structure.map.GraphStepGV; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.structure.map.MergeEdgeStepGV; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.structure.map.MergeElementStepStructure; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.structure.map.MergeVertexStepGV; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.structure.map.VertexStepGV; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.structure.sideEffect.AddPropertyStepGV; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.structure.sideEffect.InjectStepGV; import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.TraverserSet; import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper; import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalMetrics; @@ -231,6 +245,208 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> { public interface Admin<S, E> extends Traversal.Admin<S, E>, GraphTraversal<S, 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 GValue<String> edgeLabel, final GValue<String>... edgeLabels) { + this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.to, direction, edgeLabels); + return this.asAdmin().addStep(new VertexStepGV<>(this.asAdmin(), Vertex.class, direction, ArrayUtils.addFirst(edgeLabels, edgeLabel))); + } + + /** + * 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 GValue<String> edgeLabel, final GValue<String>... edgeLabels) { + this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.out, edgeLabels); + return this.asAdmin().addStep(new VertexStepGV<>(this.asAdmin(), Vertex.class, Direction.OUT, ArrayUtils.addFirst(edgeLabels, edgeLabel))); + } + + /** + * 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 GValue<String> edgeLabel, final GValue<String>... edgeLabels) { + this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.in, edgeLabels); + return this.asAdmin().addStep(new VertexStepGV<>(this.asAdmin(), Vertex.class, Direction.IN, ArrayUtils.addFirst(edgeLabels, edgeLabel))); + } + + /** + * 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 GValue<String> edgeLabel, final GValue<String>... edgeLabels) { + this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.both, edgeLabels); + return this.asAdmin().addStep(new VertexStepGV<>(this.asAdmin(), Vertex.class, Direction.BOTH, ArrayUtils.addFirst(edgeLabels, edgeLabel))); + } + + /** + * 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 GValue<String> edgeLabel, final GValue<String>... edgeLabels) { + this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.toE, direction, edgeLabels); + return this.asAdmin().addStep(new VertexStepGV<>(this.asAdmin(), Edge.class, direction, ArrayUtils.addFirst(edgeLabels, edgeLabel))); + } + + /** + * 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 GValue<String> edgeLabel, final GValue<String>... edgeLabels) { + this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.outE, edgeLabels); + return this.asAdmin().addStep(new VertexStepGV<>(this.asAdmin(), Edge.class, Direction.OUT, ArrayUtils.addFirst(edgeLabels, edgeLabel))); + } + + /** + * 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 GValue<String> edgeLabel, final GValue<String>... edgeLabels) { + this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.inE, edgeLabels); + return this.asAdmin().addStep(new VertexStepGV<>(this.asAdmin(), Edge.class, Direction.IN, ArrayUtils.addFirst(edgeLabels, edgeLabel))); + } + + /** + * 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 GValue<String> edgeLabel, final GValue<String>... edgeLabels) { + this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.bothE, edgeLabels); + return this.asAdmin().addStep(new VertexStepGV<>(this.asAdmin(), Edge.class, Direction.BOTH, ArrayUtils.addFirst(edgeLabels, edgeLabel))); + } + + /** + * 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.6.0 + */ + 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 MergeEdgeStepGV<S> step = new MergeEdgeStepGV(this.asAdmin(), false, searchCreate); + return this.asAdmin().addStep(step); + } + + /** + * 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.6.0 + */ + public default GraphTraversal<S, Vertex> mergeV(final GValue<Map<Object, Object>> searchCreate) { + this.asAdmin().getBytecode().addStep(GraphTraversal.Symbols.mergeV, searchCreate); + final MergeVertexStepGV step = new MergeVertexStepGV(this.asAdmin(), false, searchCreate); + return this.asAdmin().addStep(step); + } + + /** + * 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.6.0 + */ + 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); + + // handle case where the MergeStep was added instead of a MergeVertexStepGV. in this case, mergeV/E was + // called such that it did not have a GValue so a standard step was added. leave it ot hte + // GValueReplacementStrategy to resolve this + final Step endStep = this.asAdmin().getEndStep(); + if (endStep instanceof MergeStep) { + this.asAdmin().addStep(new GValueStep.MergeOptionStep(this.asAdmin(), (Merge) token, GValue.of(m.getName(), m))); + } else { + ((TraversalOptionParent<M, E, E2>) this.asAdmin().getEndStep()).addChildOption(token, (Traversal.Admin<E, E2>) new ConstantGVTraversal<>(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.0 + */ + 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); + // do explicit cardinality for every single pair in the map + final Map<Object,Object> m1 = m.get(); + for (Object k : m1.keySet()) { + final Object o = m1.get(k); + if (!(o instanceof CardinalityValueTraversal)) + m1.put(k, new CardinalityValueTraversal(cardinality, o)); + } + + // handle case where the MergeStep was added instead of a MergeVertexStepGV. in this case, mergeV/E was + // called such that it did not have a GValue so a standard step was added. leave it ot hte + // GValueReplacementStrategy to resolve this + final Step endStep = this.asAdmin().getEndStep(); + if (endStep instanceof MergeStep) { + this.asAdmin().addStep(new GValueStep.MergeOptionStep(this.asAdmin(), merge, GValue.of(m.getName(), m1))); + } else { + ((TraversalOptionParent<M, E, E2>) this.asAdmin().getEndStep()).addChildOption((M) merge, (Traversal.Admin<E, E2>) new ConstantGVTraversal<>(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); @@ -352,7 +568,12 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> { */ public default <E2> GraphTraversal<S, E2> constant(final E2 e) { this.asAdmin().getBytecode().addStep(Symbols.constant, e); - return this.asAdmin().addStep(new ConstantStep<E, E2>(this.asAdmin(), e)); + + if (e instanceof GValue) { + return this.asAdmin().addStep(new ConstantStepGV<>(this.asAdmin(), (GValue) e)); + } else { + return this.asAdmin().addStep(new ConstantStep<E, E2>(this.asAdmin(), e)); + } } /** @@ -367,7 +588,12 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> { // a single null is [null] final Object[] ids = null == vertexIdsOrElements ? new Object[] { null } : vertexIdsOrElements; this.asAdmin().getBytecode().addStep(Symbols.V, ids); - return this.asAdmin().addStep(new GraphStep<>(this.asAdmin(), Vertex.class, false, ids)); + + if (GValue.hasGValue(ids)) { + return this.asAdmin().addStep(new GraphStepGV<>(this.asAdmin(), Vertex.class, false, GValue.promoteGValues(ids))); + } else { + return this.asAdmin().addStep(new GraphStep<>(this.asAdmin(), Vertex.class, false, ids)); + } } /** @@ -382,7 +608,12 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> { // a single null is [null] final Object[] ids = null == edgeIdsOrElements ? new Object[] { null } : edgeIdsOrElements; this.asAdmin().getBytecode().addStep(Symbols.E, ids); - return this.asAdmin().addStep(new GraphStep<>(this.asAdmin(), Edge.class, false, ids)); + + if (GValue.hasGValue(ids)) { + return this.asAdmin().addStep(new GraphStepGV<>(this.asAdmin(), Edge.class, false, GValue.promoteGValues(ids))); + } else { + return this.asAdmin().addStep(new GraphStep<>(this.asAdmin(), Edge.class, false, ids)); + } } /** @@ -772,11 +1003,8 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> { * @since 3.2.0-incubating */ public default <E2> GraphTraversal<S, Map<String, E2>> project(final String projectKey, final String... otherProjectKeys) { - final String[] projectKeys = new String[otherProjectKeys.length + 1]; - projectKeys[0] = projectKey; - System.arraycopy(otherProjectKeys, 0, projectKeys, 1, otherProjectKeys.length); this.asAdmin().getBytecode().addStep(Symbols.project, projectKey, otherProjectKeys); - return this.asAdmin().addStep(new ProjectStep<>(this.asAdmin(), projectKeys)); + return this.asAdmin().addStep(new ProjectStep<>(this.asAdmin(), ArrayUtils.addFirst(otherProjectKeys, projectKey))); } /** @@ -792,12 +1020,10 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> { * @since 3.0.0-incubating */ public default <E2> GraphTraversal<S, Map<String, E2>> select(final Pop pop, final String selectKey1, final String selectKey2, String... otherSelectKeys) { - final String[] selectKeys = new String[otherSelectKeys.length + 2]; - selectKeys[0] = selectKey1; - selectKeys[1] = selectKey2; - System.arraycopy(otherSelectKeys, 0, selectKeys, 2, otherSelectKeys.length); + String[] args = ArrayUtils.addFirst(otherSelectKeys, selectKey2); + args = ArrayUtils.addFirst(args, selectKey1); this.asAdmin().getBytecode().addStep(Symbols.select, pop, selectKey1, selectKey2, otherSelectKeys); - return this.asAdmin().addStep(new SelectStep<>(this.asAdmin(), pop, selectKeys)); + return this.asAdmin().addStep(new SelectStep<>(this.asAdmin(), pop, args)); } /** @@ -812,12 +1038,10 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> { * @since 3.0.0-incubating */ public default <E2> GraphTraversal<S, Map<String, E2>> select(final String selectKey1, final String selectKey2, String... otherSelectKeys) { - final String[] selectKeys = new String[otherSelectKeys.length + 2]; - selectKeys[0] = selectKey1; - selectKeys[1] = selectKey2; - System.arraycopy(otherSelectKeys, 0, selectKeys, 2, otherSelectKeys.length); + String[] args = ArrayUtils.addFirst(otherSelectKeys, selectKey2); + args = ArrayUtils.addFirst(args, selectKey1); this.asAdmin().getBytecode().addStep(Symbols.select, selectKey1, selectKey2, otherSelectKeys); - return this.asAdmin().addStep(new SelectStep<>(this.asAdmin(), Pop.last, selectKeys)); + return this.asAdmin().addStep(new SelectStep<>(this.asAdmin(), Pop.last, args)); } /** @@ -2051,7 +2275,10 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> { // a single null is [null] final E[] s = null == injections ? (E[]) new Object[] { null } : injections; this.asAdmin().getBytecode().addStep(Symbols.inject, s); - return this.asAdmin().addStep(new InjectStep<>(this.asAdmin(), s)); + if (GValue.hasGValue(s)) + return this.asAdmin().addStep(new InjectStepGV<>(this.asAdmin(), GValue.promoteGValues(s))); + else + return this.asAdmin().addStep(new InjectStep<>(this.asAdmin(), s)); } /** @@ -2539,7 +2766,10 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> { */ public default GraphTraversal<S, E> is(final P<E> predicate) { this.asAdmin().getBytecode().addStep(Symbols.is, predicate); - return this.asAdmin().addStep(new IsStep<>(this.asAdmin(), predicate)); + if (GValue.hasGValueInP(predicate)) + return this.asAdmin().addStep(new IsStepGV<>(this.asAdmin(), predicate)); + else + return this.asAdmin().addStep(new IsStep<>(this.asAdmin(), predicate)); } /** @@ -3123,9 +3353,15 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> { (key instanceof T || (key instanceof String && null == cardinality) || key instanceof Traversal))) { ((Mutating) endStep).configure(key, value); } else { - final AddPropertyStep<Element> addPropertyStep = new AddPropertyStep<>(this.asAdmin(), cardinality, key, value); - this.asAdmin().addStep(addPropertyStep); - addPropertyStep.configure(keyValues); + if (value instanceof GValue || GValue.hasGValue(keyValues)) { + final AddPropertyStepGV addPropertyStep = new AddPropertyStepGV(this.asAdmin(), cardinality, key, GValue.of(value)); + this.asAdmin().addStep(addPropertyStep); + addPropertyStep.configure(keyValues); + } else { + final AddPropertyStep<Element> addPropertyStep = new AddPropertyStep<>(this.asAdmin(), cardinality, key, value); + this.asAdmin().addStep(addPropertyStep); + addPropertyStep.configure(keyValues); + } } return this; } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.java index dc15306fce..4a4ecfc115 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.java @@ -36,6 +36,11 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.map.MergeEdgeStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.MergeVertexStep; import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.IoStep; import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.InjectStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.GValue; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.structure.map.GraphStepGV; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.structure.map.MergeEdgeStepGV; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.structure.map.MergeVertexStepGV; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.structure.sideEffect.InjectStepGV; import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.RequirementsStrategy; import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement; import org.apache.tinkerpop.gremlin.structure.Direction; @@ -68,6 +73,7 @@ public class GraphTraversalSource implements TraversalSource { protected final Graph graph; protected TraversalStrategies strategies; protected Bytecode bytecode = new Bytecode(); + protected Admin admin; //////////////// @@ -100,6 +106,12 @@ public class GraphTraversalSource implements TraversalSource { this.strategies.addStrategies(new RemoteStrategy(connection)); } + public GraphTraversalSource.Admin asAdmin() { + if (null == this.admin) + this.admin = new Admin(); + return this.admin; + } + @Override public Optional<Class<?>> getAnonymousTraversalClass() { return Optional.of(__.class); @@ -447,7 +459,12 @@ public class GraphTraversalSource implements TraversalSource { final GraphTraversalSource clone = this.clone(); clone.bytecode.addStep(GraphTraversal.Symbols.inject, s); final GraphTraversal.Admin<S, S> traversal = new DefaultGraphTraversal<>(clone); - return traversal.addStep(new InjectStep<S>(traversal, s)); + + if (GValue.hasGValue(s)) { + return traversal.addStep(new InjectStepGV<S>(traversal, GValue.promoteGValues(s))); + } else { + return traversal.addStep(new InjectStep<>(traversal, s)); + } } /** @@ -462,7 +479,12 @@ public class GraphTraversalSource implements TraversalSource { final GraphTraversalSource clone = this.clone(); clone.bytecode.addStep(GraphTraversal.Symbols.V, ids); final GraphTraversal.Admin<Vertex, Vertex> traversal = new DefaultGraphTraversal<>(clone); - return traversal.addStep(new GraphStep<>(traversal, Vertex.class, true, ids)); + + if (GValue.hasGValue(ids)) { + return traversal.addStep(new GraphStepGV<>(traversal, Vertex.class, true, GValue.promoteGValues(ids))); + } else { + return traversal.addStep(new GraphStep<>(traversal, Vertex.class, true, ids)); + } } /** @@ -477,7 +499,12 @@ public class GraphTraversalSource implements TraversalSource { final GraphTraversalSource clone = this.clone(); clone.bytecode.addStep(GraphTraversal.Symbols.E, ids); final GraphTraversal.Admin<Edge, Edge> traversal = new DefaultGraphTraversal<>(clone); - return traversal.addStep(new GraphStep<>(traversal, Edge.class, true, ids)); + + if (GValue.hasGValue(ids)) { + return traversal.addStep(new GraphStepGV<>(traversal, Edge.class, true, GValue.promoteGValues(ids))); + } else { + return traversal.addStep(new GraphStep<>(traversal, Edge.class, true, ids)); + } } /** @@ -624,4 +651,39 @@ public class GraphTraversalSource implements TraversalSource { return StringFactory.traversalSourceString(this); } + /** + * This class masks spawn steps that are more reserved for advanced usage. + */ + public class Admin { + /** + * Spawns a {@link GraphTraversal} by doing 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 4.0.0 + */ + public GraphTraversal<Vertex, Vertex> mergeV(final GValue<Map<Object, Object>> searchCreate) { + final GraphTraversalSource clone = GraphTraversalSource.this.clone(); + clone.bytecode.addStep(GraphTraversal.Symbols.mergeV, searchCreate); + final GraphTraversal.Admin<Vertex, Vertex> traversal = new DefaultGraphTraversal<>(clone); + return traversal.addStep(new MergeVertexStepGV(traversal, true, searchCreate)); + } + + /** + * 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 4.0.0 + */ + public GraphTraversal<Edge, Edge> mergeE(final GValue<Map<?, Object>> searchCreate) { + final GraphTraversalSource clone = GraphTraversalSource.this.clone(); + clone.bytecode.addStep(GraphTraversal.Symbols.mergeE, searchCreate); + final GraphTraversal.Admin<Edge, Edge> traversal = new DefaultGraphTraversal<>(clone); + return traversal.addStep(new MergeEdgeStepGV(traversal, true, searchCreate)); + } + } + } 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/lambda/ConstantGVTraversal.java similarity index 51% copy from gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConstantStep.java copy to gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/lambda/ConstantGVTraversal.java index 9434cc3b47..03492a0e7c 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/lambda/ConstantGVTraversal.java @@ -16,42 +16,48 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.tinkerpop.gremlin.process.traversal.step.map; +package org.apache.tinkerpop.gremlin.process.traversal.lambda; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; -import org.apache.tinkerpop.gremlin.process.traversal.Traverser; -import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement; -import org.apache.tinkerpop.gremlin.structure.util.StringFactory; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.GValue; -import java.util.Collections; import java.util.Objects; -import java.util.Set; -public class ConstantStep<S, E> extends ScalarMapStep<S, E> { +/** + * A {@link Traversal} that always returns a constant value. + * + * @author Marko A. Rodriguez (http://markorodriguez.com) + */ +public final class ConstantGVTraversal<S, E> extends AbstractLambdaTraversal<S, E> { - private final E constant; + private final GValue<E> end; - public ConstantStep(final Traversal.Admin traversal, final E constant) { - super(traversal); - this.constant = constant; + public ConstantGVTraversal(final GValue<E> end) { + this.end = end; } - public E getConstant() { - return this.constant; + public GValue<E> getEnd() { + return end; } @Override - protected E map(final Traverser.Admin<S> traverser) { - return this.constant; + public E next() { + throw new UnsupportedOperationException("Constant traversals with a GValue are not meant to be executed"); } @Override public String toString() { - return StringFactory.stepString(this, this.constant); + return "(" + Objects.toString(this.end) + ")"; } @Override public int hashCode() { - return super.hashCode() ^ Objects.hashCode(this.constant); + return this.getClass().hashCode() ^ Objects.hashCode(this.end); + } + + @Override + public boolean equals(final Object other) { + return other instanceof ConstantGVTraversal + && Objects.equals(((ConstantGVTraversal) other).end, this.end); } -} +} \ No newline at end of file diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/HasStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/HasStep.java index 6048ba78fe..e25bc592ce 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/HasStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/HasStep.java @@ -24,6 +24,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.Configuring; import org.apache.tinkerpop.gremlin.process.traversal.step.HasContainerHolder; import org.apache.tinkerpop.gremlin.process.traversal.step.util.HasContainer; import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.structure.filter.HasStepStructure; import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement; import org.apache.tinkerpop.gremlin.structure.Element; import org.apache.tinkerpop.gremlin.structure.Property; @@ -38,7 +39,7 @@ import java.util.Set; /** * @author Marko A. Rodriguez (http://markorodriguez.com) */ -public class HasStep<S extends Element> extends FilterStep<S> implements HasContainerHolder, Configuring { +public class HasStep<S extends Element> extends FilterStep<S> implements HasContainerHolder, Configuring, HasStepStructure { private final Parameters parameters = new Parameters(); private List<HasContainer> hasContainers; diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/IsStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/IsStep.java index a2889864a8..abead84787 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/IsStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/IsStep.java @@ -21,6 +21,7 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.filter; import org.apache.tinkerpop.gremlin.process.traversal.P; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.process.traversal.Traverser; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.structure.filter.IsStepStructure; import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement; import org.apache.tinkerpop.gremlin.structure.util.StringFactory; @@ -31,7 +32,7 @@ import java.util.Set; * @author Daniel Kuppitz (http://gremlin.guru) * @author Marko A. Rodriguez (http://markorodriguez.com) */ -public final class IsStep<S> extends FilterStep<S> { +public final class IsStep<S> extends FilterStep<S> implements IsStepStructure<S> { private P<S> predicate; 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..bbc5abc731 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,14 +20,12 @@ 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.traverser.TraverserRequirement; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.structure.map.ConstantStepStructure; import org.apache.tinkerpop.gremlin.structure.util.StringFactory; -import java.util.Collections; import java.util.Objects; -import java.util.Set; -public class ConstantStep<S, E> extends ScalarMapStep<S, E> { +public class ConstantStep<S, E> extends ScalarMapStep<S, E> implements ConstantStepStructure<E> { private final E constant; @@ -54,4 +52,5 @@ public class ConstantStep<S, E> extends ScalarMapStep<S, E> { public int hashCode() { return super.hashCode() ^ Objects.hashCode(this.constant); } + } 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..53cf2cc12f 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 @@ -29,6 +29,7 @@ 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; import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.structure.map.GraphStepStructure; import org.apache.tinkerpop.gremlin.process.traversal.util.FastNoSuchElementException; import org.apache.tinkerpop.gremlin.structure.Edge; import org.apache.tinkerpop.gremlin.structure.Element; @@ -49,7 +50,8 @@ import java.util.function.Supplier; * @author Marko A. Rodriguez (http://markorodriguez.com) * @author Pieter Martin */ -public class GraphStep<S, E extends Element> extends AbstractStep<S, E> implements GraphComputing, AutoCloseable, Configuring { +public class GraphStep<S, E extends Element> extends AbstractStep<S, E> implements GraphComputing, AutoCloseable, + Configuring, GraphStepStructure<E, Object> { protected Parameters parameters = new Parameters(); protected final Class<E> returnClass; diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeStep.java index 9b0f12d1d6..c87f6576be 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MergeStep.java @@ -41,6 +41,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters; import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.CallbackRegistry; import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.Event; import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.ListCallbackRegistry; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.structure.map.MergeElementStepStructure; import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.PartitionStrategy; import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement; import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper; @@ -58,13 +59,13 @@ import org.apache.tinkerpop.gremlin.structure.util.StringFactory; * Abstract base class for the {@code mergeV/E()} implementations. */ public abstract class MergeStep<S, E, C> extends FlatMapStep<S, E> - implements Writing<Event>, Deleting<Event>, TraversalOptionParent<Merge, S, C> { + implements Writing<Event>, Deleting<Event>, TraversalOptionParent<Merge, S, C>, MergeElementStepStructure<S> { protected final boolean isStart; protected boolean first = true; protected Traversal.Admin<S, Map> mergeTraversal; protected Traversal.Admin<S, Map> onCreateTraversal = null; - protected Traversal.Admin<S, Map<String, ?>> onMatchTraversal = null; + protected Traversal.Admin<S, Map> onMatchTraversal = null; protected CallbackRegistry<Event> callbackRegistry; @@ -116,7 +117,7 @@ public abstract class MergeStep<S, E, C> extends FlatMapStep<S, E> * Gets the traversal that will be used to provide the {@code Map} that will be used to modify elements that * match the search criteria of {@link #getMergeTraversal()}. */ - public Traversal.Admin<S, Map<String, ?>> getOnMatchTraversal() { + public Traversal.Admin<S, Map> getOnMatchTraversal() { return onMatchTraversal; } 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 11a9fc2ca4..3f59784541 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 @@ -31,6 +31,7 @@ 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.util.event.EventUtil; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.structure.map.MergeElementStepStructure; import org.apache.tinkerpop.gremlin.structure.Graph; import org.apache.tinkerpop.gremlin.structure.T; import org.apache.tinkerpop.gremlin.structure.Vertex; diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java index 7d155a40bd..c7fe4d831c 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java @@ -26,6 +26,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent; import org.apache.tinkerpop.gremlin.process.traversal.step.Writing; import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters; import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.*; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.structure.sideEffect.AddPropertyStepStructure; import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.EventStrategy; import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement; import org.apache.tinkerpop.gremlin.structure.Edge; @@ -48,7 +49,8 @@ import java.util.Set; * @author Marko A. Rodriguez (http://markorodriguez.com) */ public class AddPropertyStep<S extends Element> extends SideEffectStep<S> - implements Writing<Event.ElementPropertyChangedEvent>, Deleting<Event.ElementPropertyChangedEvent>, TraversalParent, Scoping { + implements Writing<Event.ElementPropertyChangedEvent>, Deleting<Event.ElementPropertyChangedEvent>, + TraversalParent, Scoping, AddPropertyStepStructure { private Parameters parameters = new Parameters(); private final VertexProperty.Cardinality cardinality; diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/InjectStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/InjectStep.java index 0502f18541..4ce3137a42 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/InjectStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/InjectStep.java @@ -19,12 +19,13 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.structure.sideEffect.InjectStepStructure; import org.apache.tinkerpop.gremlin.util.iterator.ArrayIterator; /** * @author Marko A. Rodriguez (http://markorodriguez.com) */ -public final class InjectStep<S> extends StartStep<S> { +public final class InjectStep<S> extends StartStep<S> implements InjectStepStructure<S> { private final S[] injections; diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/GType.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/GType.java new file mode 100644 index 0000000000..534c89baac --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/GType.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.process.traversal.step.util; + +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 {@code true} if the type is a number. + */ + public boolean isNumeric() { + return this == INTEGER || this == DOUBLE || this == LONG || this == BIG_INTEGER || this == BIG_DECIMAL; + } + + /** + * Returns {@code true} if the type is a collection.v + */ + public boolean isCollection() { + return this == LIST || this == SET; + } + + /** + * Returns {@code true} if the type is an element. + */ + public boolean isElement() { + return this == VERTEX || this == EDGE; + } + + /** + * 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; + } +} \ No newline at end of file diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/GValue.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/GValue.java new file mode 100644 index 0000000000..47b3b0c287 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/GValue.java @@ -0,0 +1,443 @@ +/* + * 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.util; + +import org.apache.tinkerpop.gremlin.process.traversal.P; +import org.apache.tinkerpop.gremlin.process.traversal.Path; +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.process.traversal.util.ConnectiveP; +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.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Objects; +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 String getName() { + return 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; + } + + /** + * Determines if the value held is of a {@code null} value. + */ + public boolean isNull() { + return this.value == null; + } + + /** + * 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; + final 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) { + if (value instanceof GValue) + return (GValue<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 <T> GValue<List<T>> ofList(final List<T> value) { + return new GValue<>(GType.LIST, value); + } + + /** + * Create a new {@code GValue} for a list value with a specified name. + */ + public static <T> GValue<List<T>> ofList(final String name, final List<T> 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); + } + + public static <T> GValue[] promoteGValues(final Object... objects) { + return Arrays.stream(objects).map(o -> { + if (o instanceof GValue) + return (GValue<T>) o; + else + return GValue.of(o); + }).toArray(GValue[]::new); + } + + /** + * The objects array is a series of key-value pairs. Construct a new object array that ensures that the values + * become {@link GValue} instances if they are not already. + */ + public static Object[] promoteGValuesInKeyValues(final Object... objects) { + final Object[] keyValues = new Object[objects.length]; + for (int i = 0; i < objects.length; i++) { + final Object o = objects[i]; + + // even are keys, odd are values + if (i % 2 == 1) { + keyValues[i] = o instanceof GValue ? o : GValue.of(o); + } else { + keyValues[i] = o; + } + } + return keyValues; + } + + /** + * Resolve all {@link GValue} instance in the array to their value and return a new object array. + */ + public static Object[] resolveGValues(final Object... objects) { + return Arrays.stream(objects).map(o -> { + if (o instanceof GValue) + return ((GValue) o).get(); + else + return o; + }).toArray(); + } + + /** + * Take an instance of {@link P} and if it is a {@link GValue}, unwrap it to its value and reassign it back to + * the {@link P} instance. In addition, Detect if the {@link P} instance is a {@link ConnectiveP}, then iterate + * the list of predicates and convert them to {@link GValue} if they are instances of {@link GValue}. This must be + * done recursively as the {@link ConnectiveP} can contain other {@link ConnectiveP} instances. + */ + public static P resolveGValuesInP(final P p) { + final Object o = p.getValue(); + if (o instanceof GValue) { + if (o instanceof GValue) { + p.setValue(((GValue) o).get()); + } + } else if (o instanceof List) { + final List l = (List) o; + final List ln = new ArrayList<>(); + l.forEach(value -> ln.add(value instanceof GValue ? ((GValue) value).get() : value) ); + p.setValue(ln); + } else if (p instanceof ConnectiveP) { + final ConnectiveP connectiveP = (ConnectiveP) p; + connectiveP.getPredicates().forEach(pred -> resolveGValuesInP((P) pred)); + } + return p; + } + + public static HasContainer resolveGValuesInHasContainer(final HasContainer hasContainer) { + final P p = hasContainer.getPredicate(); + hasContainer.setPredicate(resolveGValuesInP(p)); + return hasContainer; + } + + public static HasContainer[] resolveGValuesInHasContainers(final HasContainer... hasContainers) { + for (int i = 0; i < hasContainers.length; i++) { + hasContainers[i] = resolveGValuesInHasContainer(hasContainers[i]); + } + return hasContainers; + } + + /** + * Detects if a value of {@link P} has a {@link GValue} and if so, return {@link true} immediately. + */ + public static boolean hasGValueInP(final P p) { + final Object o = p.getValue(); + if (o instanceof GValue) + return true; + else if (o instanceof List) { + final List l = (List) o; + for (final Object value : l) { + if (value instanceof GValue) + return true; + } + } else if (p instanceof ConnectiveP) { + final ConnectiveP<?> connectiveP = (ConnectiveP) p; + for (P pred : connectiveP.getPredicates()) { + if (hasGValueInP(pred)) + return true; + } + } + return false; + } + + public static boolean hasGValue(final Object... objects) { + for (Object o : objects) { + if (o instanceof GValue) + return true; + } + return false; + } +} \ No newline at end of file diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/GValueStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/GValueStep.java new file mode 100644 index 0000000000..5dca16e1c1 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/GValueStep.java @@ -0,0 +1,93 @@ +/* + * 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.util; + +import org.apache.tinkerpop.gremlin.process.traversal.Merge; +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 java.util.Map; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.Set; + +public class GValueStep<C extends Step, S, E> extends AbstractStep<S,E> { + + protected final C concreteStep; + + public GValueStep(final Traversal.Admin traversal, final C concreteStep) { + super(traversal); + this.concreteStep = concreteStep; + } + + public C getConcreteStep() { + return concreteStep; + } + + @Override + public void addLabel(String label) { + this.concreteStep.addLabel(label); + } + + @Override + public void removeLabel(String label) { + this.concreteStep.removeLabel(label); + } + + @Override + public void clearLabels() { + this.concreteStep.clearLabels(); + } + + @Override + public Set<String> getLabels() { + return this.concreteStep.getLabels(); + } + + @Override + protected Traverser.Admin<E> processNextStart() throws NoSuchElementException { + throw new UnsupportedOperationException("GValueStep is a placeholder step and should not be executed"); + } + + @Override + public String toString() { + return Objects.toString(concreteStep); + } + + public static class MergeOptionStep<S,E> extends GValueStep<EmptyStep, S, E> { + private final Merge merge; + private final GValue gvalue; + + public MergeOptionStep(final Traversal.Admin traversal, final Merge merge, final GValue<Map<Object, Object>> gvalue) { + super(traversal, EmptyStep.instance()); + this.merge = merge; + this.gvalue = gvalue; + } + + public Merge getMerge() { + return merge; + } + + public GValue getGvalue() { + return gvalue; + } + } + +} diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/HasContainer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/HasContainer.java index c20c68fde0..99995d42dc 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/HasContainer.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/HasContainer.java @@ -134,6 +134,10 @@ public class HasContainer implements Serializable, Cloneable, Predicate<Element> return this.predicate; } + public void setPredicate(final P predicate) { + this.predicate = predicate; + } + public final BiPredicate<?, ?> getBiPredicate() { return this.predicate.getBiPredicate(); } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/filter/HasStepGV.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/filter/HasStepGV.java new file mode 100644 index 0000000000..a50d966b2d --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/filter/HasStepGV.java @@ -0,0 +1,74 @@ +/* + * 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.util.structure.filter; + +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.process.traversal.step.filter.HasStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.GValue; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.GValueStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.HasContainer; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class HasStepGV<S> extends GValueStep<HasStep, S, S> implements HasStepStructure { + private List<HasContainer> hasContainers; + private final Parameters parameters = new Parameters(); + + public HasStepGV(final Traversal.Admin traversal, final HasContainer... hasContainers) { + super(traversal, new HasStep(traversal, GValue.resolveGValuesInHasContainers(hasContainers))); + this.hasContainers = new ArrayList<>(); + Collections.addAll(this.hasContainers, hasContainers); + } + + @Override + public void configure(final Object... keyValues) { + // all GValues locally stored in the parameters, but resolve them for the concrete step + this.parameters.set(null, GValue.promoteGValuesInKeyValues(keyValues)); + this.concreteStep.configure(GValue.resolveGValues(keyValues)); + } + + @Override + public Parameters getParameters() { + return concreteStep.getParameters(); + } + + @Override + public List<HasContainer> getHasContainers() { + return Collections.unmodifiableList(this.hasContainers); + } + + @Override + public void removeHasContainer(final HasContainer hasContainer) { + // not sure what this means in this context since this object will be different from the one in the concrete + // step. strategies are the only things that remove HasContainers so since the GValueSteps will all be + // removed by the time they execute perhaps we don't need to implement this. + throw new UnsupportedOperationException("Cannot remove HasContainers from HasStepGV"); + } + + @Override + public void addHasContainer(final HasContainer hasContainer) { + assert(GValue.hasGValueInP(hasContainer.getPredicate())); + this.hasContainers.add(hasContainer); + this.concreteStep.addHasContainer(GValue.resolveGValuesInHasContainer(hasContainer)); + } + +} diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/filter/HasStepStructure.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/filter/HasStepStructure.java new file mode 100644 index 0000000000..397dda4710 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/filter/HasStepStructure.java @@ -0,0 +1,25 @@ +/* + * 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.util.structure.filter; + +import org.apache.tinkerpop.gremlin.process.traversal.step.Configuring; +import org.apache.tinkerpop.gremlin.process.traversal.step.HasContainerHolder; + +public interface HasStepStructure extends HasContainerHolder, Configuring { +} diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/InjectStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/filter/IsStepGV.java similarity index 51% copy from gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/InjectStep.java copy to gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/filter/IsStepGV.java index 0502f18541..a7aa23a6e7 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/InjectStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/filter/IsStepGV.java @@ -16,39 +16,29 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect; +package org.apache.tinkerpop.gremlin.process.traversal.step.util.structure.filter; +import org.apache.tinkerpop.gremlin.process.traversal.P; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; -import org.apache.tinkerpop.gremlin.util.iterator.ArrayIterator; +import org.apache.tinkerpop.gremlin.process.traversal.step.filter.IsStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.GValue; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.GValueStep; -/** - * @author Marko A. Rodriguez (http://markorodriguez.com) - */ -public final class InjectStep<S> extends StartStep<S> { - - private final S[] injections; +public class IsStepGV<S> extends GValueStep<IsStep, S, S> implements IsStepStructure<S> { + private P<S> predicate; - @SafeVarargs - public InjectStep(final Traversal.Admin traversal, final S... injections) { - super(traversal); - this.injections = injections; - this.start = new ArrayIterator<>(this.injections); + public IsStepGV(final Traversal.Admin traversal, final P<S> predicate) { + super(traversal, new IsStep(traversal, GValue.resolveGValuesInP(predicate))); + this.predicate = predicate; } @Override - public InjectStep<S> clone() { - final InjectStep<S> clone = (InjectStep<S>) super.clone(); - clone.start = new ArrayIterator<>(clone.injections); - return clone; + public P<S> getPredicate() { + return predicate; } @Override - public void reset() { - super.reset(); - this.start = new ArrayIterator<>(this.injections); - } - - public S[] getInjections() { - return this.injections; + public IsStep<S> getConcreteStep() { + return concreteStep; } } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/filter/IsStepStructure.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/filter/IsStepStructure.java new file mode 100644 index 0000000000..0f40305d48 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/filter/IsStepStructure.java @@ -0,0 +1,25 @@ +/* + * 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.util.structure.filter; + +import org.apache.tinkerpop.gremlin.process.traversal.P; + +public interface IsStepStructure<S> { + public P<S> getPredicate(); +} diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/map/AddEdgeStepGV.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/map/AddEdgeStepGV.java new file mode 100644 index 0000000000..2f896a7e15 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/map/AddEdgeStepGV.java @@ -0,0 +1,59 @@ +/* + * 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.util.structure.map; + +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddEdgeStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.GValue; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.GValueStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters; +import org.apache.tinkerpop.gremlin.structure.T; + +public class AddEdgeStepGV<S> extends GValueStep<AddEdgeStep, S, S> implements AddEdgeStepStructure { + private final Parameters parameters = new Parameters(); + + public AddEdgeStepGV(final Traversal.Admin traversal, final GValue<String> edgeLabel) { + super(traversal, new AddEdgeStep(traversal, edgeLabel.get())); + this.parameters.set(this, T.label, edgeLabel); + } + + @Override + public void configure(final Object... keyValues) { + // all GValues locally stored in the parameters, but resolve them for the concrete step + this.parameters.set(this, GValue.promoteGValuesInKeyValues(keyValues)); + this.concreteStep.configure(GValue.resolveGValues(keyValues)); + } + + @Override + public Parameters getParameters() { + return this.parameters; + } + + @Override + public void addTo(final Traversal.Admin<?, ?> toObject) { + // pass through to the concrete step because it can't a be a GValue and it gets called from GraphTraversal + this.concreteStep.addTo(toObject); + } + + @Override + public void addFrom(final Traversal.Admin<?, ?> fromObject) { + // pass through to the concrete step because it can't a be a GValue and it gets called from GraphTraversal + this.concreteStep.addFrom(fromObject); + } +} diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/map/AddEdgeStepStructure.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/map/AddEdgeStepStructure.java new file mode 100644 index 0000000000..fb0b0e9d6e --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/map/AddEdgeStepStructure.java @@ -0,0 +1,27 @@ +/* + * 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.util.structure.map; + +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.TraversalParent; + +public interface AddEdgeStepStructure extends Configuring, FromToModulating, TraversalParent { + +} diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/InjectStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/map/ConstantStepGV.java similarity index 50% copy from gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/InjectStep.java copy to gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/map/ConstantStepGV.java index 0502f18541..0b61bc81f3 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/InjectStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/map/ConstantStepGV.java @@ -16,39 +16,24 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect; +package org.apache.tinkerpop.gremlin.process.traversal.step.util.structure.map; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; -import org.apache.tinkerpop.gremlin.util.iterator.ArrayIterator; +import org.apache.tinkerpop.gremlin.process.traversal.step.map.ConstantStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.GValue; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.GValueStep; -/** - * @author Marko A. Rodriguez (http://markorodriguez.com) - */ -public final class InjectStep<S> extends StartStep<S> { - - private final S[] injections; +public class ConstantStepGV<S> extends GValueStep<ConstantStep, S, S> implements ConstantStepStructure<GValue<S>> { - @SafeVarargs - public InjectStep(final Traversal.Admin traversal, final S... injections) { - super(traversal); - this.injections = injections; - this.start = new ArrayIterator<>(this.injections); - } + private final GValue<S> constant; - @Override - public InjectStep<S> clone() { - final InjectStep<S> clone = (InjectStep<S>) super.clone(); - clone.start = new ArrayIterator<>(clone.injections); - return clone; + public ConstantStepGV(final Traversal.Admin traversal, final GValue<S> constant) { + super(traversal, new ConstantStep(traversal, constant.get())); + this.constant = constant; } @Override - public void reset() { - super.reset(); - this.start = new ArrayIterator<>(this.injections); - } - - public S[] getInjections() { - return this.injections; + public GValue<S> getConstant() { + return constant; } } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/map/ConstantStepStructure.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/map/ConstantStepStructure.java new file mode 100644 index 0000000000..4932752477 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/map/ConstantStepStructure.java @@ -0,0 +1,23 @@ +/* + * 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.util.structure.map; + +public interface ConstantStepStructure<E> { + public E getConstant(); +} diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/map/GraphStepGV.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/map/GraphStepGV.java new file mode 100644 index 0000000000..6f5cb1d31f --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/map/GraphStepGV.java @@ -0,0 +1,62 @@ +/* + * 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.util.structure.map; + +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.process.traversal.step.map.GraphStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.GValue; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.GValueStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters; + +import java.util.Arrays; + +public class GraphStepGV<S, E> extends GValueStep<GraphStep, E, E> implements GraphStepStructure<E, GValue<Object>> { + private final Parameters parameters = new Parameters(); + private final GValue<Object>[] ids; + private final Class<E> returnClass; + + public GraphStepGV(final Traversal.Admin traversal, final Class<E> returnClass, final boolean isStart, final GValue<Object>... ids) { + super(traversal, new GraphStep(traversal, returnClass, isStart, + Arrays.stream(ids).map(GValue::get).toArray(Object[]::new))); + this.returnClass = returnClass; + this.ids = ids; + } + + @Override + public void configure(final Object... keyValues) { + // all GValues locally stored in the parameters, but resolve them for the concrete step + this.parameters.set(null, GValue.promoteGValuesInKeyValues(keyValues)); + this.concreteStep.configure(GValue.resolveGValues(keyValues)); + } + + @Override + public Parameters getParameters() { + return concreteStep.getParameters(); + } + + @Override + public GValue<Object>[] getIds() { + return ids; + } + + @Override + public Class<E> getReturnClass() { + return returnClass; + } +} diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/map/GraphStepStructure.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/map/GraphStepStructure.java new file mode 100644 index 0000000000..76c333f460 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/map/GraphStepStructure.java @@ -0,0 +1,28 @@ +/* + * 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.util.structure.map; + +import org.apache.tinkerpop.gremlin.process.traversal.step.Configuring; +import org.apache.tinkerpop.gremlin.structure.Direction; + +public interface GraphStepStructure<E, A> extends Configuring { + + public A[] getIds(); + public Class<E> getReturnClass(); +} diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/map/MergeEdgeStepGV.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/map/MergeEdgeStepGV.java new file mode 100644 index 0000000000..f3cae8bd5f --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/map/MergeEdgeStepGV.java @@ -0,0 +1,94 @@ +/* + * 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.util.structure.map; + +import org.apache.tinkerpop.gremlin.process.traversal.Merge; +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.process.traversal.lambda.ConstantGVTraversal; +import org.apache.tinkerpop.gremlin.process.traversal.lambda.ConstantTraversal; +import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent; +import org.apache.tinkerpop.gremlin.process.traversal.step.map.MergeVertexStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.GValue; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.GValueStep; +import org.apache.tinkerpop.gremlin.structure.Edge; + +import java.util.Map; + +public class MergeEdgeStepGV<S> extends GValueStep<MergeVertexStep, S, Edge> implements TraversalOptionParent<Merge, S, Map>, MergeElementStepStructure<S> { + + private Traversal.Admin<S, Map> merge; + private Traversal.Admin<S, Map> create; + private Traversal.Admin<S, Map> match; + + + public MergeEdgeStepGV(final Traversal.Admin traversal, final boolean isStart) { + super(traversal, new MergeVertexStep(traversal, isStart)); + } + + public MergeEdgeStepGV(final Traversal.Admin traversal, final boolean isStart, final Map merge) { + super(traversal, new MergeVertexStep(traversal, isStart, merge)); + } + + public MergeEdgeStepGV(final Traversal.Admin traversal, final boolean isStart, final GValue<Map> merge) { + super(traversal, new MergeVertexStep(traversal, isStart, merge.get())); + this.merge = new ConstantGVTraversal<>(merge); + + } + + public MergeEdgeStepGV(final Traversal.Admin traversal, final boolean isStart, final Traversal.Admin<S,Map> mergeTraversal) { + super(traversal, new MergeVertexStep(traversal, isStart, mergeTraversal)); + } + + @Override + public void addChildOption(final Merge token, final Traversal.Admin<S, Map> traversalOption) { + if (token == Merge.onCreate) { + if (traversalOption instanceof ConstantGVTraversal) { + this.create = (Traversal.Admin<S, Map>) traversalOption; + this.getConcreteStep().addChildOption(token, new ConstantTraversal(((ConstantGVTraversal) traversalOption).getEnd())); + } else { + this.getConcreteStep().addChildOption(token, traversalOption); + } + + } else if (token == Merge.onMatch) { + if (traversalOption instanceof ConstantGVTraversal) { + this.match = (Traversal.Admin<S, Map>) traversalOption; + this.getConcreteStep().addChildOption(token, new ConstantTraversal(((ConstantGVTraversal) traversalOption).getEnd())); + } else { + this.getConcreteStep().addChildOption(token, traversalOption); + } + } else { + throw new UnsupportedOperationException(String.format("Option %s for Merge is not supported", token.name())); + } + } + + @Override + public Traversal.Admin<S, Map> getMergeTraversal() { + return merge; + } + + @Override + public Traversal.Admin<S, Map> getOnCreateTraversal() { + return create; + } + + @Override + public Traversal.Admin<S, Map> getOnMatchTraversal() { + return match; + } +} diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/map/MergeElementStepStructure.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/map/MergeElementStepStructure.java new file mode 100644 index 0000000000..af2facc97b --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/map/MergeElementStepStructure.java @@ -0,0 +1,44 @@ +/* + * 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.util.structure.map; + +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; + +import java.util.Map; + +public interface MergeElementStepStructure<S> { + /** + * Gets the traversal that will be used to provide the {@code Map} that will be used to search for elements. + * This {@code Map} also will be used as the default data set to be used to create the element if the search is not + * successful. + */ + public Traversal.Admin<S, Map> getMergeTraversal(); + + /** + * Gets the traversal that will be used to provide the {@code Map} that will be used to create elements that + * do not match the search criteria of {@link #getMergeTraversal()}. + */ + public Traversal.Admin<S, Map> getOnCreateTraversal(); + + /** + * Gets the traversal that will be used to provide the {@code Map} that will be used to modify elements that + * match the search criteria of {@link #getMergeTraversal()}. + */ + public Traversal.Admin<S, Map> getOnMatchTraversal(); +} diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/map/MergeVertexStepGV.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/map/MergeVertexStepGV.java new file mode 100644 index 0000000000..773beb00e4 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/map/MergeVertexStepGV.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.process.traversal.step.util.structure.map; + +import org.apache.tinkerpop.gremlin.process.traversal.Merge; +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.process.traversal.lambda.ConstantGVTraversal; +import org.apache.tinkerpop.gremlin.process.traversal.lambda.ConstantTraversal; +import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent; +import org.apache.tinkerpop.gremlin.process.traversal.step.map.MergeVertexStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.GValue; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.GValueStep; +import org.apache.tinkerpop.gremlin.structure.Vertex; + +import java.util.Map; +import java.util.Set; + +public class MergeVertexStepGV<S> extends GValueStep<MergeVertexStep, S, Vertex> implements TraversalOptionParent<Merge, S, Map>, MergeElementStepStructure<S> { + + private Traversal.Admin<S, Map> merge; + private Traversal.Admin<S, Map> create; + private Traversal.Admin<S, Map> match; + + + public MergeVertexStepGV(final Traversal.Admin traversal, final boolean isStart) { + super(traversal, new MergeVertexStep(traversal, isStart)); + } + + public MergeVertexStepGV(final Traversal.Admin traversal, final boolean isStart, final Map merge) { + super(traversal, new MergeVertexStep(traversal, isStart, merge)); + } + + public MergeVertexStepGV(final Traversal.Admin traversal, final boolean isStart, final GValue<Map> merge) { + super(traversal, new MergeVertexStep(traversal, isStart, merge.get())); + this.merge = new ConstantGVTraversal<>(merge); + + } + + public MergeVertexStepGV(final Traversal.Admin traversal, final boolean isStart, final Traversal.Admin<S,Map> mergeTraversal) { + super(traversal, new MergeVertexStep(traversal, isStart, mergeTraversal)); + } + + @Override + public void addChildOption(final Merge token, final Traversal.Admin<S, Map> traversalOption) { + if (token == Merge.onCreate) { + if (traversalOption instanceof ConstantGVTraversal) { + this.create = (Traversal.Admin<S, Map>) traversalOption; + this.getConcreteStep().addChildOption(token, new ConstantTraversal(((ConstantGVTraversal) traversalOption).getEnd())); + } else { + this.getConcreteStep().addChildOption(token, traversalOption); + } + + } else if (token == Merge.onMatch) { + if (traversalOption instanceof ConstantGVTraversal) { + this.match = (Traversal.Admin<S, Map>) traversalOption; + this.getConcreteStep().addChildOption(token, new ConstantTraversal(((ConstantGVTraversal) traversalOption).getEnd())); + } else { + this.getConcreteStep().addChildOption(token, traversalOption); + } + } else { + throw new UnsupportedOperationException(String.format("Option %s for Merge is not supported", token.name())); + } + } + + @Override + public Traversal.Admin<S, Map> getMergeTraversal() { + return merge; + } + + @Override + public Traversal.Admin<S, Map> getOnCreateTraversal() { + return create; + } + + @Override + public Traversal.Admin<S, Map> getOnMatchTraversal() { + return match; + } +} diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/map/VertexStepGV.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/map/VertexStepGV.java new file mode 100644 index 0000000000..b194a46ca7 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/map/VertexStepGV.java @@ -0,0 +1,67 @@ +/* + * 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.util.structure.map; + +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.process.traversal.step.map.VertexStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.GValue; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.GValueStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters; +import org.apache.tinkerpop.gremlin.structure.Direction; +import org.apache.tinkerpop.gremlin.structure.Element; + +import java.util.Arrays; + +public class VertexStepGV<E extends Element> extends GValueStep<VertexStep, E, E> implements VertexStepStructure<E, GValue<String>> { + private final GValue<String>[] edgeLabels; + private final Parameters parameters = new Parameters(); + + public VertexStepGV(final Traversal.Admin traversal, final Class<E> returnClass, final Direction direction, final GValue<String>... edgeLabels) { + super(traversal, new VertexStep(traversal, returnClass, direction, + Arrays.stream(edgeLabels).map(GValue::get).toArray(String[]::new))); + this.edgeLabels = edgeLabels; + } + + @Override + public void configure(final Object... keyValues) { + // all GValues locally stored in the parameters, but resolve them for the concrete step + this.parameters.set(null, GValue.promoteGValuesInKeyValues(keyValues)); + this.concreteStep.configure(GValue.resolveGValues(keyValues)); + } + + @Override + public Parameters getParameters() { + return concreteStep.getParameters(); + } + + @Override + public Direction getDirection() { + return concreteStep.getDirection(); + } + + @Override + public GValue<String>[] getEdgeLabels() { + return edgeLabels; + } + + @Override + public Class<E> getReturnClass() { + return concreteStep.getReturnClass(); + } +} diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/map/VertexStepStructure.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/map/VertexStepStructure.java new file mode 100644 index 0000000000..7cba0bd87b --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/map/VertexStepStructure.java @@ -0,0 +1,30 @@ +/* + * 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.util.structure.map; + +import org.apache.tinkerpop.gremlin.process.traversal.step.Configuring; +import org.apache.tinkerpop.gremlin.structure.Direction; + +public interface VertexStepStructure<E, A> extends Configuring { + public Direction getDirection(); + + public A[] getEdgeLabels(); + + public Class<E> getReturnClass(); +} diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/sideEffect/AddPropertyStepGV.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/sideEffect/AddPropertyStepGV.java new file mode 100644 index 0000000000..c847958697 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/sideEffect/AddPropertyStepGV.java @@ -0,0 +1,54 @@ +/* + * 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.util.structure.sideEffect; + +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.AddPropertyStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.GValue; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.GValueStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters; +import org.apache.tinkerpop.gremlin.structure.T; +import org.apache.tinkerpop.gremlin.structure.VertexProperty; + +public class AddPropertyStepGV extends GValueStep<AddPropertyStep, Object, Object> implements AddPropertyStepStructure { + private final Parameters parameters = new Parameters(); + + public AddPropertyStepGV(final Traversal.Admin traversal, final VertexProperty.Cardinality cardinality, + final Object keyObject, final GValue<Object> valueObject) { + super(traversal, new AddPropertyStep(traversal, cardinality, keyObject, valueObject.get())); + this.parameters.set(this, T.key, keyObject, T.value, valueObject); + } + + @Override + public void configure(final Object... keyValues) { + // all GValues locally stored in the parameters, but resolve them for the concrete step + this.parameters.set(this, GValue.promoteGValuesInKeyValues(keyValues)); + this.concreteStep.configure(GValue.resolveGValues(keyValues)); + } + + @Override + public Parameters getParameters() { + return this.parameters; + } + + public VertexProperty.Cardinality getCardinality() { + return concreteStep.getCardinality(); + } + +} diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/sideEffect/AddPropertyStepStructure.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/sideEffect/AddPropertyStepStructure.java new file mode 100644 index 0000000000..0e00a2b5b3 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/sideEffect/AddPropertyStepStructure.java @@ -0,0 +1,28 @@ +/* + * 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.util.structure.sideEffect; + +import org.apache.tinkerpop.gremlin.process.traversal.step.Configuring; +import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent; +import org.apache.tinkerpop.gremlin.structure.VertexProperty; + +public interface AddPropertyStepStructure extends Configuring, TraversalParent { + + public VertexProperty.Cardinality getCardinality(); +} diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/InjectStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/sideEffect/InjectStepGV.java similarity index 51% copy from gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/InjectStep.java copy to gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/sideEffect/InjectStepGV.java index 0502f18541..4764910ec8 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/InjectStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/sideEffect/InjectStepGV.java @@ -16,39 +16,25 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect; +package org.apache.tinkerpop.gremlin.process.traversal.step.util.structure.sideEffect; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; -import org.apache.tinkerpop.gremlin.util.iterator.ArrayIterator; +import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.InjectStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.GValue; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.GValueStep; -/** - * @author Marko A. Rodriguez (http://markorodriguez.com) - */ -public final class InjectStep<S> extends StartStep<S> { - - private final S[] injections; +import java.util.Arrays; - @SafeVarargs - public InjectStep(final Traversal.Admin traversal, final S... injections) { - super(traversal); - this.injections = injections; - this.start = new ArrayIterator<>(this.injections); - } +public class InjectStepGV<S> extends GValueStep<InjectStep, S, S> implements InjectStepStructure<GValue<S>> { - @Override - public InjectStep<S> clone() { - final InjectStep<S> clone = (InjectStep<S>) super.clone(); - clone.start = new ArrayIterator<>(clone.injections); - return clone; - } + private final GValue<S>[] injections; - @Override - public void reset() { - super.reset(); - this.start = new ArrayIterator<>(this.injections); + public InjectStepGV(final Traversal.Admin traversal, final GValue<S>... injections) { + super(traversal, new InjectStep<>(traversal, Arrays.stream(injections).map(GValue::get).toArray(Object[]::new))); + this.injections = injections; } - public S[] getInjections() { - return this.injections; + public GValue<S>[] getInjections() { + return injections; } } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/sideEffect/InjectStepStructure.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/sideEffect/InjectStepStructure.java new file mode 100644 index 0000000000..44698339c1 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/structure/sideEffect/InjectStepStructure.java @@ -0,0 +1,23 @@ +/* + * 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.util.structure.sideEffect; + +public interface InjectStepStructure<S> { + public S[] getInjections(); +} diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/ConnectiveStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/ConnectiveStrategy.java index d314b4d65c..0eed16fcc8 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/ConnectiveStrategy.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/ConnectiveStrategy.java @@ -34,7 +34,6 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.util.EmptyStep; import org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy; import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper; -import java.util.Collections; import java.util.List; import java.util.Set; diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/GValueReplacementStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/GValueReplacementStrategy.java new file mode 100644 index 0000000000..1813c93622 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/GValueReplacementStrategy.java @@ -0,0 +1,97 @@ +/* + * 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.strategy.decoration; + +import org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.decoration.VertexProgramStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.Merge; +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.TraversalOptionParent; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.GValueStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.structure.map.MergeElementStepStructure; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class GValueReplacementStrategy extends AbstractTraversalStrategy<TraversalStrategy.DecorationStrategy> implements TraversalStrategy.DecorationStrategy { + private static final GValueReplacementStrategy INSTANCE = new GValueReplacementStrategy(); + + /** + * This strategy needs to be the first decoration to run as it replaces the {@link GValueStep} with the concrete + * step. + */ + private static final Set<Class<? extends DecorationStrategy>> POSTS = new HashSet<>(Arrays.asList( + ConnectiveStrategy.class, ElementIdStrategy.class, EventStrategy.class, PartitionStrategy.class, + RequirementsStrategy.class, SackStrategy.class, SeedStrategy.class, SideEffectStrategy.class, + SubgraphStrategy.class, VertexProgramStrategy.class)); + + private GValueReplacementStrategy() { + } + + public static GValueReplacementStrategy instance() { + return INSTANCE; + } + + @Override + public void apply(Traversal.Admin<?, ?> traversal) { + final List<GValueStep> gvalueSteps = TraversalHelper.getStepsOfAssignableClass(GValueStep.class, traversal); + + // loop through the graphSteps and replace the GValueStep in the traversal with the concrete step + for (final GValueStep gvalueStep : gvalueSteps) { + if (gvalueStep instanceof GValueStep.MergeOptionStep) { + // fold the option argument into the previous step which should be a concrete step. we get in theis + // situation where a step like mergeV/E takes a concrete argument, but its associated option() takes + // a GValue. we can't fold that GValue in at traversal creation time. so this section of code cleans + // it all up. + if (gvalueStep.getPreviousStep() instanceof TraversalOptionParent) { + final TraversalOptionParent parent = (TraversalOptionParent) gvalueStep.getPreviousStep(); + if (gvalueStep instanceof MergeElementStepStructure) { + final MergeElementStepStructure mergeElementStepStructure = (MergeElementStepStructure) gvalueStep; + if (mergeElementStepStructure.getOnCreateTraversal() != null) + parent.addChildOption(Merge.onCreate, mergeElementStepStructure.getOnCreateTraversal()); + else if (mergeElementStepStructure.getOnMatchTraversal() != null) + parent.addChildOption(Merge.onMatch, mergeElementStepStructure.getOnMatchTraversal()); + else + throw new IllegalStateException("No option found for MergeElementStepStructure"); + } else { + throw new IllegalStateException("The GValueStep is not an option argument for a TraversalOptionParent"); + } + } else { + throw new IllegalStateException("The GValueStep is not an option argument for a TraversalOptionParent"); + } + + // remove the step since its just an option argument + traversal.removeStep(gvalueStep); + } else { + final Step concreteStep = gvalueStep.getConcreteStep(); + TraversalHelper.replaceStep(gvalueStep, concreteStep, traversal); + } + } + } + + @Override + public Set<Class<? extends DecorationStrategy>> applyPost() { + return POSTS; + } +} diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalHelper.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalHelper.java index 19117d6e9a..707ade5340 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalHelper.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalHelper.java @@ -47,14 +47,16 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.map.VertexStep; import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.StartStep; import org.apache.tinkerpop.gremlin.process.traversal.step.util.BulkSet; import org.apache.tinkerpop.gremlin.process.traversal.step.util.EmptyStep; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.GValue; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.GValueStep; import org.apache.tinkerpop.gremlin.process.traversal.step.util.HasContainer; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.structure.filter.HasStepGV; import org.apache.tinkerpop.gremlin.structure.Graph; import org.apache.tinkerpop.gremlin.structure.T; import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils; import java.util.ArrayList; import java.util.Collection; -import java.util.Comparator; import java.util.EnumSet; import java.util.HashSet; import java.util.LinkedHashSet; @@ -296,8 +298,8 @@ public final class TraversalHelper { /** * Get steps of the specified classes throughout the traversal. */ - public static List<Step<?,?>> getStepsOfAssignableClassRecursively(final Traversal.Admin<?, ?> traversal, final Class<?>... stepClasses) { - final List<Step<?,?>> list = new ArrayList<>(); + public static List<Step<?, ?>> getStepsOfAssignableClassRecursively(final Traversal.Admin<?, ?> traversal, final Class<?>... stepClasses) { + final List<Step<?, ?>> list = new ArrayList<>(); for (final Step<?, ?> step : traversal.getSteps()) { for (Class<?> stepClass : stepClasses) { if (stepClass.isAssignableFrom(step.getClass())) @@ -320,14 +322,14 @@ public final class TraversalHelper { * Get steps of the specified classes throughout the traversal, collecting them in a fashion that orders them * from the deepest steps first. */ - public static List<Step<?,?>> getStepsOfAssignableClassRecursivelyFromDepth(final Traversal.Admin<?, ?> traversal, final Class<?>... stepClasses) { - final List<Step<?,?>> list = new ArrayList<>(); - final Stack<Step<?,?>> stack = new Stack<>(); + public static List<Step<?, ?>> getStepsOfAssignableClassRecursivelyFromDepth(final Traversal.Admin<?, ?> traversal, final Class<?>... stepClasses) { + final List<Step<?, ?>> list = new ArrayList<>(); + final Stack<Step<?, ?>> stack = new Stack<>(); traversal.getSteps().forEach(stack::push); while (!stack.isEmpty()) { - final Step<?,?> current = stack.pop(); + final Step<?, ?> current = stack.pop(); list.add(current); if (current instanceof TraversalParent) { @@ -782,10 +784,24 @@ public final class TraversalHelper { * @return the has container folded or appended traversal */ public static <T extends Traversal.Admin<?, ?>> T addHasContainer(final T traversal, final HasContainer hasContainer) { - if (traversal.getEndStep() instanceof HasContainerHolder) { - ((HasContainerHolder) traversal.getEndStep()).addHasContainer(hasContainer); - return traversal; - } else - return (T) traversal.addStep(new HasStep<>(traversal, hasContainer)); + final boolean containsGValue = GValue.hasGValueInP(hasContainer.getPredicate()); + final Step<?, ?> endStep = traversal.getEndStep(); + + // when there is a GValue in the hasContainer then it cannot be folded into the previous HasContainerHolder + // unless it too held GValue objects. + if (containsGValue) { + if (endStep instanceof HasContainerHolder && endStep instanceof GValueStep) { + ((HasContainerHolder) endStep).addHasContainer(hasContainer); + return traversal; + } else + return (T) traversal.addStep(new HasStepGV<>(traversal, hasContainer)); + } else { + if (traversal.getEndStep() instanceof HasContainerHolder) { + ((HasContainerHolder) traversal.getEndStep()).addHasContainer(hasContainer); + return traversal; + } else + return (T) traversal.addStep(new HasStep<>(traversal, hasContainer)); + } + } } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/ArrayUtil.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/ArrayUtil.java new file mode 100644 index 0000000000..3d35909348 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/ArrayUtil.java @@ -0,0 +1,42 @@ +/* + * 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.util; + +import org.apache.tinkerpop.gremlin.process.traversal.step.util.GValue; + +public class ArrayUtil { + + /** + * Copies the given array and adds the given element at the beginning of the new array. + * <p> + * The new array contains the same elements of the input array plus the given element in the first position. The + * component type of the new array is the same as that of the input array. + * </p> + * <p> + * If the input array is {@code null}, a new one element array is returned whose component type is the same as the + * element. + * </p> + */ + public static GValue[] addFirst(final GValue[] array, final GValue element) { + final GValue[] elements = new GValue[array.length + 1]; + elements[0] = element; + System.arraycopy(array, 0, elements, 1, array.length); + return elements; + } +}
