This is an automated email from the ASF dual-hosted git repository. andreac pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit f50294137f401188f13039c6d350171ded7c6e97 Merge: 6b5c24d20c a64ed7220e Author: Andrea Child <[email protected]> AuthorDate: Tue Oct 21 09:47:50 2025 -0700 Merge branch '3.8-dev' CHANGELOG.asciidoc | 2 + docs/src/reference/the-traversal.asciidoc | 21 +- docs/src/upgrade/release-3.8.x.asciidoc | 70 +- .../gremlin/process/traversal/Traverser.java | 19 +- .../process/traversal/step/branch/RepeatStep.java | 2 +- .../traversal/step/filter/RangeGlobalStep.java | 86 ++- .../optimization/RepeatUnrollStrategy.java | 4 + .../traverser/B_LP_NL_O_P_S_SE_SL_Traverser.java | 90 +-- .../traverser/B_LP_NL_O_S_SE_SL_Traverser.java | 94 +-- .../traverser/B_NL_O_S_SE_SL_Traverser.java | 93 +-- .../traversal/traverser/B_O_S_SE_SL_Traverser.java | 70 +- .../traverser/LP_NL_O_OB_P_S_SE_SL_Traverser.java | 97 +-- .../traverser/LP_NL_O_OB_S_SE_SL_Traverser.java | 95 +-- .../traverser/NL_O_OB_S_SE_SL_Traverser.java | 95 +-- .../traversal/traverser/NL_SL_Traverser.java | 247 +++++++ .../traverser/O_OB_S_SE_SL_Traverser.java | 83 ++- .../traverser/util/AbstractTraverser.java | 27 +- .../traversal/traverser/util/LabelledCounter.java | 4 + .../traversal/step/filter/RangeGlobalStepTest.java | 61 +- .../B_LP_NL_O_P_S_SE_SL_TraverserTest.java | 28 + .../traverser/B_LP_NL_O_S_SE_SL_TraverserTest.java | 28 + .../traverser/B_LP_O_P_S_SE_SL_TraverserTest.java | 28 + .../traverser/B_LP_O_S_SE_SL_TraverserTest.java | 28 + .../traverser/B_NL_O_S_SE_SL_TraverserTest.java | 28 + .../traverser/B_O_S_SE_SL_TraverserTest.java | 27 + .../LP_NL_O_OB_P_S_SE_SL_TraverserTest.java | 28 + .../LP_NL_O_OB_S_SE_SL_TraverserTest.java | 28 + .../traverser/LP_O_OB_P_S_SE_SL_TraverserTest.java | 28 + .../traverser/LP_O_OB_S_SE_SL_TraverserTest.java | 28 + .../traverser/NL_O_OB_S_SE_SL_TraverserTest.java | 28 + .../traversal/traverser/NL_TraverserTest.java | 158 +++++ .../traverser/O_OB_S_SE_SL_TraverserTest.java | 28 + .../traversal/traverser/SL_TraverserTest.java | 135 ++++ .../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs | 21 + gremlin-go/driver/cucumber/gremlin.go | 21 + .../gremlin-javascript/test/cucumber/gremlin.js | 21 + gremlin-python/src/main/python/radish/gremlin.py | 21 + .../gremlin/test/features/filter/Range.feature | 236 +++++++ .../integrated/RepeatUnrollStrategy.feature | 4 +- .../TinkerGraphRepeatRangeGlobalTest.java | 769 +++++++++++++++++++++ 40 files changed, 2404 insertions(+), 577 deletions(-) diff --cc gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_LP_NL_O_P_S_SE_SL_Traverser.java index fd54ff71bc,335b8070d1..a0ce38d1f2 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_LP_NL_O_P_S_SE_SL_Traverser.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_LP_NL_O_P_S_SE_SL_Traverser.java @@@ -18,20 -18,16 +18,17 @@@ */ package org.apache.tinkerpop.gremlin.process.traversal.traverser; + import java.util.Objects; + import java.util.Stack; -import org.apache.commons.collections.map.ReferenceMap; +import org.apache.commons.collections4.map.AbstractReferenceMap; +import org.apache.commons.collections4.map.ReferenceMap; import org.apache.tinkerpop.gremlin.process.traversal.Step; import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.LabelledCounter; - - import java.util.Iterator; - import java.util.Map; - import java.util.Stack; - - public class B_LP_NL_O_P_S_SE_SL_Traverser<T> extends B_LP_O_P_S_SE_SL_Traverser<T> { + public class B_LP_NL_O_P_S_SE_SL_Traverser<T> extends B_LP_O_P_S_SE_SL_Traverser<T> implements NL_SL_Traverser<T> { protected Stack<LabelledCounter> nestedLoops; - protected ReferenceMap loopNames = null; + protected ReferenceMap<String,Object> loopNames = null; protected B_LP_NL_O_P_S_SE_SL_Traverser() { } @@@ -39,25 -35,17 +36,18 @@@ public B_LP_NL_O_P_S_SE_SL_Traverser(final T t, final Step<T, ?> step, final long initialBulk) { super(t, step, initialBulk); this.nestedLoops = new Stack<>(); - this.loopNames = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.WEAK); + this.loopNames = new ReferenceMap<>(AbstractReferenceMap.ReferenceStrength.HARD, + AbstractReferenceMap.ReferenceStrength.WEAK); } - ///////////////// - @Override - public int loops() { - return this.nestedLoops.peek().count(); + public Stack<LabelledCounter> getNestedLoops() { + return nestedLoops; } @Override - public int loops(final String loopName) { - if (loopName == null) - return loops(); - else if (this.loopNames.containsKey(loopName)) - return ((LabelledCounter) this.loopNames.get(loopName)).count(); - else - throw new IllegalArgumentException("Loop name not defined: " + loopName); - public ReferenceMap getNestedLoopNames() { ++ public ReferenceMap<String,Object> getNestedLoopNames() { + return loopNames; } @Override @@@ -158,4 -97,10 +99,11 @@@ return result; } + private void cloneLoopState(final B_LP_NL_O_P_S_SE_SL_Traverser<?> clone) { + clone.nestedLoops = new Stack<>(); - clone.loopNames = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.WEAK); ++ clone.loopNames = new ReferenceMap<>(AbstractReferenceMap.ReferenceStrength.HARD, ++ AbstractReferenceMap.ReferenceStrength.WEAK); + copyNestedLoops(clone.nestedLoops, clone.loopNames); + } + } diff --cc gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_LP_NL_O_S_SE_SL_Traverser.java index d14c8c1a91,03c3a5835b..f3cf0fb87d --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_LP_NL_O_S_SE_SL_Traverser.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_LP_NL_O_S_SE_SL_Traverser.java @@@ -18,20 -18,16 +18,17 @@@ */ package org.apache.tinkerpop.gremlin.process.traversal.traverser; + import java.util.Objects; + import java.util.Stack; -import org.apache.commons.collections.map.ReferenceMap; +import org.apache.commons.collections4.map.ReferenceMap; +import org.apache.commons.collections4.map.AbstractReferenceMap; import org.apache.tinkerpop.gremlin.process.traversal.Step; import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.LabelledCounter; - import java.util.Iterator; - import java.util.Map; - import java.util.Set; - import java.util.Stack; - - public class B_LP_NL_O_S_SE_SL_Traverser<T> extends B_LP_O_S_SE_SL_Traverser<T> { + public class B_LP_NL_O_S_SE_SL_Traverser<T> extends B_LP_O_S_SE_SL_Traverser<T> implements NL_SL_Traverser<T> { protected Stack<LabelledCounter> nestedLoops; - protected ReferenceMap loopNames = null; + protected ReferenceMap<String,Object> loopNames = null; protected B_LP_NL_O_S_SE_SL_Traverser() { } @@@ -39,39 -35,17 +36,18 @@@ public B_LP_NL_O_S_SE_SL_Traverser(final T t, final Step<T, ?> step, final long initialBulk) { super(t, step, initialBulk); this.nestedLoops = new Stack<>(); - this.loopNames = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.WEAK); + this.loopNames = new ReferenceMap<>(AbstractReferenceMap.ReferenceStrength.HARD, + AbstractReferenceMap.ReferenceStrength.WEAK); } - ///////////////// - - @Override - public int loops() { - return this.nestedLoops.peek().count(); - } - @Override - public int loops(final String loopName) { - if (loopName == null) - return loops(); - else if (this.loopNames.containsKey(loopName)) - return ((LabelledCounter) this.loopNames.get(loopName)).count(); - else - throw new IllegalArgumentException("Loop name not defined: " + loopName); + public Stack<LabelledCounter> getNestedLoops() { + return nestedLoops; } @Override - public void initialiseLoops(final String stepLabel, final String loopName) { - if (this.nestedLoops.empty() || !this.nestedLoops.peek().hasLabel(stepLabel)) { - final LabelledCounter lc = new LabelledCounter(stepLabel, (short) 0); - this.nestedLoops.push(lc); - if (loopName != null) - this.loopNames.put(loopName, lc); - } - } - @Override - public Set<String> getLoopNames() { - return loopNames.keySet(); - public ReferenceMap getNestedLoopNames() { ++ public ReferenceMap<String,Object> getNestedLoopNames() { + return loopNames; } @Override @@@ -162,4 -97,10 +99,11 @@@ return result; } + private void cloneLoopState(final B_LP_NL_O_S_SE_SL_Traverser<?> clone) { + clone.nestedLoops = new Stack<>(); - clone.loopNames = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.WEAK); ++ clone.loopNames = new ReferenceMap<>(AbstractReferenceMap.ReferenceStrength.HARD, ++ AbstractReferenceMap.ReferenceStrength.WEAK); + copyNestedLoops(clone.nestedLoops, clone.loopNames); + } + } diff --cc gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_NL_O_S_SE_SL_Traverser.java index ba1d543a5a,46794ca4e6..ebe3ee9f58 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_NL_O_S_SE_SL_Traverser.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_NL_O_S_SE_SL_Traverser.java @@@ -18,19 -18,16 +18,17 @@@ */ package org.apache.tinkerpop.gremlin.process.traversal.traverser; + import java.util.Objects; + import java.util.Stack; -import org.apache.commons.collections.map.ReferenceMap; +import org.apache.commons.collections4.map.AbstractReferenceMap; +import org.apache.commons.collections4.map.ReferenceMap; import org.apache.tinkerpop.gremlin.process.traversal.Step; import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.LabelledCounter; - import java.util.Iterator; - import java.util.Map; - import java.util.Stack; - - public class B_NL_O_S_SE_SL_Traverser<T> extends B_O_S_SE_SL_Traverser<T> { + public class B_NL_O_S_SE_SL_Traverser<T> extends B_O_S_SE_SL_Traverser<T> implements NL_SL_Traverser<T> { protected Stack<LabelledCounter> nestedLoops; - protected ReferenceMap loopNames = null; + protected ReferenceMap<String,Object> loopNames = null; protected B_NL_O_S_SE_SL_Traverser() { } @@@ -38,45 -35,22 +36,23 @@@ public B_NL_O_S_SE_SL_Traverser(final T t, final Step<T, ?> step, final long initialBulk) { super(t, step, initialBulk); this.nestedLoops = new Stack<>(); - this.loopNames = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.WEAK); + this.loopNames = new ReferenceMap<>(AbstractReferenceMap.ReferenceStrength.HARD, + AbstractReferenceMap.ReferenceStrength.WEAK); } - ///////////////// - - @Override - public int loops() { - return this.nestedLoops.peek().count(); - } - @Override - public int loops(final String loopName) { - if (loopName == null) - return loops(); - else if (this.loopNames.containsKey(loopName)) - return ((LabelledCounter) this.loopNames.get(loopName)).count(); - else - throw new IllegalArgumentException("Loop name not defined: " + loopName); + public Stack<LabelledCounter> getNestedLoops() { + return nestedLoops; } @Override - public void initialiseLoops(final String stepLabel, final String loopName) { - if (this.nestedLoops.empty() || !this.nestedLoops.peek().hasLabel(stepLabel)) { - final LabelledCounter lc = new LabelledCounter(stepLabel, (short) 0); - this.nestedLoops.push(lc); - if (loopName != null) - this.loopNames.put(loopName, lc); - } - public ReferenceMap getNestedLoopNames() { ++ public ReferenceMap<String,Object> getNestedLoopNames() { + return loopNames; } - + @Override - public void incrLoops() { - this.nestedLoops.peek().increment(); - } - - @Override - public void resetLoops() { - this.nestedLoops.pop(); + public TraverserRequirement getLoopRequirement() { + return TraverserRequirement.NESTED_LOOP; } ///////////////// @@@ -159,4 -97,10 +99,11 @@@ return result; } + private void cloneLoopState(final B_NL_O_S_SE_SL_Traverser<?> clone) { + clone.nestedLoops = new Stack<>(); - clone.loopNames = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.WEAK); ++ clone.loopNames = new ReferenceMap<>(AbstractReferenceMap.ReferenceStrength.HARD, ++ AbstractReferenceMap.ReferenceStrength.WEAK); + copyNestedLoops(clone.nestedLoops, clone.loopNames); + } + } diff --cc gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/LP_NL_O_OB_P_S_SE_SL_Traverser.java index ee22c6f5ee,4d78805f51..06b8aea4c0 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/LP_NL_O_OB_P_S_SE_SL_Traverser.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/LP_NL_O_OB_P_S_SE_SL_Traverser.java @@@ -19,20 -19,16 +19,17 @@@ package org.apache.tinkerpop.gremlin.process.traversal.traverser; + import java.util.Objects; + import java.util.Stack; -import org.apache.commons.collections.map.ReferenceMap; +import org.apache.commons.collections4.map.AbstractReferenceMap; +import org.apache.commons.collections4.map.ReferenceMap; import org.apache.tinkerpop.gremlin.process.traversal.Step; import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.LabelledCounter; - import java.util.Iterator; - import java.util.Map; - import java.util.Set; - import java.util.Stack; - - public class LP_NL_O_OB_P_S_SE_SL_Traverser<T> extends LP_O_OB_P_S_SE_SL_Traverser<T> { + public class LP_NL_O_OB_P_S_SE_SL_Traverser<T> extends LP_O_OB_P_S_SE_SL_Traverser<T> implements NL_SL_Traverser<T> { protected Stack<LabelledCounter> nestedLoops; -- protected ReferenceMap loopNames = null; ++ protected ReferenceMap<String,Object> loopNames = null; protected LP_NL_O_OB_P_S_SE_SL_Traverser() { } @@@ -40,40 -36,17 +37,18 @@@ public LP_NL_O_OB_P_S_SE_SL_Traverser(final T t, final Step<T, ?> step) { super(t, step); this.nestedLoops = new Stack<>(); - this.loopNames = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.WEAK); + this.loopNames = new ReferenceMap<>(AbstractReferenceMap.ReferenceStrength.HARD, + AbstractReferenceMap.ReferenceStrength.WEAK); } - ///////////////// - - @Override - public int loops() { - return this.nestedLoops.peek().count(); - } - - @Override - public int loops(final String loopName) { - if (loopName == null) - return loops(); - else if (this.loopNames.containsKey(loopName)) - return ((LabelledCounter) this.loopNames.get(loopName)).count(); - else - throw new IllegalArgumentException("Loop name not defined: " + loopName); - } - @Override - public void initialiseLoops(final String stepLabel, final String loopName) { - if (this.nestedLoops.empty() || !this.nestedLoops.peek().hasLabel(stepLabel)) { - final LabelledCounter lc = new LabelledCounter(stepLabel, (short) 0); - this.nestedLoops.push(lc); - if (loopName != null) - this.loopNames.put(loopName, lc); - } + public Stack<LabelledCounter> getNestedLoops() { + return nestedLoops; } @Override - public void incrLoops() { - this.nestedLoops.peek().increment(); - public ReferenceMap getNestedLoopNames() { ++ public ReferenceMap<String,Object> getNestedLoopNames() { + return loopNames; } @Override @@@ -164,4 -98,10 +100,11 @@@ return result; } + private void cloneLoopState(final LP_NL_O_OB_P_S_SE_SL_Traverser<?> clone) { + clone.nestedLoops = new Stack<>(); - clone.loopNames = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.WEAK); ++ clone.loopNames = new ReferenceMap<>(AbstractReferenceMap.ReferenceStrength.HARD, ++ AbstractReferenceMap.ReferenceStrength.WEAK); + copyNestedLoops(clone.nestedLoops, clone.loopNames); + } + } diff --cc gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/LP_NL_O_OB_S_SE_SL_Traverser.java index 47a8058015,5b86416e32..12073cea2c --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/LP_NL_O_OB_S_SE_SL_Traverser.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/LP_NL_O_OB_S_SE_SL_Traverser.java @@@ -19,20 -19,16 +19,17 @@@ package org.apache.tinkerpop.gremlin.process.traversal.traverser; + import java.util.Objects; + import java.util.Stack; -import org.apache.commons.collections.map.ReferenceMap; +import org.apache.commons.collections4.map.AbstractReferenceMap; +import org.apache.commons.collections4.map.ReferenceMap; import org.apache.tinkerpop.gremlin.process.traversal.Step; import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.LabelledCounter; - import java.util.Iterator; - import java.util.Map; - import java.util.Set; - import java.util.Stack; - - public class LP_NL_O_OB_S_SE_SL_Traverser<T> extends LP_O_OB_S_SE_SL_Traverser<T> { + public class LP_NL_O_OB_S_SE_SL_Traverser<T> extends LP_O_OB_S_SE_SL_Traverser<T> implements NL_SL_Traverser<T> { protected Stack<LabelledCounter> nestedLoops; - protected ReferenceMap loopNames = null; + protected ReferenceMap<String,Object> loopNames = null; protected LP_NL_O_OB_S_SE_SL_Traverser() { } @@@ -40,45 -36,17 +37,18 @@@ public LP_NL_O_OB_S_SE_SL_Traverser(final T t, final Step<T, ?> step) { super(t, step); this.nestedLoops = new Stack<>(); - this.loopNames = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.WEAK); + this.loopNames = new ReferenceMap<>(AbstractReferenceMap.ReferenceStrength.HARD, + AbstractReferenceMap.ReferenceStrength.WEAK); } - ///////////////// - - @Override - public int loops() { - return this.nestedLoops.peek().count(); - } - - @Override - public int loops(final String loopName) { - if (loopName == null) - return loops(); - else if (this.loopNames.containsKey(loopName)) - return ((LabelledCounter) this.loopNames.get(loopName)).count(); - else - throw new IllegalArgumentException("Loop name not defined: " + loopName); - } - - @Override - public Set<String> getLoopNames() { - return loopNames.keySet(); - } - @Override - public void initialiseLoops(final String stepLabel, final String loopName) { - if (this.nestedLoops.empty() || !this.nestedLoops.peek().hasLabel(stepLabel)) { - final LabelledCounter lc = new LabelledCounter(stepLabel, (short) 0); - this.nestedLoops.push(lc); - if (loopName != null) - this.loopNames.put(loopName, lc); - } + public Stack<LabelledCounter> getNestedLoops() { + return nestedLoops; } @Override - public void incrLoops() { - this.nestedLoops.peek().increment(); - public ReferenceMap getNestedLoopNames() { ++ public ReferenceMap<String,Object> getNestedLoopNames() { + return loopNames; } @Override @@@ -164,4 -98,10 +100,11 @@@ return result; } + private void cloneLoopState(final LP_NL_O_OB_S_SE_SL_Traverser<?> clone) { + clone.nestedLoops = new Stack<>(); - clone.loopNames = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.WEAK); ++ clone.loopNames = new ReferenceMap<>(AbstractReferenceMap.ReferenceStrength.HARD, ++ AbstractReferenceMap.ReferenceStrength.WEAK); + copyNestedLoops(clone.nestedLoops, clone.loopNames); + } + } diff --cc gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/NL_O_OB_S_SE_SL_Traverser.java index efcda13bdb,9b51ef08e6..92a2abb58a --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/NL_O_OB_S_SE_SL_Traverser.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/NL_O_OB_S_SE_SL_Traverser.java @@@ -19,20 -19,16 +19,17 @@@ package org.apache.tinkerpop.gremlin.process.traversal.traverser; + import java.util.Objects; + import java.util.Stack; -import org.apache.commons.collections.map.ReferenceMap; +import org.apache.commons.collections4.map.AbstractReferenceMap; +import org.apache.commons.collections4.map.ReferenceMap; import org.apache.tinkerpop.gremlin.process.traversal.Step; import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.LabelledCounter; - import java.util.Iterator; - import java.util.Map; - import java.util.Set; - import java.util.Stack; - - public class NL_O_OB_S_SE_SL_Traverser<T> extends O_OB_S_SE_SL_Traverser<T> { + public class NL_O_OB_S_SE_SL_Traverser<T> extends O_OB_S_SE_SL_Traverser<T> implements NL_SL_Traverser<T> { protected Stack<LabelledCounter> nestedLoops; - protected ReferenceMap loopNames; + protected ReferenceMap<String,Object> loopNames; protected NL_O_OB_S_SE_SL_Traverser() { } @@@ -40,40 -36,17 +37,18 @@@ public NL_O_OB_S_SE_SL_Traverser(final T t, final Step<T, ?> step) { super(t, step); this.nestedLoops = new Stack<>(); - this.loopNames = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.WEAK); + this.loopNames = new ReferenceMap<>(AbstractReferenceMap.ReferenceStrength.HARD, + AbstractReferenceMap.ReferenceStrength.WEAK); } - ///////////////// - - @Override - public int loops() { - return this.nestedLoops.peek().count(); - } - - @Override - public int loops(final String loopName) { - if (loopName == null) - return loops(); - else if (this.loopNames.containsKey(loopName)) - return ((LabelledCounter) this.loopNames.get(loopName)).count(); - else - throw new IllegalArgumentException("Loop name not defined: " + loopName); - } - @Override - public void initialiseLoops(final String stepLabel, final String loopName) { - if (this.nestedLoops.empty() || !this.nestedLoops.peek().hasLabel(stepLabel)) { - final LabelledCounter lc = new LabelledCounter(stepLabel, (short) 0); - this.nestedLoops.push(lc); - if (loopName != null) - this.loopNames.put(loopName, lc); - } + public Stack<LabelledCounter> getNestedLoops() { + return nestedLoops; } @Override - public void incrLoops() { - this.nestedLoops.peek().increment(); - public ReferenceMap getNestedLoopNames() { ++ public ReferenceMap<String,Object> getNestedLoopNames() { + return loopNames; } @Override @@@ -164,4 -98,10 +100,11 @@@ return result; } + private void cloneLoopState(final NL_O_OB_S_SE_SL_Traverser<?> clone) { + clone.nestedLoops = new Stack<>(); - clone.loopNames = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.WEAK); ++ clone.loopNames = new ReferenceMap<>(AbstractReferenceMap.ReferenceStrength.HARD, ++ AbstractReferenceMap.ReferenceStrength.WEAK); + copyNestedLoops(clone.nestedLoops, clone.loopNames); + } + } diff --cc gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/NL_SL_Traverser.java index 0000000000,e949adc043..8b863edc16 mode 000000,100644..100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/NL_SL_Traverser.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/NL_SL_Traverser.java @@@ -1,0 -1,248 +1,247 @@@ + /* + * 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.traverser; + + import java.util.HashMap; + import java.util.Map; + import java.util.Objects; + import java.util.Set; + import java.util.Stack; + import java.util.stream.Collectors; + import java.util.stream.Stream; -import org.apache.commons.collections.map.ReferenceMap; ++import org.apache.commons.collections4.map.ReferenceMap; + import org.apache.tinkerpop.gremlin.process.traversal.Traverser; + import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.LabelledCounter; + + import static org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement.NESTED_LOOP; + import static org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement.SINGLE_LOOP; + + /** + * Intermediate helper interface for {@link Traverser}s that support loops. Implementations are expected to override + * either the single or nested loop functions depending on the type of loop {@link TraverserRequirement} they are using. + * <p> + * Note: it would be nice to separate the {@link TraverserRequirement#SINGLE_LOOP} vs {@link TraverserRequirement#NESTED_LOOP} + * logic into different traverser interfaces in the future however the current {@link Traverser} class hierarchy makes + * that difficult as {@link Traverser}s that support nested loops extend from {@link Traverser}s that support single + * loops but override all single loop logic with nested loop logic. + */ + public interface NL_SL_Traverser<T> extends Traverser.Admin<T> { + /** + * @return the supported loop type - either {@link TraverserRequirement#NESTED_LOOP} or {@link TraverserRequirement#SINGLE_LOOP} + */ + TraverserRequirement getLoopRequirement(); + + // NESTED LOOPS + + /** + * Override this method to support nested loops. + * + * @return the {@link Stack} of {@link LabelledCounter} which holds the nested loops. + */ + default Stack<LabelledCounter> getNestedLoops() { + throw new UnsupportedOperationException(); + } + + /** + * Override this method to support nested loops. + * + * @return the {@link ReferenceMap} where key=loop name and value=reference to the {@link LabelledCounter}. + */ - default ReferenceMap getNestedLoopNames() { ++ default ReferenceMap<String,Object> getNestedLoopNames() { + throw new UnsupportedOperationException(); + } + + // SINGLE LOOPS + + /** + * Override this method to support single loop. + * + * @return the single loop count. + */ + default short getSingleLoopCount() { + throw new UnsupportedOperationException(); + } + + /** + * Override this method to support single loop. Sets the single loop count. + */ + default void setSingleLoopCount(short loops) { + throw new UnsupportedOperationException(); + } + + /** + * Override this method to support single loop. + * + * @return the single loop name. + */ + default String getSingleLoopName() { + throw new UnsupportedOperationException(); + } + + /** + * Override this method to support single loop. Sets the single loop name. + */ + default void setSingleLoopName(final String loopName) { + throw new UnsupportedOperationException(); + } + + /** + * Override this method to support single loop. + * + * @return the single loop step label. + */ + default String getSingleLoopStepLabel() { + throw new UnsupportedOperationException(); + } + + /** + * Override this method to support single loop. Sets the single loop step label. + */ + default void setSingleLoopStepLabel(final String stepLabel) { + throw new UnsupportedOperationException(); + } + + @Override + default void incrLoops() { + if (NESTED_LOOP.equals(getValidLoopRequirement())) { + getNestedLoops().peek().increment(); + } else { + setSingleLoopCount((short) (getSingleLoopCount() + 1)); + } + } + + /** + * @return the current loop count for single loops or the latest loop count for nested loops. + */ + @Override + default int loops() { + return loops(null); + } + + /** + * Obtain a loop count for a named loop or for the current loop count for single loops or the latest loop count for nested loops. + * + * @param loopName the optional name applied to the loop + * @return the count for the loop identified by the given loopName or current loop count for single loops or latest loop for nested loops. + */ + @Override + default int loops(final String loopName) { + if (NESTED_LOOP.equals(getValidLoopRequirement())) { + return getNestedLoopCount(loopName); + } else if (loopName == null || + Objects.equals(loopName, getSingleLoopName()) || + Objects.equals(loopName, getSingleLoopStepLabel())) { + return getSingleLoopCount(); + } + throw new IllegalArgumentException("Single loop name not defined: " + loopName); + } + + /** + * Initialises loops depending on the {@link #getLoopRequirement()} loop type. + * + * @param stepLabel the label of the step initialising the loops. + * @param loopName the optional user defined name for referencing the loop counter + */ + @Override + default void initialiseLoops(final String stepLabel, final String loopName) { + if (NESTED_LOOP.equals(getValidLoopRequirement())) { + initialiseNestedLoops(stepLabel, loopName); + } else { + setSingleLoopStepLabel(stepLabel); + setSingleLoopName(loopName); + } + } + + /** + * Resets loops depending on the {@link #getLoopRequirement()} loop type. + */ + @Override + default void resetLoops() { + if (NESTED_LOOP.equals(getValidLoopRequirement())) { + getNestedLoops().pop(); + } else { + setSingleLoopCount((short) 0); + } + } + + /** + * @return the loop names that can be used to reference loops. + */ + @Override + default Set<String> getLoopNames() { + if (NESTED_LOOP.equals(getValidLoopRequirement())) { + return getNestedLoopNames().keySet(); + } else { + return Stream.of(getSingleLoopName(), getSingleLoopStepLabel()) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + } + } + + default TraverserRequirement getValidLoopRequirement() { + final TraverserRequirement loopRequirement = getLoopRequirement(); + if (!SINGLE_LOOP.equals(loopRequirement) && !NESTED_LOOP.equals(loopRequirement)) { + throw new IllegalStateException("Invalid loop TraverserRequirement " + loopRequirement); + } + return loopRequirement; + } + + default void initialiseNestedLoops(final String stepLabel, final String loopName) { + final Stack<LabelledCounter> nestedLoops = getNestedLoops(); - final ReferenceMap loopNames = getNestedLoopNames(); ++ final ReferenceMap<String, Object> loopNames = getNestedLoopNames(); + if (nestedLoops.empty() || !nestedLoops.peek().hasLabel(stepLabel)) { + final LabelledCounter lc = new LabelledCounter(stepLabel, (short) 0); + nestedLoops.push(lc); + loopNames.put(stepLabel, lc); + if (loopName != null && !loopName.equals(stepLabel)) { + loopNames.put(loopName, lc); + } + } + } + + default int getNestedLoopCount(final String loopName) { + if (loopName == null) { + return getNestedLoops().peek().count(); + } else if (getNestedLoopNames().containsKey(loopName)) { + return ((LabelledCounter) getNestedLoopNames().get(loopName)).count(); + } else { + throw new IllegalArgumentException("Nested loop name not defined: " + loopName); + } + } + - default void copyNestedLoops(final Stack<LabelledCounter> targetNestedLoops, final ReferenceMap targetLoopNames) { ++ default void copyNestedLoops(final Stack<LabelledCounter> targetNestedLoops, final ReferenceMap<String, Object> targetLoopNames) { + final Map<LabelledCounter, LabelledCounter> counterMapping = new HashMap<>(); + + for (LabelledCounter original : getNestedLoops()) { + final LabelledCounter cloned = (LabelledCounter) original.clone(); + targetNestedLoops.push(cloned); + counterMapping.put(original, cloned); + } + + if (getNestedLoopNames() != null) { - for (Object entry : getNestedLoopNames().entrySet()) { - final ReferenceMap.Entry pair = (ReferenceMap.Entry) entry; ++ for (Map.Entry<String, Object> pair : getNestedLoopNames().entrySet()) { + final LabelledCounter original = (LabelledCounter) pair.getValue(); + final LabelledCounter cloned = counterMapping.get(original); + if (cloned != null) { + targetLoopNames.put(pair.getKey(), cloned); + } + } + } + } + }
