This is an automated email from the ASF dual-hosted git repository. spmallette pushed a commit to branch TINKERPOP-2235 in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 41450c20ba936a05c2b2ee921446049809e0dadd Author: stephen <spmalle...@gmail.com> AuthorDate: Mon Nov 4 15:56:11 2019 -0500 TINKERPOP-2235 Allow null to work within Gremlin In previous versions null as a value was not allowed as a Traverser in a Traversal. It was part of a filter in DefaultTraversal that would automatically remove it from the stream. While this was a design choice for Gremlin to use null that way, it removed a bit of functionality from Gremlin that could have use at times. As it so happened, the use of null as a filter really didn't need to work that way as we already had a secondary filter which was even better for representing an Empty [...] --- .../tinkerpop/gremlin/jsr223/JavaTranslator.java | 2 +- .../tinkerpop/gremlin/process/traversal/Order.java | 8 ++++-- .../gremlin/process/traversal/Traverser.java | 9 ++++++- .../process/traversal/step/map/MapStep.java | 16 +++++++++++- .../process/traversal/step/map/SelectOneStep.java | 7 ++++- .../process/traversal/step/map/SelectStep.java | 6 +++++ .../traversal/step/map/TraversalMapStep.java | 6 +++++ .../process/traversal/step/util/AbstractStep.java | 5 ++-- .../traversal/traverser/B_O_S_SE_SL_Traverser.java | 2 +- .../traverser/util/AbstractTraverser.java | 7 ++--- .../traversal/traverser/util/EmptyTraverser.java | 9 ++++++- .../gremlin/process/traversal/OrderTest.java | 4 +++ .../traverser/util/EmptyTraverserTest.java} | 30 +++++++++------------- .../gremlin/util/iterator/ArrayIteratorTest.java | 18 +++++++++++-- .../groovy/jsr223/GroovyTranslatorProvider.java | 4 --- .../ParameterizedGroovyTranslatorProvider.java | 4 --- .../test/cucumber/feature-steps.js | 7 ++++- .../gremlin-javascript/test/unit/graphson-test.js | 5 ++++ .../gremlin_python/structure/io/graphbinaryV1.py | 4 +++ .../src/main/jython/radish/feature_steps.py | 6 ++++- .../tests/structure/io/test_graphbinaryV1.py | 9 +++++++ .../jython/tests/structure/io/test_graphsonV2d0.py | 4 +++ .../jython/tests/structure/io/test_graphsonV3d0.py | 4 +++ .../GraphBinaryRemoteGraphComputerProvider.java | 6 +---- .../GraphSONRemoteGraphComputerProvider.java | 6 +---- .../remote/GryoRemoteGraphComputerProvider.java | 6 +---- ...emoteGraphGroovyTranslatorComputerProvider.java | 6 +---- gremlin-test/features/sideEffect/Inject.feature | 14 ++++++++++ .../process/traversal/CoreTraversalTest.java | 12 --------- .../traversal/step/sideEffect/InjectTest.java | 14 ++++++++++ .../strategy/decoration/TranslationStrategy.java | 4 ++- ...tractTinkerGraphGraphSONTranslatorProvider.java | 4 --- .../io/gryo/TinkerGraphGryoTranslatorProvider.java | 4 --- 33 files changed, 168 insertions(+), 84 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 e71f112..515bc93 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 @@ -214,7 +214,7 @@ public final class JavaTranslator<S extends TraversalSource, T extends Traversal for (int i = 0; i < parameters.length; i++) { if (parameters[i].isVarArgs()) { final Class<?> parameterClass = parameters[i].getType().getComponentType(); - if (argumentsCopy.length > i && !parameterClass.isAssignableFrom(argumentsCopy[i].getClass())) { + if (argumentsCopy.length > i && argumentsCopy[i] != null && !parameterClass.isAssignableFrom(argumentsCopy[i].getClass())) { found = false; break; } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Order.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Order.java index 34a61a7..1176f54 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Order.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Order.java @@ -54,11 +54,13 @@ public enum Order implements Comparator<Object> { * @since 3.3.4 */ asc { + private final Comparator<Comparable> ascendingComparator = Comparator.nullsFirst(Comparator.<Comparable>naturalOrder()); + @Override public int compare(final Object first, final Object second) { return first instanceof Number && second instanceof Number ? NumberHelper.compare((Number) first, (Number) second) - : Comparator.<Comparable>naturalOrder().compare((Comparable) first, (Comparable) second); + : ascendingComparator.compare((Comparable) first, (Comparable) second); } @Override @@ -73,11 +75,13 @@ public enum Order implements Comparator<Object> { * @since 3.3.4 */ desc { + private final Comparator<Comparable> descendingComparator = Comparator.nullsLast(Comparator.<Comparable>reverseOrder()); + @Override public int compare(final Object first, final Object second) { return first instanceof Number && second instanceof Number ? NumberHelper.compare((Number) second, (Number) first) - : Comparator.<Comparable>reverseOrder().compare((Comparable) first, (Comparable) second); + : descendingComparator.compare((Comparable) first, (Comparable) second); } @Override diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Traverser.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Traverser.java index e07bec1..fc58c62 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Traverser.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Traverser.java @@ -18,9 +18,11 @@ */ package org.apache.tinkerpop.gremlin.process.traversal; +import org.apache.tinkerpop.gremlin.process.traversal.step.map.LoopsStep; import org.apache.tinkerpop.gremlin.structure.util.Attachable; import java.io.Serializable; +import java.util.Comparator; import java.util.Set; import java.util.function.Function; @@ -134,7 +136,12 @@ public interface Traverser<T> extends Serializable, Comparable<Traverser<T>>, Cl */ @Override public default int compareTo(final Traverser<T> other) throws ClassCastException { - return ((Comparable) this.get()).compareTo(other.get()); + final Object thisObj = this.get(); + final Object otherObj = other.get(); + if (thisObj == otherObj) return 0; + if (null == thisObj) return -1; + if (null == otherObj) return 1; + return ((Comparable) thisObj).compareTo(otherObj); } /** diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MapStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MapStep.java index 03e544e..2747b9c 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MapStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MapStep.java @@ -21,6 +21,7 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.map; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.process.traversal.Traverser; import org.apache.tinkerpop.gremlin.process.traversal.step.util.AbstractStep; +import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.EmptyTraverser; /** * @author Marko A. Rodriguez (http://markorodriguez.com) @@ -34,10 +35,23 @@ public abstract class MapStep<S, E> extends AbstractStep<S, E> { @Override protected Traverser.Admin<E> processNextStart() { final Traverser.Admin<S> traverser = this.starts.next(); - return traverser.split(this.map(traverser), this); + final E obj = this.map(traverser); + + // maybe looks tricky, but it's just a play on Java generics without having to tear apart every MapStep + // instance. basically just want to respect the fact that a subclass can return an EmptyTraverser from + // map() and if so, just return that empty. + return isEmptyTraverser(obj) ? EmptyTraverser.instance() : traverser.split(obj, this); } protected abstract E map(final Traverser.Admin<S> traverser); + /** + * Determines if the value returned from {@link #map(Traverser.Admin)} should be representative of an + * {@link EmptyTraverser}. Such traversers will effectively be filtered out by the traversal. + */ + protected boolean isEmptyTraverser(E obj) { + return false; + } + } 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 2c7aa82..98ee83f 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 @@ -26,7 +26,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.PathProcessor; 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.traverser.TraverserRequirement; -import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper; +import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.EmptyTraverser; import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil; import org.apache.tinkerpop.gremlin.structure.util.StringFactory; @@ -58,6 +58,11 @@ public final class SelectOneStep<S, E> extends MapStep<S, E> implements Traversa } @Override + protected boolean isEmptyTraverser(final E obj) { + return null == obj; + } + + @Override public String toString() { return StringFactory.stepString(this, this.pop, this.selectKey, this.selectTraversal); } 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 875cf93..db15a28 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 @@ -26,6 +26,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.PathProcessor; 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.traverser.TraverserRequirement; +import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.EmptyTraverser; import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper; import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalRing; import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil; @@ -77,6 +78,11 @@ public final class SelectStep<S, E> extends MapStep<S, Map<String, E>> implement } @Override + protected boolean isEmptyTraverser(final Map<String, E> obj) { + return null == obj; + } + + @Override public void reset() { super.reset(); this.traversalRing.reset(); diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TraversalMapStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TraversalMapStep.java index 896b833..32c2e3c 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TraversalMapStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/TraversalMapStep.java @@ -22,6 +22,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.process.traversal.Traverser; import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent; import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement; +import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.EmptyTraverser; import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil; import org.apache.tinkerpop.gremlin.structure.util.StringFactory; @@ -49,6 +50,11 @@ public final class TraversalMapStep<S, E> extends MapStep<S, E> implements Trave } @Override + protected boolean isEmptyTraverser(final E obj) { + return null == obj; + } + + @Override public List<Traversal.Admin<S, E>> getLocalChildren() { return Collections.singletonList(this.mapTraversal); } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/AbstractStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/AbstractStep.java index e2757e2..fe49e56 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/AbstractStep.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/AbstractStep.java @@ -21,6 +21,7 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.util; import org.apache.tinkerpop.gremlin.process.traversal.Step; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.process.traversal.Traverser; +import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.EmptyTraverser; import org.apache.tinkerpop.gremlin.process.traversal.util.EmptyTraversal; import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalInterruptedException; import org.apache.tinkerpop.gremlin.structure.util.StringFactory; @@ -126,7 +127,7 @@ public abstract class AbstractStep<S, E> implements Step<S, E> { while (true) { if (Thread.interrupted()) throw new TraversalInterruptedException(); final Traverser.Admin<E> traverser = this.processNextStart(); - if (null != traverser.get() && 0 != traverser.bulk()) + if (traverser.bulk() > 0) return this.prepareTraversalForNextStep(traverser); } } @@ -141,7 +142,7 @@ public abstract class AbstractStep<S, E> implements Step<S, E> { while (true) { if (Thread.interrupted()) throw new TraversalInterruptedException(); this.nextEnd = this.processNextStart(); - if (null != this.nextEnd.get() && 0 != this.nextEnd.bulk()) + if (this.nextEnd.bulk() > 0) return true; else this.nextEnd = null; diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_O_S_SE_SL_Traverser.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_O_S_SE_SL_Traverser.java index 33c5520..57fa47a 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_O_S_SE_SL_Traverser.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/B_O_S_SE_SL_Traverser.java @@ -136,7 +136,7 @@ public class B_O_S_SE_SL_Traverser<T> extends B_O_Traverser<T> { protected final boolean equals(final B_O_S_SE_SL_Traverser other) { return super.equals(other) && other.loops == this.loops - && (this.loopName != null ? this.loopName.equals(other.loopName) : other.loopName == null) + && Objects.equals(this.loopName, other.loopName) && !carriesUnmergeableSack(); } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/AbstractTraverser.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/AbstractTraverser.java index 658ba4b..e76016a 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/AbstractTraverser.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/AbstractTraverser.java @@ -27,6 +27,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.util.EmptyTraversalSideEff import org.apache.tinkerpop.gremlin.structure.util.Attachable; import org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceFactory; +import java.util.Objects; import java.util.Set; import java.util.function.Function; @@ -203,16 +204,16 @@ public abstract class AbstractTraverser<T> implements Traverser<T>, Traverser.Ad @Override public int hashCode() { - return this.t.hashCode(); + return Objects.hashCode(this.t); } @Override public boolean equals(final Object object) { - return object instanceof AbstractTraverser && ((AbstractTraverser) object).get().equals(this.t); + return object instanceof AbstractTraverser && Objects.equals(this.t, ((AbstractTraverser) object).t); } @Override public String toString() { - return this.t.toString(); + return Objects.toString(this.t); } } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/EmptyTraverser.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/EmptyTraverser.java index 3aeafbd..c68fdc6 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/EmptyTraverser.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/EmptyTraverser.java @@ -20,6 +20,7 @@ package org.apache.tinkerpop.gremlin.process.traversal.traverser.util; import org.apache.tinkerpop.gremlin.process.traversal.Path; import org.apache.tinkerpop.gremlin.process.traversal.Step; +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.process.traversal.TraversalSideEffects; import org.apache.tinkerpop.gremlin.process.traversal.Traverser; import org.apache.tinkerpop.gremlin.process.traversal.step.util.EmptyPath; @@ -30,12 +31,18 @@ import java.util.Set; import java.util.function.Function; /** + * A {@link Traverser} with no bulk which effectively means that it will no longer be propagated through a + * {@link Traversal}. + * * @author Marko A. Rodriguez (http://markorodriguez.com) */ public final class EmptyTraverser<T> implements Traverser<T>, Traverser.Admin<T> { private static final EmptyTraverser INSTANCE = new EmptyTraverser(); + /** + * The empty {@link Traverser} instance. + */ public static <R> EmptyTraverser<R> instance() { return INSTANCE; } @@ -95,7 +102,7 @@ public final class EmptyTraverser<T> implements Traverser<T>, Traverser.Admin<T> } @Override - public void setBulk(long count) { + public void setBulk(final long count) { } diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/OrderTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/OrderTest.java index 2e5f2e1..5726f5d 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/OrderTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/OrderTest.java @@ -42,13 +42,17 @@ public class OrderTest { @Parameterized.Parameters(name = "{0}.test({1},{2})") public static Iterable<Object[]> data() throws ParseException { return new ArrayList<>(Arrays.asList(new Object[][]{ + {Order.asc, Arrays.asList("a", "c", null, "d"), Arrays.asList(null, "a", "c", "d")}, {Order.asc, Arrays.asList("b", "a", "c", "d"), Arrays.asList("a", "b", "c", "d")}, {Order.desc, Arrays.asList("b", "a", "c", "d"), Arrays.asList("d", "c", "b", "a")}, + {Order.desc, Arrays.asList("c", "a", null, "d"), Arrays.asList("d", "c", "a", null)}, {Order.asc, Arrays.asList(formatter.parse("1-Jan-2018"), formatter.parse("1-Jan-2020"), formatter.parse("1-Jan-2008")), Arrays.asList(formatter.parse("1-Jan-2008"), formatter.parse("1-Jan-2018"), formatter.parse("1-Jan-2020"))}, {Order.desc, Arrays.asList(formatter.parse("1-Jan-2018"), formatter.parse("1-Jan-2020"), formatter.parse("1-Jan-2008")), Arrays.asList(formatter.parse("1-Jan-2020"), formatter.parse("1-Jan-2018"), formatter.parse("1-Jan-2008"))}, + {Order.desc, Arrays.asList(100L, 1L, null, -1L, 0L), Arrays.asList(100L, 1L, 0L, -1L, null)}, {Order.desc, Arrays.asList(100L, 1L, -1L, 0L), Arrays.asList(100L, 1L, 0L, -1L)}, + {Order.asc, Arrays.asList(100L, 1L, null, -1L, 0L), Arrays.asList(null, -1L, 0L, 1L, 100L)}, {Order.asc, Arrays.asList(100.1f, 1.1f, -1.1f, 0.1f), Arrays.asList(-1.1f, 0.1f, 1.1f, 100.1f)}, {Order.desc, Arrays.asList(100.1f, 1.1f, -1.1f, 0.1f), Arrays.asList(100.1f, 1.1f, 0.1f, -1.1f)}, {Order.asc, Arrays.asList(100.1d, 1.1d, -1.1d, 0.1d), Arrays.asList(-1.1d, 0.1d, 1.1d, 100.1d)}, diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MapStep.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/EmptyTraverserTest.java similarity index 52% copy from gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MapStep.java copy to gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/EmptyTraverserTest.java index 03e544e..da41900 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MapStep.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/EmptyTraverserTest.java @@ -16,28 +16,22 @@ * 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.traverser.util; -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.AbstractStep; +import org.junit.Test; -/** - * @author Marko A. Rodriguez (http://markorodriguez.com) - */ -public abstract class MapStep<S, E> extends AbstractStep<S, E> { +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; - public MapStep(final Traversal.Admin traversal) { - super(traversal); - } +public class EmptyTraverserTest { - @Override - protected Traverser.Admin<E> processNextStart() { - final Traverser.Admin<S> traverser = this.starts.next(); - return traverser.split(this.map(traverser), this); + @Test + public void shouldHaveSameInstance() { + assertSame(EmptyTraverser.instance(), EmptyTraverser.instance()); } - protected abstract E map(final Traverser.Admin<S> traverser); - + @Test + public void shouldHaveZeroBulk() { + assertEquals(0, EmptyTraverser.instance().bulk()); + } } - diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/util/iterator/ArrayIteratorTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/util/iterator/ArrayIteratorTest.java index 60ddd88..32f7401 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/util/iterator/ArrayIteratorTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/util/iterator/ArrayIteratorTest.java @@ -23,8 +23,7 @@ import org.junit.Test; import java.util.Iterator; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; +import static org.junit.Assert.*; /** * @author Stephen Mallette (http://stephen.genoprime.com) @@ -45,6 +44,21 @@ public class ArrayIteratorTest { assertFalse(itty.hasNext()); } + @Test + public void shouldIterateAnArrayWithNull() { + final String[] arr = new String[3]; + arr[0] = "test1"; + arr[1] = "test2"; + arr[2] = null; + + final Iterator<String> itty = new ArrayIterator<>(arr); + assertEquals("test1", itty.next()); + assertEquals("test2", itty.next()); + assertNull(itty.next()); + + assertFalse(itty.hasNext()); + } + @Test(expected = FastNoSuchElementException.class) public void shouldThrowFastNoSuchElementException() { final Iterator<String> itty = new ArrayIterator<>(new String[0]); diff --git a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslatorProvider.java b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslatorProvider.java index 5f556b8..b4d2452 100644 --- a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslatorProvider.java +++ b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslatorProvider.java @@ -207,10 +207,6 @@ import org.apache.tinkerpop.gremlin.util.TinkerGraphProvider; method = "shouldNeverPropagateANoBulkTraverser", reason = "Reason requires investigation") @Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.process.traversal.CoreTraversalTest", - method = "shouldNeverPropagateANullValuedTraverser", - reason = "Reason requires investigation") -@Graph.OptOut( test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.ReadTest", method = "*", reason = "read and write tests don't translate locally well because of calling iterate() inside read()/write() add a none()") diff --git a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/ParameterizedGroovyTranslatorProvider.java b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/ParameterizedGroovyTranslatorProvider.java index 2e60cba..56466a5 100644 --- a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/ParameterizedGroovyTranslatorProvider.java +++ b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/ParameterizedGroovyTranslatorProvider.java @@ -207,10 +207,6 @@ import org.apache.tinkerpop.gremlin.util.TinkerGraphProvider; method = "shouldNeverPropagateANoBulkTraverser", reason = "Reason requires investigation") @Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.process.traversal.CoreTraversalTest", - method = "shouldNeverPropagateANullValuedTraverser", - reason = "Reason requires investigation") -@Graph.OptOut( test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.ReadTest", method = "*", reason = "read and write tests don't translate locally well because of calling iterate() inside read()/write() add a none()") diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js index fed551d..d85d678 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js @@ -223,6 +223,10 @@ function parseRow(row) { } function parseValue(stringValue) { + + if(stringValue === "null") + return null; + let extractedValue = null; let parser = null; for (let item of parsers) { @@ -234,6 +238,7 @@ function parseValue(stringValue) { break; } } + return parser !== null ? parser.call(this, extractedValue) : stringValue; } @@ -279,7 +284,7 @@ function toT(value) { } function toDirection(value) { - return direction[value.toLowerCase()]; + return direction[value.toLowerCase()]; } function toArray(stringList) { diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/graphson-test.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/graphson-test.js index f355fb5..2942d01 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/graphson-test.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/graphson-test.js @@ -32,6 +32,11 @@ const GraphSONWriter = gs.GraphSONWriter; const P = t.P; describe('GraphSONReader', function () { + it('should parse GraphSON null', function () { + const reader = new GraphSONReader(); + const result = reader.read(null); + assert.equal(result, null); + }); it('should parse GraphSON int32, float and double to Number from GraphSON', function () { const reader = new GraphSONReader(); [ diff --git a/gremlin-python/src/main/jython/gremlin_python/structure/io/graphbinaryV1.py b/gremlin-python/src/main/jython/gremlin_python/structure/io/graphbinaryV1.py index c06ee12..b5aca52 100644 --- a/gremlin-python/src/main/jython/gremlin_python/structure/io/graphbinaryV1.py +++ b/gremlin-python/src/main/jython/gremlin_python/structure/io/graphbinaryV1.py @@ -156,6 +156,10 @@ class GraphBinaryWriter(object): if to_extend is None: to_extend = bytearray() + if obj is None: + to_extend.extend(NULL_BYTES) + return + try: t = type(obj) diff --git a/gremlin-python/src/main/jython/radish/feature_steps.py b/gremlin-python/src/main/jython/radish/feature_steps.py index 441a3fb..6514f3c 100644 --- a/gremlin-python/src/main/jython/radish/feature_steps.py +++ b/gremlin-python/src/main/jython/radish/feature_steps.py @@ -38,6 +38,7 @@ regex_or = re.compile(r"([(.,\s])or\(") regex_with = re.compile(r"([(.,\s])with\(") regex_true = re.compile(r"(true)") regex_false = re.compile(r"(false)") +regex_null = re.compile(r"(null)") outV = __.outV label = __.label @@ -182,6 +183,8 @@ def _convert(val, ctx): return T[val[2:-1]] elif isinstance(val, str) and re.match("^D\[.*\]$", val): # parse instance of Direction enum return Direction[val[2:-1]] + elif isinstance(val, str) and re.match("^null$", val): # parse null to None + return None else: return val @@ -247,7 +250,8 @@ def _translate(traversal_): replaced = regex_in.sub(r"\1in_(", replaced) replaced = regex_with.sub(r"\1with_(", replaced) replaced = regex_true.sub(r"True", replaced) - return regex_false.sub(r"False", replaced) + replaced = regex_false.sub(r"False", replaced) + return regex_null.sub(r"None", replaced) def _make_traversal(g, traversal_string, params): diff --git a/gremlin-python/src/main/jython/tests/structure/io/test_graphbinaryV1.py b/gremlin-python/src/main/jython/tests/structure/io/test_graphbinaryV1.py index a2320bc..7416a94 100644 --- a/gremlin-python/src/main/jython/tests/structure/io/test_graphbinaryV1.py +++ b/gremlin-python/src/main/jython/tests/structure/io/test_graphbinaryV1.py @@ -44,6 +44,10 @@ class TestGraphSONWriter(object): graphbinary_writer = GraphBinaryWriter() graphbinary_reader = GraphBinaryReader() + def test_null(self): + c = self.graphbinary_reader.readObject(self.graphbinary_writer.writeObject(None)) + assert c is None + def test_int(self): x = 100 output = self.graphbinary_reader.readObject(self.graphbinary_writer.writeObject(x)) @@ -101,6 +105,11 @@ class TestGraphSONWriter(object): output = self.graphbinary_reader.readObject(self.graphbinary_writer.writeObject(x)) assert x == output + def test_heterogeneous_list_with_none(self): + x = ["serialize this!", 0, "serialize that!", "serialize that!", 1, "stop telling me what to serialize", 2, None] + output = self.graphbinary_reader.readObject(self.graphbinary_writer.writeObject(x)) + assert x == output + def test_homogeneous_set(self): x = {"serialize this!", "serialize that!", "stop telling me what to serialize"} output = self.graphbinary_reader.readObject(self.graphbinary_writer.writeObject(x)) diff --git a/gremlin-python/src/main/jython/tests/structure/io/test_graphsonV2d0.py b/gremlin-python/src/main/jython/tests/structure/io/test_graphsonV2d0.py index 1f54736..cebe29c 100644 --- a/gremlin-python/src/main/jython/tests/structure/io/test_graphsonV2d0.py +++ b/gremlin-python/src/main/jython/tests/structure/io/test_graphsonV2d0.py @@ -289,6 +289,10 @@ class TestGraphSONReader(object): assert isinstance(c, SingleChar) assert chr(76) == c + def test_null(self): + c = self.graphson_reader.readObject(json.dumps(None)) + assert c is None + class TestGraphSONWriter(object): graphson_writer = GraphSONWriter() diff --git a/gremlin-python/src/main/jython/tests/structure/io/test_graphsonV3d0.py b/gremlin-python/src/main/jython/tests/structure/io/test_graphsonV3d0.py index d68e5be..9366937 100644 --- a/gremlin-python/src/main/jython/tests/structure/io/test_graphsonV3d0.py +++ b/gremlin-python/src/main/jython/tests/structure/io/test_graphsonV3d0.py @@ -336,6 +336,10 @@ class TestGraphSONReader(object): assert isinstance(c, SingleChar) assert chr(76) == c + def test_null(self): + c = self.graphson_reader.readObject(json.dumps(None)) + assert c is None + class TestGraphSONWriter(object): graphson_writer = GraphSONWriter() diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/GraphBinaryRemoteGraphComputerProvider.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/GraphBinaryRemoteGraphComputerProvider.java index 2133a0b..57d0a28 100644 --- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/GraphBinaryRemoteGraphComputerProvider.java +++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/GraphBinaryRemoteGraphComputerProvider.java @@ -85,11 +85,7 @@ import org.apache.tinkerpop.gremlin.tinkergraph.process.computer.TinkerGraphComp reason = "Local traversals may not traverse past the local star-graph on GraphComputer") @Graph.OptOut( test = "org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.InjectTest", - method = "g_VX1X_injectXg_VX4XX_out_name", - reason = "The inject() step is not supported by GraphComputer") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.InjectTest", - method = "g_VX1X_out_injectXv2X_name", + method = "*", reason = "The inject() step is not supported by GraphComputer") @Graph.OptOut( test = "org.apache.tinkerpop.gremlin.process.traversal.step.branch.LocalTest", diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/GraphSONRemoteGraphComputerProvider.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/GraphSONRemoteGraphComputerProvider.java index 7b80c2c..100aef6 100644 --- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/GraphSONRemoteGraphComputerProvider.java +++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/GraphSONRemoteGraphComputerProvider.java @@ -85,11 +85,7 @@ import org.apache.tinkerpop.gremlin.tinkergraph.process.computer.TinkerGraphComp reason = "Local traversals may not traverse past the local star-graph on GraphComputer") @Graph.OptOut( test = "org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.InjectTest", - method = "g_VX1X_injectXg_VX4XX_out_name", - reason = "The inject() step is not supported by GraphComputer") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.InjectTest", - method = "g_VX1X_out_injectXv2X_name", + method = "*", reason = "The inject() step is not supported by GraphComputer") @Graph.OptOut( test = "org.apache.tinkerpop.gremlin.process.traversal.step.branch.LocalTest", diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/GryoRemoteGraphComputerProvider.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/GryoRemoteGraphComputerProvider.java index 1269322..ba0ccda 100644 --- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/GryoRemoteGraphComputerProvider.java +++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/GryoRemoteGraphComputerProvider.java @@ -86,11 +86,7 @@ import org.apache.tinkerpop.gremlin.tinkergraph.process.computer.TinkerGraphComp reason = "Local traversals may not traverse past the local star-graph on GraphComputer") @Graph.OptOut( test = "org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.InjectTest", - method = "g_VX1X_injectXg_VX4XX_out_name", - reason = "The inject() step is not supported by GraphComputer") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.InjectTest", - method = "g_VX1X_out_injectXv2X_name", + method = "*", reason = "The inject() step is not supported by GraphComputer") @Graph.OptOut( test = "org.apache.tinkerpop.gremlin.process.traversal.step.branch.LocalTest", diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GryoRemoteGraphGroovyTranslatorComputerProvider.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GryoRemoteGraphGroovyTranslatorComputerProvider.java index 77fa5f6..5cfe2fe 100644 --- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GryoRemoteGraphGroovyTranslatorComputerProvider.java +++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GryoRemoteGraphGroovyTranslatorComputerProvider.java @@ -88,11 +88,7 @@ import org.apache.tinkerpop.gremlin.tinkergraph.process.computer.TinkerGraphComp reason = "Local traversals may not traverse past the local star-graph on GraphComputer") @Graph.OptOut( test = "org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.InjectTest", - method = "g_VX1X_injectXg_VX4XX_out_name", - reason = "The inject() step is not supported by GraphComputer") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.InjectTest", - method = "g_VX1X_out_injectXv2X_name", + method = "*", reason = "The inject() step is not supported by GraphComputer") @Graph.OptOut( test = "org.apache.tinkerpop.gremlin.process.traversal.step.branch.LocalTest", diff --git a/gremlin-test/features/sideEffect/Inject.feature b/gremlin-test/features/sideEffect/Inject.feature index 740928d..db5a26c 100644 --- a/gremlin-test/features/sideEffect/Inject.feature +++ b/gremlin-test/features/sideEffect/Inject.feature @@ -66,3 +66,17 @@ Feature: Step - inject() | lop | | vadas | | josh | + + Scenario: g_injectXnull_1_3_nullX + Given the empty graph + And the traversal of + """ + g.inject(null, 1, 3, null) + """ + When iterated to list + Then the result should be unordered + | result | + | null | + | d[1].i | + | d[3].i | + | null | \ No newline at end of file diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/CoreTraversalTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/CoreTraversalTest.java index 04417a0..92794c5 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/CoreTraversalTest.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/CoreTraversalTest.java @@ -78,18 +78,6 @@ public class CoreTraversalTest extends AbstractGremlinProcessTest { } @Test - @LoadGraphWith - public void shouldNeverPropagateANullValuedTraverser() { - try { - assertFalse(g.V().map(t -> null).hasNext()); - assertEquals(0, g.V().map(t -> null).toList().size()); - g.V().map(t -> null).sideEffect(t -> fail("this should not have happened")).iterate(); - } catch (VerificationException e) { - // its okay if lambdas can't be serialized by the test suite - } - } - - @Test @LoadGraphWith(MODERN) public void shouldFilterOnIterate() { final Traversal<Vertex,String> traversal = g.V().out().out().<String>values("name").aggregate("x").iterate(); diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/InjectTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/InjectTest.java index d3fc1ed..0b3b584 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/InjectTest.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/InjectTest.java @@ -48,6 +48,8 @@ public abstract class InjectTest extends AbstractGremlinProcessTest { public abstract Traversal<Vertex, String> get_g_VX1X_injectXg_VX4XX_out_name(final Object v1Id, final Object v4Id); + public abstract Traversal<Integer, Integer> get_g_injectXnull_1_3_nullX(); + @Test @LoadGraphWith(MODERN) public void g_VX1X_out_injectXv2X_name() { @@ -85,6 +87,13 @@ public abstract class InjectTest extends AbstractGremlinProcessTest { } @Test + public void g_injectXnull_1_3_nullX() { + final Traversal<Integer, Integer> traversal = get_g_injectXnull_1_3_nullX(); + printTraversalForm(traversal); + checkResults(Arrays.asList(null, 1, 3, null), traversal); + } + + @Test @LoadGraphWith(MODERN) public void g_VX1X_injectXg_VX4XX_out_name() { final Traversal<Vertex, String> traversal = get_g_VX1X_injectXg_VX4XX_out_name(convertToVertexId("marko"), convertToVertexId("josh")); @@ -108,5 +117,10 @@ public abstract class InjectTest extends AbstractGremlinProcessTest { public Traversal<Vertex, String> get_g_VX1X_injectXg_VX4XX_out_name(final Object v1Id, final Object v4Id) { return g.V(v1Id).inject(g.V(v4Id).next()).out().values("name"); } + + @Override + public Traversal<Integer, Integer> get_g_injectXnull_1_3_nullX() { + return g.inject(null, 1, 3, null); + } } } diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/TranslationStrategy.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/TranslationStrategy.java index fcecf9e..0897cc6 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/TranslationStrategy.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/TranslationStrategy.java @@ -134,7 +134,9 @@ public final class TranslationStrategy extends AbstractTraversalStrategy<Travers final Object[] newArgs = new Object[args.length]; for (int i = 0; i < args.length; i++) { - if (args[i].equals("knows")) + if (args[i] == null) + newArgs[i] = null; + else if (args[i].equals("knows")) newArgs[i] = new Bytecode.Binding<>("a", "knows"); else if (args[i].equals("created")) newArgs[i] = new Bytecode.Binding<>("b", "created"); diff --git a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/io/graphson/AbstractTinkerGraphGraphSONTranslatorProvider.java b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/io/graphson/AbstractTinkerGraphGraphSONTranslatorProvider.java index 4d4ee00..7f4ebe8 100644 --- a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/io/graphson/AbstractTinkerGraphGraphSONTranslatorProvider.java +++ b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/io/graphson/AbstractTinkerGraphGraphSONTranslatorProvider.java @@ -213,10 +213,6 @@ import org.apache.tinkerpop.gremlin.tinkergraph.process.computer.TinkerGraphComp method = "shouldNeverPropagateANoBulkTraverser", reason = "Reason requires investigation") @Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.process.traversal.CoreTraversalTest", - method = "shouldNeverPropagateANullValuedTraverser", - reason = "Reason requires investigation") -@Graph.OptOut( test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.ReadTest", method = "*", reason = "read and write tests don't translate locally well because of calling iterate() inside read()/write() add a none()") diff --git a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/io/gryo/TinkerGraphGryoTranslatorProvider.java b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/io/gryo/TinkerGraphGryoTranslatorProvider.java index d6d93a0..0d30df9 100644 --- a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/io/gryo/TinkerGraphGryoTranslatorProvider.java +++ b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/io/gryo/TinkerGraphGryoTranslatorProvider.java @@ -209,10 +209,6 @@ import org.apache.tinkerpop.gremlin.tinkergraph.TinkerGraphProvider; method = "shouldNeverPropagateANoBulkTraverser", reason = "Reason requires investigation") @Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.process.traversal.CoreTraversalTest", - method = "shouldNeverPropagateANullValuedTraverser", - reason = "Reason requires investigation") -@Graph.OptOut( test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.ReadTest", method = "*", reason = "read and write tests don't translate locally well because of calling iterate() inside read()/write() add a none()")