This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch TINKERPOP-2605
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git


The following commit(s) were added to refs/heads/TINKERPOP-2605 by this push:
     new 84b51d4  TINKERPOP-2598 Addressed NullPointerException with 
P.within/out
84b51d4 is described below

commit 84b51d455034dd665a89708e99bdf6a103ea040f
Author: Stephen Mallette <[email protected]>
AuthorDate: Wed Aug 25 10:38:39 2021 -0400

    TINKERPOP-2598 Addressed NullPointerException with P.within/out
---
 CHANGELOG.asciidoc                                 |  1 +
 .../tinkerpop/gremlin/process/traversal/P.java     | 20 ++++++++++++-----
 .../structure/io/binary/types/PSerializer.java     |  2 +-
 .../structure/io/gryo/GryoSerializersV1d0.java     |  2 +-
 .../structure/io/gryo/GryoSerializersV3d0.java     |  2 +-
 .../tinkerpop/gremlin/process/traversal/PTest.java | 26 ++++++++++++++++++++++
 .../graphson/GraphSONMapperEmbeddedTypeTest.java   |  4 ++--
 .../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs |  1 +
 .../GraphBinaryReaderWriterRoundTripTest.java      |  2 +-
 .../gremlin-javascript/test/cucumber/gremlin.js    |  1 +
 gremlin-python/src/main/python/radish/gremlin.py   |  1 +
 gremlin-test/features/filter/Has.feature           | 11 +++++++++
 12 files changed, 61 insertions(+), 12 deletions(-)

diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 825b951..9762fca 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -30,6 +30,7 @@ 
image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 * Deprecated `JsonBuilder` serialization for GraphSON and Gryo.
 * Allowed `null` string values in the Gremlin grammar.
 * Prevented exception with `hasLabel(null)` and instead filter away traversers 
as labels can't ever be null.
+* Improved handling of `null` when passed to `P` predicates.
 * Refined `DotNetTranslator` to be more explicit with `null` arguments to 
ensure that the right overloads are called.
 * Created `GremlinParser` to construct `Traversal` objects from 
`gremlin-language`.
 
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java
index 8454f33..a79231a 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java
@@ -204,38 +204,46 @@ public class P<V> implements Predicate<V>, Serializable, 
Cloneable {
     }
 
     /**
-     * Determines if a value is within the specified list of values.
+     * Determines if a value is within the specified list of values. If the 
array of arguments itself is {@code null}
+     * then the argument is treated as {@code Object[1]} where that single 
value is {@code null}.
      *
      * @since 3.0.0-incubating
      */
     public static <V> P<V> within(final V... values) {
-        return P.within(Arrays.asList(values));
+        final V[] v = null == values ? (V[]) new Object[] { null } : values;
+        return P.within(Arrays.asList(v));
     }
 
     /**
-     * Determines if a value is within the specified list of values.
+     * Determines if a value is within the specified list of values. Calling 
this with {@code null} is the same as
+     * calling {@link #within(Object[])} using {@code null}.
      *
      * @since 3.0.0-incubating
      */
     public static <V> P<V> within(final Collection<V> value) {
+        if (null == value) return P.within((V) null);
         return new P(Contains.within, value);
     }
 
     /**
-     * Determines if a value is not within the specified list of values.
+     * Determines if a value is not within the specified list of values. If 
the array of arguments itself is {@code null}
+     * then the argument is treated as {@code Object[1]} where that single 
value is {@code null}.
      *
      * @since 3.0.0-incubating
      */
     public static <V> P<V> without(final V... values) {
-        return P.without(Arrays.asList(values));
+        final V[] v = null == values ? (V[]) new Object[] { null } : values;
+        return P.without(Arrays.asList(v));
     }
 
     /**
-     * Deermines if a value is not within the specified list of values.
+     * Determines if a value is not within the specified list of values. 
Calling this with {@code null} is the same as
+     * calling {@link #within(Object[])} using {@code null}.
      *
      * @since 3.0.0-incubating
      */
     public static <V> P<V> without(final Collection<V> value) {
+        if (null == value) return P.without((V) null);
         return new P(Contains.without, value);
     }
 
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/PSerializer.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/PSerializer.java
index e5ea3e3..a44e41d 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/PSerializer.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/PSerializer.java
@@ -60,7 +60,7 @@ public class PSerializer<T extends P> extends 
SimpleTypeSerializer<T> {
         final Class<?>[] argumentClasses = new Class[length];
         for (int i = 0; i < length; i++) {
             args[i] = context.read(buffer);
-            argumentClasses[i] = args[i].getClass();
+            argumentClasses[i] = null == args[i] ? Object.class : 
args[i].getClass();
         }
                     
         if ("and".equals(predicateName))
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoSerializersV1d0.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoSerializersV1d0.java
index 7697462..33e535a 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoSerializersV1d0.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoSerializersV1d0.java
@@ -196,7 +196,7 @@ public final class GryoSerializersV1d0 {
             final boolean isCollection = input.readByte() == (byte) 0;
             final Object value;
             if (isCollection) {
-                value = new ArrayList();
+                value = new ArrayList<>();
                 final int size = input.readInt();
                 for (int ix = 0; ix < size; ix++) {
                     ((List) value).add(kryo.readClassAndObject(input));
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoSerializersV3d0.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoSerializersV3d0.java
index e35bd33..d45bdd9 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoSerializersV3d0.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoSerializersV3d0.java
@@ -312,7 +312,7 @@ public final class GryoSerializersV3d0 {
             final boolean isCollection = input.readByte() == (byte) 0;
             final Object value;
             if (isCollection) {
-                value = new ArrayList();
+                value = new ArrayList<>();
                 final int size = input.readInt();
                 for (int ix = 0; ix < size; ix++) {
                     ((List) value).add(kryo.readClassAndObject(input));
diff --git 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/PTest.java
 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/PTest.java
index f2e0868..f6ec0d9 100644
--- 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/PTest.java
+++ 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/PTest.java
@@ -52,8 +52,14 @@ public class PTest {
             return new ArrayList<>(Arrays.asList(new Object[][]{
                     {P.eq(0), 0, true},
                     {P.eq(0), 1, false},
+                    {P.eq(0), null, false},
+                    {P.eq(null), null, true},
+                    {P.eq(null), 0, false},
                     {P.neq(0), 0, false},
                     {P.neq(0), 1, true},
+                    {P.neq(0), null, true},
+                    {P.neq(null), null, false},
+                    {P.neq(null), 0, true},
                     {P.gt(0), -1, false},
                     {P.gt(0), 0, false},
                     {P.gt(0), 1, true},
@@ -78,15 +84,35 @@ public class PTest {
                     {P.outside(1, 10), 1, false},
                     {P.outside(1, 10), 9, false},
                     {P.outside(1, 10), 10, false},
+                    {P.within((Object) null), 0, false},
+                    {P.within((Object) null), null, true},
+                    {P.within((Collection) null), 0, false},
+                    {P.within((Collection) null), null, true},
+                    {P.within(null, 2, 3), 0, false},
+                    {P.within(null, 2, 3), null, true},
+                    {P.within(null, 2, 3), 2, true},
                     {P.within(1, 2, 3), 0, false},
                     {P.within(1, 2, 3), 1, true},
                     {P.within(1, 2, 3), 10, false},
+                    {P.within(Arrays.asList(null, 2, 3)), null, true},
+                    {P.within(Arrays.asList(null, 2, 3)), 1, false},
+                    {P.within(Arrays.asList(null, 2, 3)), 2, true},
                     {P.within(Arrays.asList(1, 2, 3)), 0, false},
                     {P.within(Arrays.asList(1, 2, 3)), 1, true},
                     {P.within(Arrays.asList(1, 2, 3)), 10, false},
+                    {P.without((Object) null), 0, true},
+                    {P.without((Object) null), null, false},
+                    {P.without((Collection) null), 0, true},
+                    {P.without((Collection) null), null, false},
+                    {P.without(null, 2, 3), 0, true},
+                    {P.without(null, 2, 3), null, false},
+                    {P.without(null, 2, 3), 2, false},
                     {P.without(1, 2, 3), 0, true},
                     {P.without(1, 2, 3), 1, false},
                     {P.without(1, 2, 3), 10, true},
+                    {P.without(Arrays.asList(null, 2, 3)), null, false},
+                    {P.without(Arrays.asList(null, 2, 3)), 1, true},
+                    {P.without(Arrays.asList(null, 2, 3)), 2, false},
                     {P.without(Arrays.asList(1, 2, 3)), 0, true},
                     {P.without(Arrays.asList(1, 2, 3)), 1, false},
                     {P.without(Arrays.asList(1, 2, 3)), 10, true},
diff --git 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperEmbeddedTypeTest.java
 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperEmbeddedTypeTest.java
index dd25ec9..3238480 100644
--- 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperEmbeddedTypeTest.java
+++ 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperEmbeddedTypeTest.java
@@ -373,7 +373,7 @@ public class GraphSONMapperEmbeddedTypeTest extends 
AbstractGraphSONTest {
     public void shouldHandlePMultiValue() throws Exception  {
         assumeThat(version, either(startsWith("v2")).or(startsWith("v3")));
 
-        final P o = P.within(1,2,3);
+        final P o = P.within(1,2,3,null);
         assertEquals(o, serializeDeserialize(mapper, o, P.class));
     }
 
@@ -389,7 +389,7 @@ public class GraphSONMapperEmbeddedTypeTest extends 
AbstractGraphSONTest {
     public void shouldHandlePMultiValueAsList() throws Exception  {
         assumeThat(version, either(startsWith("v2")).or(startsWith("v3")));
 
-        final P o = P.within(Arrays.asList(1,2,3));
+        final P o = P.within(Arrays.asList(1,2,3,null));
         assertEquals(o, serializeDeserialize(mapper, o, P.class));
     }
 
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs 
b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
index f9567a0..6bc762c 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
@@ -199,6 +199,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                
{"g_V_hasLabelXpersonX_hasXage_notXlteX10X_andXnotXbetweenX11_20XXXX_andXltX29X_orXeqX35XXXX_name",
 new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) 
=>g.V().HasLabel("person").Has("age",P.Gt(10).Or(P.Gte(11).And(P.Lt(20))).And(P.Lt(29).Or(P.Eq(35)))).Values<object>("name")}},
 
                {"g_V_in_hasIdXneqX1XX", new List<Func<GraphTraversalSource, 
IDictionary<string, object>, ITraversal>> {(g,p) 
=>g.V().In().HasId(p["xx1"])}}, 
                {"g_V_hasXage_withinX27X_count", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V().Has("age",P.Within(new List<object> {27})).Count()}}, 
+               {"g_V_hasXage_withinX27_nullX_count", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V().Has("age",P.Within(new List<object> {27, null})).Count()}}, 
                {"g_V_hasXage_withinX27_29X_count", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V().Has("age",P.Within(new List<object> {27, 29})).Count()}}, 
                {"g_V_hasXage_withoutX27X_count", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V().Has("age",P.Without(new List<object> {27})).Count()}}, 
                {"g_V_hasXage_withoutX27_29X_count", new 
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> 
{(g,p) =>g.V().Has("age",P.Without(new List<object> {27, 29})).Count()}}, 
diff --git 
a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/binary/GraphBinaryReaderWriterRoundTripTest.java
 
b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/binary/GraphBinaryReaderWriterRoundTripTest.java
index 5a351f1..3fd54e2 100644
--- 
a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/binary/GraphBinaryReaderWriterRoundTripTest.java
+++ 
b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/binary/GraphBinaryReaderWriterRoundTripTest.java
@@ -231,7 +231,7 @@ public class GraphBinaryReaderWriterRoundTripTest {
                 new Object[] {"Pand", P.gt(1).and(P.lt(2)), null},
                 new Object[] {"Por", P.gt(1).or(P.lt(2)), null},
                 new Object[] {"Pnot", P.not(P.lte(1)), null},
-                new Object[] {"Pwithout", P.without(1,2,3,4), null},
+                new Object[] {"Pwithout", P.without(1,2,3,4,null), null},
                 new Object[] {"Pinside", P.inside(0.0d, 0.6d), null},
                 new Object[] {"TextP", TextP.startingWith("mark"), null},
 
diff --git 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
index 13a5a24..a82873b 100644
--- 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
+++ 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
@@ -188,6 +188,7 @@ const gremlins = {
     
g_V_hasLabelXpersonX_hasXage_notXlteX10X_andXnotXbetweenX11_20XXXX_andXltX29X_orXeqX35XXXX_name:
 [function({g}) { return 
g.V().hasLabel("person").has("age",P.gt(10).or(P.gte(11).and(P.lt(20))).and(P.lt(29).or(P.eq(35)))).values("name")
 }], 
     g_V_in_hasIdXneqX1XX: [function({g, xx1}) { return g.V().in_().hasId(xx1) 
}], 
     g_V_hasXage_withinX27X_count: [function({g}) { return 
g.V().has("age",P.within([27])).count() }], 
+    g_V_hasXage_withinX27_nullX_count: [function({g}) { return 
g.V().has("age",P.within([27, null])).count() }], 
     g_V_hasXage_withinX27_29X_count: [function({g}) { return 
g.V().has("age",P.within([27, 29])).count() }], 
     g_V_hasXage_withoutX27X_count: [function({g}) { return 
g.V().has("age",P.without([27])).count() }], 
     g_V_hasXage_withoutX27_29X_count: [function({g}) { return 
g.V().has("age",P.without([27, 29])).count() }], 
diff --git a/gremlin-python/src/main/python/radish/gremlin.py 
b/gremlin-python/src/main/python/radish/gremlin.py
index 3974ce9..8b4100c 100644
--- a/gremlin-python/src/main/python/radish/gremlin.py
+++ b/gremlin-python/src/main/python/radish/gremlin.py
@@ -173,6 +173,7 @@ world.gremlins = {
     
'g_V_hasLabelXpersonX_hasXage_notXlteX10X_andXnotXbetweenX11_20XXXX_andXltX29X_orXeqX35XXXX_name':
 [(lambda 
g:g.V().hasLabel('person').has('age',P.gt(10).or_(P.gte(11).and_(P.lt(20))).and_(P.lt(29).or_(P.eq(35)))).name)],
 
     'g_V_in_hasIdXneqX1XX': [(lambda g, xx1=None:g.V().in_().hasId(xx1))], 
     'g_V_hasXage_withinX27X_count': [(lambda 
g:g.V().has('age',P.within([27])).count())], 
+    'g_V_hasXage_withinX27_nullX_count': [(lambda 
g:g.V().has('age',P.within([27,None])).count())], 
     'g_V_hasXage_withinX27_29X_count': [(lambda 
g:g.V().has('age',P.within([27,29])).count())], 
     'g_V_hasXage_withoutX27X_count': [(lambda 
g:g.V().has('age',P.without([27])).count())], 
     'g_V_hasXage_withoutX27_29X_count': [(lambda 
g:g.V().has('age',P.without([27,29])).count())], 
diff --git a/gremlin-test/features/filter/Has.feature 
b/gremlin-test/features/filter/Has.feature
index 3be3586..db78114 100644
--- a/gremlin-test/features/filter/Has.feature
+++ b/gremlin-test/features/filter/Has.feature
@@ -419,6 +419,17 @@ Feature: Step - has()
       | result |
       | d[1].l |
 
+  Scenario: g_V_hasXage_withinX27_nullX_count
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().has("age", P.within(27,null)).count()
+      """
+    When iterated to list
+    Then the result should be ordered
+      | result |
+      | d[1].l |
+
   Scenario: g_V_hasXage_withinX27_29X_count
     Given the modern graph
     And the traversal of

Reply via email to