diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 2c39a6bf78d..f3f4bc2fcc3 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -30,6 +30,7 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
[[release-4-0-0-beta-1]]
=== TinkerPop 4.0.0-beta.1 (January 17, 2025)
+* Added the `PopContaining` interface designed to get label and `Pop` combinations held in a `PopInstruction` object.
* Added support for deserialization of `Set` for `gremlin-javascript`.
* Added grammar-based `Translator` for all languages including explicit ones for Java and anonymization.
* Removed old `Translator` infrastructure.
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/PopContaining.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/PopContaining.java
new file mode 100644
index 00000000000..89fdf28e582
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/PopContaining.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.step;
+
+import java.util.Objects;
+import java.util.Set;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Pop;
+
+/**
+ * The {@code PopContaining} interface is implemented by traversal steps that maintain Pop instructions
+ * for label access. It provides a mechanism to track and manage how labeled elements should
+ * be accessed using {@link Pop} semantics (first, last, all, or mixed).
+ *
+ * In Gremlin traversals, various elements can be labeled and later accessed via these labels.
+ * The {@link Pop} enum determines how to access these labeled elements when there are multiple
+ * values associated with the same label.
+ *
+ *
+ * {@code
+ * // Simple example with default Pop.last behavior
+ * gremlin> g.V().as("a").out().as("a").select("a")
+ * ==>[v[2]] // returns the last element labeled "a"
+ *
+ * // Using Pop.first to get the first labeled element
+ * gremlin> g.V().as("a").out().as("a").select(first, "a")
+ * ==>[v[1]] // returns the first element labeled "a"
+ *
+ * // Using Pop.all to get all labeled elements
+ * gremlin> g.V().as("a").out().as("a").select(all, "a")
+ * ==>[v[1], v[2]] // returns all elements labeled "a"
+ * }
+ *
+ *
+ * Steps implementing this interface maintain a collection of {@link PopInstruction} objects, each containing
+ * a label and a {@link Pop} value that specifies how to access elements with that label.
+ *
+ */
+public interface PopContaining {
+ public Set getPopInstructions();
+ /**
+ * A class for storing the Scope Context. It has two elements:
+ * - label: String
+ * - pop: Pop value
+ */
+ class PopInstruction {
+ private final Pop pop;
+ private final String label;
+
+ public PopInstruction(String label, Pop pop) {
+ this.pop = pop;
+ this.label = label;
+ }
+
+ public PopInstruction(final Pop pop, final String label) {
+ this.pop = pop;
+ this.label = label;
+ }
+
+ public PopInstruction() {
+ this.pop = null;
+ this.label = "";
+ }
+
+ public String getLabel() {
+ return label;
+ }
+
+ public Pop getPop() {
+ return pop;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final PopInstruction that = (PopInstruction) o;
+ return getPop() == that.getPop() && Objects.equals(getLabel(), that.getLabel());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getPop(), getLabel());
+ }
+ }
+}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Scoping.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Scoping.java
index e2c0e17747e..a765e778757 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Scoping.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Scoping.java
@@ -23,6 +23,7 @@
import org.apache.tinkerpop.gremlin.process.traversal.Step;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@@ -105,7 +106,7 @@
* @author Marko A. Rodriguez (http://markorodriguez.com)
* @author Stephen Mallette (http://stephen.genoprime.com)
*/
-public interface Scoping {
+public interface Scoping extends PopContaining {
public enum Variable {START, END}
@@ -166,6 +167,23 @@ public default S getNullableScopeValue(final Pop pop, final String key, fina
*/
public Set getScopeKeys();
+ /**
+ * Used to get PopInstruction of a Step that implements Scoping. PopInstruction includes the labels it needs, and the
+ * pop type for each label.
+ *
+ * @return A Set of {@link PopInstruction} values which contain the label and Pop value
+ */
+ @Override
+ public default HashSet getPopInstructions() {
+ final Set labels = this.getScopeKeys();
+ final HashSet scopingInfoSet = new HashSet<>();
+ for (final String label : labels) {
+ final PopInstruction scopingInfo = new PopInstruction(Pop.last, label);
+ scopingInfoSet.add(scopingInfo);
+ }
+ return scopingInfoSet;
+ }
+
public static class KeyNotFoundException extends Exception {
private final Object key;
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/TraversalParent.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/TraversalParent.java
index 695d2972fc6..3a43f7b74ce 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/TraversalParent.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/TraversalParent.java
@@ -21,16 +21,18 @@
import org.apache.tinkerpop.gremlin.process.traversal.Step;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
import java.util.Collections;
import java.util.EnumSet;
+import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* @author Marko A. Rodriguez (http://markorodriguez.com)
*/
-public interface TraversalParent extends AutoCloseable {
+public interface TraversalParent extends PopContaining, AutoCloseable {
public default List> getGlobalChildren() {
return Collections.emptyList();
@@ -95,4 +97,16 @@ default void close() throws Exception {
traversal.close();
}
}
+
+ @Override
+ public default HashSet getPopInstructions() {
+ final HashSet scopingInfos = new HashSet<>();
+ for (final Traversal.Admin local: this.getLocalChildren()) {
+ scopingInfos.addAll(TraversalHelper.getPopInstructions(local));
+ }
+ for (final Traversal.Admin global: this.getGlobalChildren()) {
+ scopingInfos.addAll(TraversalHelper.getPopInstructions(global));
+ }
+ return scopingInfos;
+ }
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DedupGlobalStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DedupGlobalStep.java
index ad4a762fe01..4bfc97c52fa 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DedupGlobalStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DedupGlobalStep.java
@@ -189,6 +189,14 @@ public Set getScopeKeys() {
return null == this.dedupLabels ? Collections.emptySet() : this.dedupLabels;
}
+ @Override
+ public HashSet getPopInstructions() {
+ final HashSet popInstructions = new HashSet<>();
+ popInstructions.addAll(Scoping.super.getPopInstructions());
+ popInstructions.addAll(TraversalParent.super.getPopInstructions());
+ return popInstructions;
+ }
+
@Override
public void processAllStarts() {
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WherePredicateStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WherePredicateStep.java
index cffe3ebc8c3..69b054446af 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WherePredicateStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WherePredicateStep.java
@@ -132,6 +132,14 @@ public Set getScopeKeys() {
return Collections.unmodifiableSet(this.scopeKeys);
}
+ @Override
+ public HashSet getPopInstructions() {
+ final HashSet popInstructions = new HashSet<>();
+ popInstructions.addAll(Scoping.super.getPopInstructions());
+ popInstructions.addAll(TraversalParent.super.getPopInstructions());
+ return popInstructions;
+ }
+
@Override
public WherePredicateStep clone() {
final WherePredicateStep clone = (WherePredicateStep) super.clone();
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WhereTraversalStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WhereTraversalStep.java
index dde900348a0..1fa30b98b95 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WhereTraversalStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WhereTraversalStep.java
@@ -147,6 +147,14 @@ public Set getKeepLabels() {
return this.keepLabels;
}
+ @Override
+ public HashSet getPopInstructions() {
+ final HashSet popInstructions = new HashSet<>();
+ popInstructions.addAll(Scoping.super.getPopInstructions());
+ popInstructions.addAll(TraversalParent.super.getPopInstructions());
+ return popInstructions;
+ }
+
//////////////////////////////
public static class WhereStartStep extends ScalarMapStep implements Scoping {
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStartStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStartStep.java
index 81f804f3e26..1c7d5a90512 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStartStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStartStep.java
@@ -43,6 +43,7 @@
import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
+import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -89,6 +90,13 @@ public Set getScopeKeys() {
return this.parameters.getReferencedLabels();
}
+ @Override
+ public HashSet getPopInstructions() {
+ final HashSet popInstructions = new HashSet<>();
+ popInstructions.addAll(TraversalParent.super.getPopInstructions());
+ return popInstructions;
+ }
+
@Override
public void configure(final Object... keyValues) {
this.parameters.set(this, keyValues);
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java
index 258f8421ab2..d9097f2ffef 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java
@@ -40,6 +40,7 @@
import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
+import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -86,6 +87,13 @@ public Set getScopeKeys() {
return this.parameters.getReferencedLabels();
}
+ @Override
+ public HashSet getPopInstructions() {
+ final HashSet popInstructions = new HashSet<>();
+ popInstructions.addAll(TraversalParent.super.getPopInstructions());
+ return popInstructions;
+ }
+
@Override
public void configure(final Object... keyValues) {
this.parameters.set(this, keyValues);
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStep.java
index d8dad9dbda0..c7a27e2eb82 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStep.java
@@ -38,6 +38,7 @@
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
+import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -85,6 +86,13 @@ public Set getScopeKeys() {
return this.parameters.getReferencedLabels();
}
+ @Override
+ public HashSet getPopInstructions() {
+ final HashSet popInstructions = new HashSet<>();
+ popInstructions.addAll(TraversalParent.super.getPopInstructions());
+ return popInstructions;
+ }
+
@Override
public List> getLocalChildren() {
return this.parameters.getTraversals();
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java
index cfde7d84fce..bcf21c7bc72 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java
@@ -34,6 +34,7 @@
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
+import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -78,6 +79,13 @@ public Set getScopeKeys() {
return this.parameters.getReferencedLabels();
}
+ @Override
+ public HashSet getPopInstructions() {
+ final HashSet popInstructions = new HashSet<>();
+ popInstructions.addAll(TraversalParent.super.getPopInstructions());
+ return popInstructions;
+ }
+
@Override
public List> getLocalChildren() {
return this.parameters.getTraversals();
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/FormatStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/FormatStep.java
index c4b50958d90..39d7d005867 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/FormatStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/FormatStep.java
@@ -34,6 +34,7 @@
import org.apache.tinkerpop.gremlin.structure.Property;
import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
+import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
@@ -168,6 +169,14 @@ public Set getScopeKeys() {
return variables;
}
+ @Override
+ public HashSet getPopInstructions() {
+ final HashSet popInstructions = new HashSet<>();
+ popInstructions.addAll(Scoping.super.getPopInstructions());
+ popInstructions.addAll(TraversalParent.super.getPopInstructions());
+ return popInstructions;
+ }
+
@Override
public void setKeepLabels(final Set labels) {
this.keepLabels = labels;
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MatchStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MatchStep.java
index 6de3970236f..055f3029966 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MatchStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MatchStep.java
@@ -218,6 +218,14 @@ public Set getScopeKeys() {
return this.scopeKeys;
}
+ @Override
+ public HashSet getPopInstructions() {
+ final HashSet popInstructions = new HashSet<>();
+ popInstructions.addAll(Scoping.super.getPopInstructions());
+ popInstructions.addAll(TraversalParent.super.getPopInstructions());
+ return popInstructions;
+ }
+
@Override
public String toString() {
return StringFactory.stepString(this, this.dedupLabels, this.connective, this.matchTraversals);
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MathStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MathStep.java
index e36a1311c8a..25330705429 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MathStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MathStep.java
@@ -165,6 +165,14 @@ public Set getScopeKeys() {
return this.expression.getVariables();
}
+ @Override
+ public HashSet getPopInstructions() {
+ final HashSet popInstructions = new HashSet<>();
+ popInstructions.addAll(Scoping.super.getPopInstructions());
+ popInstructions.addAll(TraversalParent.super.getPopInstructions());
+ return popInstructions;
+ }
+
@Override
public void setKeepLabels(final Set labels) {
this.keepLabels = labels;
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectOneStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectOneStep.java
index b2de4b002c9..dd6661eb77d 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectOneStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectOneStep.java
@@ -134,6 +134,18 @@ public Set getScopeKeys() {
return Collections.singleton(this.selectKey);
}
+ @Override
+ public HashSet getPopInstructions() {
+ final HashSet popInstructions = new HashSet<>();
+ final Set labels = this.getScopeKeys();
+ for (String label : labels) {
+ final PopInstruction scopingInfo = new PopInstruction(this.getPop(), label);
+ popInstructions.add(scopingInfo);
+ }
+ popInstructions.addAll(TraversalParent.super.getPopInstructions());
+ return popInstructions;
+ }
+
public Pop getPop() {
return this.pop;
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectStep.java
index 24c9771ed2c..d47c9037396 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SelectStep.java
@@ -34,7 +34,6 @@
import java.util.Arrays;
import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
@@ -149,6 +148,18 @@ public Set getScopeKeys() {
return this.selectKeysSet;
}
+ @Override
+ public HashSet getPopInstructions() {
+ final HashSet popInstructions = new HashSet<>();
+ final Set labels = this.getScopeKeys();
+ for (final String label : labels) {
+ final PopInstruction scopingInfo = new PopInstruction(this.getPop(), label);
+ popInstructions.add(scopingInfo);
+ }
+ popInstructions.addAll(TraversalParent.super.getPopInstructions());
+ return popInstructions;
+ }
+
/**
* Get the keys for this SelectStep. Unlike {@link SelectStep#getScopeKeys()}, this returns a list possibly with
* a duplicate key. This guarantees to return the keys in the same order as passed in.
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TraversalSelectStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TraversalSelectStep.java
index ff9f0996f5d..0eea87f36cc 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TraversalSelectStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TraversalSelectStep.java
@@ -85,6 +85,18 @@ public Set getScopeKeys() {
return Collections.emptySet();
}
+ @Override
+ public HashSet getPopInstructions() {
+ final HashSet popInstructions = new HashSet<>();
+ final Set labels = this.getScopeKeys();
+ for (String label : labels) {
+ final PopInstruction scopingInfo = new PopInstruction(this.getPop(), label);
+ popInstructions.add(scopingInfo);
+ }
+ popInstructions.addAll(TraversalParent.super.getPopInstructions());
+ return popInstructions;
+ }
+
@Override
public String toString() {
return StringFactory.stepString(this, this.pop, this.keyTraversal, this.selectTraversal);
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 3dfef89285a..5f869b6df31 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
@@ -38,6 +38,7 @@
import org.apache.tinkerpop.gremlin.structure.util.keyed.KeyedProperty;
import org.apache.tinkerpop.gremlin.structure.util.keyed.KeyedVertexProperty;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
@@ -70,6 +71,14 @@ public Set getScopeKeys() {
return this.parameters.getReferencedLabels();
}
+ @Override
+ public HashSet getPopInstructions() {
+ final HashSet popInstructions = new HashSet<>();
+ popInstructions.addAll(Scoping.super.getPopInstructions());
+ popInstructions.addAll(TraversalParent.super.getPopInstructions());
+ return popInstructions;
+ }
+
@Override
public List> getLocalChildren() {
return this.parameters.getTraversals();
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 19117d6e9ab..44df37cfd02 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
@@ -28,6 +28,7 @@
import org.apache.tinkerpop.gremlin.process.traversal.lambda.TokenTraversal;
import org.apache.tinkerpop.gremlin.process.traversal.step.ByModulating;
import org.apache.tinkerpop.gremlin.process.traversal.step.HasContainerHolder;
+import org.apache.tinkerpop.gremlin.process.traversal.step.PopContaining;
import org.apache.tinkerpop.gremlin.process.traversal.step.Scoping;
import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
import org.apache.tinkerpop.gremlin.process.traversal.step.branch.RepeatStep;
@@ -54,7 +55,6 @@
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Comparator;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.LinkedHashSet;
@@ -788,4 +788,22 @@ public static void applySingleLevelStrategies(final Traversal.Admin, ?> parent
} else
return (T) traversal.addStep(new HasStep<>(traversal, hasContainer));
}
+
+ /**
+ * Used to get PopInstruction of a traversal. Pop Instruction includes the labels it needs, and the pop type for each label.
+ *
+ * @param traversal the traversal to get Scope Context for
+ * @param the traversal type
+ * @return A Set of {@link org.apache.tinkerpop.gremlin.process.traversal.step.PopContaining.PopInstruction} values which contain the label and Pop value
+ */
+ public static > Set getPopInstructions(final T traversal) {
+ final Set scopingInfos = new HashSet<>();
+ for (final Step step: traversal.getSteps()) {
+ if (step instanceof PopContaining) {
+ scopingInfos.addAll(((PopContaining) step).getPopInstructions());
+ }
+ }
+ return scopingInfos;
+ }
+
}
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/TestDataBuilder.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/TestDataBuilder.java
new file mode 100644
index 00000000000..1f429659faa
--- /dev/null
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/TestDataBuilder.java
@@ -0,0 +1,48 @@
+/*
+ * 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;
+
+import java.util.HashSet;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Pop;
+import org.apache.tinkerpop.gremlin.process.traversal.step.PopContaining;
+
+/**
+ * This class is responsible for building test data for `PopInstruction` Unit tests.
+ * It provides methods to create a set of `PopInstruction` test data objects.
+ *
+ */
+public class TestDataBuilder {
+
+ // Helper function to create a Set of `PopInstruction` values
+ public static HashSet createPopInstructionSet(final Object[]... pairs) {
+ final HashSet popInstructions = new HashSet<>();
+
+ // Each pair should contain a name (String) and a Pop value
+ for (final Object[] pair : pairs) {
+ if (pair.length == 2 && pair[0] instanceof String && pair[1] instanceof Pop) {
+ popInstructions.add(new PopContaining.PopInstruction((String)pair[0], (Pop)pair[1]));
+ } else {
+ throw new IllegalArgumentException("Invalid pair: " + pair);
+ }
+ }
+
+ return popInstructions;
+ }
+}
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DedupGlobalStepTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DedupGlobalStepTest.java
index d7beabe555d..a763e90ea79 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DedupGlobalStepTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DedupGlobalStepTest.java
@@ -18,13 +18,19 @@
*/
package org.apache.tinkerpop.gremlin.process.traversal.step.filter;
+import org.apache.tinkerpop.gremlin.TestDataBuilder;
+import org.apache.tinkerpop.gremlin.process.traversal.Pop;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
+import org.apache.tinkerpop.gremlin.process.traversal.step.PopContaining;
import org.apache.tinkerpop.gremlin.process.traversal.step.StepTest;
+import org.junit.Test;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
-import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
/**
* @author Daniel Kuppitz (http://gremlin.guru)
@@ -43,4 +49,16 @@ protected List getTraversals() {
public void shouldThrowForMultipleByModulators() {
__.dedup().by("name").by("age");
}
+
+ @Test
+ public void shouldObtainPopInstructions() {
+ final DedupGlobalStep dedupGlobalStep = new DedupGlobalStep(__.identity().asAdmin(), "label1", "label2", "label1");
+
+ final HashSet popInstructionSet = TestDataBuilder.createPopInstructionSet(
+ new Object[]{"label1", Pop.last},
+ new Object[]{"label2", Pop.last}
+ );
+
+ assertEquals(dedupGlobalStep.getPopInstructions(), popInstructionSet);
+ }
}
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WhereStepTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WhereStepTest.java
index fa9adeb1f7a..24700083f5a 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WhereStepTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WhereStepTest.java
@@ -18,15 +18,20 @@
*/
package org.apache.tinkerpop.gremlin.process.traversal.step.filter;
+import org.apache.tinkerpop.gremlin.TestDataBuilder;
import org.apache.tinkerpop.gremlin.process.traversal.P;
+import org.apache.tinkerpop.gremlin.process.traversal.Pop;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
+import org.apache.tinkerpop.gremlin.process.traversal.step.PopContaining;
import org.apache.tinkerpop.gremlin.process.traversal.step.StepTest;
import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
+import org.apache.tinkerpop.gremlin.process.traversal.util.DefaultTraversal;
import org.junit.Test;
-
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
+import java.util.Optional;
import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.as;
import static org.junit.Assert.assertEquals;
@@ -65,4 +70,30 @@ public void shouldRequirePathsAccordingly() {
assertEquals(traversalPath[0], ((Traversal.Admin, ?>) traversalPath[1]).getTraverserRequirements().contains(TraverserRequirement.LABELED_PATH));
}
}
+
+ @Test
+ public void shouldObtainPopInstructions() {
+
+ // Testing WherePredicate Step
+ final WherePredicateStep wherePredicateStep = new WherePredicateStep(__.identity().asAdmin(), Optional.of("key1"), P.neq("label1"));
+
+ HashSet popInstructionSet = TestDataBuilder.createPopInstructionSet(
+ new Object[]{"key1", Pop.last},
+ new Object[]{"label1", Pop.last}
+ );
+
+ assertEquals(wherePredicateStep.getPopInstructions(), popInstructionSet);
+
+ // Testing WhereTraversal Test
+ final WhereTraversalStep whereTraversalStep = new WhereTraversalStep<>(new DefaultTraversal(), __.as("x").select(Pop.first, "a", "b").asAdmin());
+
+ popInstructionSet = TestDataBuilder.createPopInstructionSet(
+ new Object[]{"x", Pop.last},
+ new Object[]{"a", Pop.first},
+ new Object[]{"b", Pop.first}
+ );
+
+ assertEquals(whereTraversalStep.getPopInstructions(), popInstructionSet);
+
+ }
}
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStepTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStepTest.java
index f4282aec696..d5742ba1f02 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStepTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStepTest.java
@@ -18,13 +18,23 @@
*/
package org.apache.tinkerpop.gremlin.process.traversal.step.map;
+import org.apache.tinkerpop.gremlin.TestDataBuilder;
+import org.apache.tinkerpop.gremlin.process.traversal.Pop;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
import org.apache.tinkerpop.gremlin.process.traversal.step.StepTest;
+import org.junit.Test;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
+import static org.junit.Assert.assertEquals;
+
+
+import org.apache.tinkerpop.gremlin.process.traversal.step.PopContaining;
+
+
/**
* @author Daniel Kuppitz (http://gremlin.guru)
*/
@@ -39,4 +49,24 @@ protected List getTraversals() {
__.addE("knows").property("c", "d")
);
}
+
+ @Test
+ public void shouldObtainPopInstructions() {
+ // Edge Step Test
+ final AddEdgeStep