You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by sp...@apache.org on 2019/11/06 14:46:15 UTC

[tinkerpop] branch TINKERPOP-2235 updated (222b72d -> 0880743)

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

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


 discard 222b72d  TINKERPOP-2235 Added upgrade and reference docs
 discard 289f9a8  TINKERPOP-2235 Exposed the configuration option for null as a feature
 discard 36888dc  TINKERPOP-2235 Added tests and Graph level null support
 discard 6ee6dd1  TINKERPOP-2235 Fixed null handling in .NET
 discard c6cff4e  TINKERPOP-2235 Fix C# gherkin test harness
 discard 41450c2  TINKERPOP-2235 Allow null to work within Gremlin
     add 82c2f5f  Fixed binary NOTICE files for Netty CTR
     add 78665fb  Merge branch 'tp34'
     add 6fbb53e  Deprecated TraversalStrategies.applyStrategies()
     add 47e25aa  Merge branch 'tp33' into tp34
     add e53c17f  Merge branch 'tp34'
     add 3fe3ca6  Add Divij Vaidya to the list of contributors. CTR
     add a403f83  minor format CTR
     add 8e3e990  TINKERPOP-1568 Changed order of strategy application.
     add ccd7e2a  TINKERPOP-1568 Refactored strategy application a bit.
     add fb83588  TINKERPOP-1568 Added Traversal.isRoot()
     add b7eb1de  TINKERPOP-1568 Changed when side-effects were set to child traversals in strategy application
     add 0bbe258  TINKERPOP-1568 Add some docs around TraversalStrategy application revisions
     add 172b77b  Merge pull request #1211 from apache/TINKERPOP-1568
     new b3e3a92  TINKERPOP-2235 Allow null to work within Gremlin
     new 38f52c3  TINKERPOP-2235 Fix C# gherkin test harness
     new 6634336  TINKERPOP-2235 Fixed null handling in .NET
     new d787431  TINKERPOP-2235 Added tests and Graph level null support
     new 8e4dd6b  TINKERPOP-2235 Exposed the configuration option for null as a feature
     new 0880743  TINKERPOP-2235 Added upgrade and reference docs

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (222b72d)
            \
             N -- N -- N   refs/heads/TINKERPOP-2235 (0880743)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 6 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 CHANGELOG.asciidoc                                 |  3 +
 docs/site/home/index.html                          |  1 +
 docs/src/upgrade/release-3.5.x.asciidoc            | 50 ++++++++++++++++
 gremlin-console/src/main/static/NOTICE             |  2 +-
 .../computer/traversal/TraversalVertexProgram.java |  2 +-
 .../strategy/decoration/VertexProgramStrategy.java |  5 +-
 .../MessagePassingReductionStrategy.java           | 22 +++++++-
 .../strategy/decoration/RemoteStrategy.java        |  2 +-
 .../gremlin/process/traversal/Bytecode.java        |  4 ++
 .../gremlin/process/traversal/Traversal.java       | 25 +++++---
 .../process/traversal/TraversalStrategies.java     | 16 +++---
 .../strategy/decoration/RequirementsStrategy.java  |  2 +-
 .../strategy/decoration/SackStrategy.java          |  2 +-
 .../strategy/decoration/SideEffectStrategy.java    |  2 +-
 .../strategy/decoration/SubgraphStrategy.java      | 13 +----
 .../strategy/finalization/ProfileStrategy.java     |  4 +-
 .../optimization/IncidentToAdjacentStrategy.java   |  2 +-
 .../optimization/InlineFilterStrategy.java         |  2 +-
 .../optimization/PathRetractionStrategy.java       |  2 +-
 .../process/traversal/util/DefaultTraversal.java   | 66 ++++++++++++++--------
 .../traversal/util/DefaultTraversalStrategies.java | 14 ++---
 .../traversal/util/EmptyTraversalStrategies.java   | 10 ++--
 .../traversal/util/TraversalExplanation.java       |  2 +-
 .../process/traversal/util/TraversalHelper.java    | 12 ++--
 .../optimization/InlineFilterStrategyTest.java     |  3 +-
 gremlin-server/src/main/static/NOTICE              |  2 +-
 .../process/traversal/step/ComplexTest.java        | 26 ++++-----
 .../strategy/decoration/TranslationStrategy.java   |  2 +-
 .../process/traversal/strategy/SparqlStrategy.java |  2 +-
 .../optimization/TinkerGraphCountStrategy.java     |  2 +-
 30 files changed, 202 insertions(+), 100 deletions(-)


[tinkerpop] 06/06: TINKERPOP-2235 Added upgrade and reference docs

Posted by sp...@apache.org.
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 088074305cac29ad2064aba1eafcfe7a05873e87
Author: stephen <sp...@gmail.com>
AuthorDate: Tue Nov 5 16:05:11 2019 -0500

    TINKERPOP-2235 Added upgrade and reference docs
---
 CHANGELOG.asciidoc                                 |  3 +
 .../reference/implementations-tinkergraph.asciidoc |  1 +
 docs/src/upgrade/release-3.5.x.asciidoc            | 74 ++++++++++++++++++++--
 3 files changed, 72 insertions(+), 6 deletions(-)

diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 50fa442..e17ad32 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -25,6 +25,9 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 
 This release also includes changes from <<release-3-4-3, 3.4.3>>.
 
+* Allowed the possibility for the user of `null` in Gremlin.
+* Added a `Graph.Feature` for `supportsNullPropertyValues`.
+* Modified TinkerGraph to support `null` property values and can be configured to disable that feature.
 * Modified `null` handling in mutations to be consistent for a new `Vertex` as well as update to an existing one.
 * Upgraded to Apache Commons Configuration2.
 * Renamed `StoreStep` to `AggregateLocalStep`.
diff --git a/docs/src/reference/implementations-tinkergraph.asciidoc b/docs/src/reference/implementations-tinkergraph.asciidoc
index fe14d0f..e919bd4 100644
--- a/docs/src/reference/implementations-tinkergraph.asciidoc
+++ b/docs/src/reference/implementations-tinkergraph.asciidoc
@@ -121,6 +121,7 @@ TinkerGraph has several settings that can be provided on creation via `Configura
 |gremlin.tinkergraph.edgeIdManager |The `IdManager` implementation to use for edges.
 |gremlin.tinkergraph.vertexPropertyIdManager |The `IdManager` implementation to use for vertex properties.
 |gremlin.tinkergraph.defaultVertexPropertyCardinality |The default `VertexProperty.Cardinality` to use when `Vertex.property(k,v)` is called.
+|gremlin.tinkergraph.allowNullPropertyValues |A boolean value that determines whether or not `null` property values are allowed and defaults to `true`.
 |gremlin.tinkergraph.graphLocation |The path and file name for where TinkerGraph should persist the graph data. If a
 value is specified here, the `gremlin.tinkergraph.graphFormat` should also be specified.  If this value is not
 included (default), then the graph will stay in-memory and not be loaded/persisted to disk.
diff --git a/docs/src/upgrade/release-3.5.x.asciidoc b/docs/src/upgrade/release-3.5.x.asciidoc
index 7372107..9e75caa 100644
--- a/docs/src/upgrade/release-3.5.x.asciidoc
+++ b/docs/src/upgrade/release-3.5.x.asciidoc
@@ -60,7 +60,6 @@ org.apache.commons.configuration.*
 
 to
 
-
 [source,text]
 ----
 org.apache.commons.configuration2.*
@@ -73,10 +72,61 @@ upgrade information can be found in the link:https://commons.apache.org/proper/c
 
 See: link:https://issues.apache.org/jira/browse/TINKERPOP-2185[TINKERPOP-2185]
 
-==== addV() and null
+==== Use of null
+
+Gremlin has traditionally disallowed `null` as a value in traversals and not always in consistent ways:
+
+[source,text]
+----
+gremlin> g.inject(1, null, null, 2, null)
+java.lang.NullPointerException
+Type ':help' or ':h' for help.
+Display stack trace? [yN]n
+gremlin> g.V().has('person','name','marko').property('age', null)
+The AddPropertyStep does not have a provided value: AddPropertyStep({key=[age]})
+Type ':help' or ':h' for help.
+Display stack trace? [yN]
+gremlin> g.addV("person").property("name", 'stephen').property("age", null)
+==>v[13]
+gremlin> g.V().has('person','name','stephen').elementMap()
+==>[id:13,label:person,name:stephen]
+gremlin> g.V().constant(null)
+gremlin>
+----
+
+Note how `null` can produce exception behavior or act as a filter. For 3.5.0, TinkerPop has not only made `null` usage
+consistent, but has also made it an allowable value within a `Traversal`:
+
+[source,text]
+----
+gremlin> g.inject(1, null, null, 2, null)
+==>1
+==>null
+==>null
+==>null
+==>2
+gremlin> g.addV("person").property("name", 'stephen').property("age", null)
+==>v[13]
+gremlin> g.V().has('person','name','stephen').elementMap()
+==>[id:13,label:person,name:stephen,age:null]
+gremlin> g.V().has('person','age',null)
+==>v[13]
+gremlin> g.V().constant(null)
+==>null
+==>null
+==>null
+==>null
+==>null
+==>null
+----
+
+Note that the above examples use TinkerGraph which now supports `null` as a property value (though it can be configured
+to work in the prior fashion) and all graphs may not support this feature (for example, Neo4j does not). Please be
+sure to check the new `supportsNullPropertyValues()` feature (or their documentation) to determine if the `Graph`
+implementation allows `null` in this same fashion.
 
-There was a bit of inconsistency in the handling of `null` in calls to `property()` depending on the type of mutation
-being executed demonstrated as follows in earlier versions:
+As a final consideration, there was a bit of inconsistency in the handling of `null` in calls to `property()`
+depending on the type of mutation being executed demonstrated as follows in earlier versions:
 
 [source,text]
 ----
@@ -91,7 +141,8 @@ gremlin> g.V(13).properties()
 ==>vp[z->2]
 ----
 
-In this release, this behavior has been altered to become consistent as follows:
+In this release, this behavior has been altered to become consistent. First, assuming `null` is not supported as a
+property value:
 
 [source,text]
 ----
@@ -105,7 +156,18 @@ Type ':help' or ':h' for help.
 Display stack trace? [yN]
 ----
 
-See: link:https://issues.apache.org/jira/browse/TINKERPOP-2099[TINKERPOP-2099]
+Then, assuming `null` is supported as a property value:
+
+[source,text]
+----
+gremlin> g.V(1).property("x", 1).property("y", null).property("z", 2)
+==>v[1]
+gremlin> g.addV().property("x", 1).property("y", null).property("z", 2)
+==>v[15]
+----
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-2235[TINKERPOP-2235],
+link:https://issues.apache.org/jira/browse/TINKERPOP-2099[TINKERPOP-2099]
 
 ==== Remote SideEffects
 


[tinkerpop] 03/06: TINKERPOP-2235 Fixed null handling in .NET

Posted by sp...@apache.org.
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 6634336de6a1428cb0c417c9d7189df0fb751e5a
Author: stephen <sp...@gmail.com>
AuthorDate: Tue Nov 5 08:26:25 2019 -0500

    TINKERPOP-2235 Fixed null handling in .NET
---
 .../src/Gremlin.Net/Process/Traversal/Bytecode.cs      |  6 ++++++
 .../Structure/IO/GraphSON/GraphSONWriter.cs            | 18 +++++++++++-------
 .../Structure/IO/GraphSON/GraphSONReaderTests.cs       |  4 ++--
 .../Structure/IO/GraphSON/GraphSONWriterTests.cs       |  4 ++--
 4 files changed, 21 insertions(+), 11 deletions(-)

diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Bytecode.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Bytecode.cs
index e09c533..490e4e4 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Bytecode.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Bytecode.cs
@@ -116,6 +116,12 @@ namespace Gremlin.Net.Process.Traversal
                 if (variable != null)
                     return new Binding(variable, ConvertArgument(argument, false));
             }
+
+            if (null == argument)
+            {
+                return null;
+            }
+            
             if (IsDictionaryType(argument.GetType()))
             {
                 var dict = new Dictionary<object, object>();
diff --git a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/GraphSONWriter.cs b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/GraphSONWriter.cs
index 3268ae4..fc7765f 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/GraphSONWriter.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/GraphSONWriter.cs
@@ -113,13 +113,17 @@ namespace Gremlin.Net.Structure.IO.GraphSON
         /// <returns>A GraphSON representation of the object ready to be serialized.</returns>
         public dynamic ToDict(dynamic objectData)
         {
-            var type = objectData.GetType();
-            if (TryGetSerializerFor(out IGraphSONSerializer serializer, type))
-                return serializer.Dictify(objectData, this);
-            if (IsDictionaryType(type))
-                return DictToGraphSONDict(objectData);
-            if (IsCollectionType(type))
-                return CollectionToGraphSONCollection(objectData);
+            if (objectData != null)
+            {
+                var type = objectData.GetType();
+                if (TryGetSerializerFor(out IGraphSONSerializer serializer, type))
+                    return serializer.Dictify(objectData, this);
+                if (IsDictionaryType(type))
+                    return DictToGraphSONDict(objectData);
+                if (IsCollectionType(type))
+                    return CollectionToGraphSONCollection(objectData);
+            }
+
             return objectData;
         }
 
diff --git a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/GraphSONReaderTests.cs b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/GraphSONReaderTests.cs
index 3a49143..15bd5f9 100644
--- a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/GraphSONReaderTests.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/GraphSONReaderTests.cs
@@ -260,13 +260,13 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphSON
         [Theory, MemberData(nameof(Versions))]
         public void ShouldDeserializeList(int version)
         {
-            var serializedValue = "[{\"@type\":\"g:Int32\",\"@value\":5},{\"@type\":\"g:Int32\",\"@value\":6}]";
+            var serializedValue = "[{\"@type\":\"g:Int32\",\"@value\":5},{\"@type\":\"g:Int32\",\"@value\":6},null]";
             var reader = CreateStandardGraphSONReader(version);
 
             var jObject = JArray.Parse(serializedValue);
             var deserializedValue = reader.ToObject(jObject);
 
-            Assert.Equal(new List<object> {5, 6}, deserializedValue);
+            Assert.Equal(new List<object> {5, 6, null}, deserializedValue);
         }
 
         [Theory, MemberData(nameof(Versions))]
diff --git a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/GraphSONWriterTests.cs b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/GraphSONWriterTests.cs
index 409776b..2eb7d51 100644
--- a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/GraphSONWriterTests.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/GraphSONWriterTests.cs
@@ -282,12 +282,12 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphSON
         public void ShouldSerializeGList(int version)
         {
             var writer = CreateGraphSONWriter(version);
-            var list = new List<object> {5, 6};
+            var list = new List<object> {5, 6, null};
 
             var serializedGraphSON = writer.WriteObject(list);
 
             var expectedGraphSON = "{\"@type\":\"g:List\",\"@value\":[{\"@type\":\"g:Int32\",\"@value\":5}," +
-                                   "{\"@type\":\"g:Int32\",\"@value\":6}]}";
+                                   "{\"@type\":\"g:Int32\",\"@value\":6},null]}";
             Assert.Equal(expectedGraphSON, serializedGraphSON);
         }
 


[tinkerpop] 01/06: TINKERPOP-2235 Allow null to work within Gremlin

Posted by sp...@apache.org.
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 b3e3a92d30a401b767d195b9d8537241624f9966
Author: stephen <sp...@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 d2d63b2..c8451bb 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()")


[tinkerpop] 05/06: TINKERPOP-2235 Exposed the configuration option for null as a feature

Posted by sp...@apache.org.
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 8e4dd6beaf95f42c6ea34c9f97ce6f19fe0c4fe7
Author: stephen <sp...@gmail.com>
AuthorDate: Tue Nov 5 16:04:37 2019 -0500

    TINKERPOP-2235 Exposed the configuration option for null as a feature
---
 .../gremlin/tinkergraph/structure/TinkerGraph.java        | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraph.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraph.java
index 7e2cf46..16282ee 100644
--- a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraph.java
+++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraph.java
@@ -389,6 +389,11 @@ public final class TinkerGraph implements Graph {
         }
 
         @Override
+        public boolean supportsNullPropertyValues() {
+            return allowNullPropertyValues;
+        }
+
+        @Override
         public Features.VertexPropertyFeatures properties() {
             return vertexPropertyFeatures;
         }
@@ -415,6 +420,11 @@ public final class TinkerGraph implements Graph {
         }
 
         @Override
+        public boolean supportsNullPropertyValues() {
+            return allowNullPropertyValues;
+        }
+
+        @Override
         public boolean supportsCustomIds() {
             return false;
         }
@@ -453,6 +463,11 @@ public final class TinkerGraph implements Graph {
         }
 
         @Override
+        public boolean supportsNullPropertyValues() {
+            return allowNullPropertyValues;
+        }
+
+        @Override
         public boolean supportsCustomIds() {
             return false;
         }


[tinkerpop] 04/06: TINKERPOP-2235 Added tests and Graph level null support

Posted by sp...@apache.org.
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 d787431de0b1af303ccd451d96873db7acdae6c5
Author: stephen <sp...@gmail.com>
AuthorDate: Tue Nov 5 15:26:13 2019 -0500

    TINKERPOP-2235 Added tests and Graph level null support
    
    The problem that reared its head here is in JavaTranslator (and likely GroovyTranslator) as well where the overloads of Gremlin steps become ambiguous when null is used. Left a big blob comment in JavaTranslator where I came up with a possibly reasonable solution for right now. Added a Graph Feature for null support in property values. Allowed TinkerGraph to support it by default but added a configuration to turn that support off. Neo4j does not support nulls so that feature is disabl [...]
---
 .../tinkerpop/gremlin/jsr223/JavaTranslator.java   | 17 ++++-
 .../gremlin/process/computer/VertexComputeKey.java |  2 +-
 .../apache/tinkerpop/gremlin/structure/Graph.java  | 18 +++++
 .../gremlin/structure/util/ElementHelper.java      | 14 ++--
 .../gremlin/structure/util/star/StarGraph.java     | 16 ++---
 .../gremlin/structure/util/ElementHelperTest.java  | 23 +++---
 gremlin-test/features/map/AddEdge.feature          | 28 ++++++++
 gremlin-test/features/map/AddVertex.feature        | 25 +++++++
 gremlin-test/features/map/Constant.feature         | 17 ++++-
 .../process/traversal/step/map/AddEdgeTest.java    | 30 ++++++++
 .../process/traversal/step/map/AddVertexTest.java  | 24 +++++++
 .../process/traversal/step/map/ConstantTest.java   | 15 +++-
 .../tinkerpop/gremlin/structure/PropertyTest.java  | 84 +++++++++++++++++++---
 .../gremlin/neo4j/structure/Neo4jEdge.java         |  2 +-
 .../gremlin/neo4j/structure/Neo4jGraph.java        | 12 +++-
 .../gremlin/neo4j/structure/Neo4jVertex.java       |  4 +-
 .../process/computer/TinkerGraphComputerView.java  |  2 +-
 .../gremlin/tinkergraph/structure/TinkerEdge.java  |  4 +-
 .../gremlin/tinkergraph/structure/TinkerGraph.java |  5 +-
 .../tinkergraph/structure/TinkerHelper.java        |  2 +-
 .../gremlin/tinkergraph/structure/TinkerIndex.java | 48 +++++++++----
 .../tinkergraph/structure/TinkerProperty.java      |  5 +-
 .../tinkergraph/structure/TinkerVertex.java        |  6 +-
 .../structure/TinkerVertexProperty.java            |  7 +-
 24 files changed, 346 insertions(+), 64 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 515bc93..bb9273c 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
@@ -226,13 +226,24 @@ public final class JavaTranslator<S extends TraversalSource, T extends Traversal
                                 newArguments[i] = varArgs;
                                 break;
                             } else {
-                                if (i < argumentsCopy.length &&
-                                        (parameters[i].getType().isAssignableFrom(argumentsCopy[i].getClass()) ||
+                                // try to detect the right method by comparing the type of the parameter to the type
+                                // of the argument. doesn't always work so well because of null arguments which don't
+                                // bring their type in bytecode and rely on position. this doesn't seem to happen often
+                                // ...luckily...because method signatures tend to be sufficiently unique and do not
+                                // encourage whacky use - like g.V().has(null, null) is clearly invalid so we don't
+                                // even need to try to sort that out. on the other hand g.V().has('name',null) which
+                                // is valid hits like four different possible overloads, but we can rely on the most
+                                // generic one which takes Object as the second parameter. that seems to work in this
+                                // case, but it's a shame this isn't nicer. seems like nicer would mean a heavy
+                                // overhaul to Gremlin or to GLVs/bytecode and/or to serialization mechanisms
+                                if (i < argumentsCopy.length && ((null == argumentsCopy[i] && parameters[i].getType() == Object.class) ||
+                                        (argumentsCopy[i] != null && (
+                                        parameters[i].getType().isAssignableFrom(argumentsCopy[i].getClass()) ||
                                                 (parameters[i].getType().isPrimitive() &&
                                                         (Number.class.isAssignableFrom(argumentsCopy[i].getClass()) ||
                                                                 argumentsCopy[i].getClass().equals(Boolean.class) ||
                                                                 argumentsCopy[i].getClass().equals(Byte.class) ||
-                                                                argumentsCopy[i].getClass().equals(Character.class))))) {
+                                                                argumentsCopy[i].getClass().equals(Character.class))))))) {
                                     newArguments[i] = argumentsCopy[i];
                                 } else {
                                     found = false;
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/VertexComputeKey.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/VertexComputeKey.java
index 284430a..072ad59 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/VertexComputeKey.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/VertexComputeKey.java
@@ -37,7 +37,7 @@ public final class VertexComputeKey implements Serializable {
     private VertexComputeKey(final String key, final boolean isTransient) {
         this.key = key;
         this.isTransient = isTransient;
-        ElementHelper.validateProperty(key, key);
+        ElementHelper.validateProperty(true, key, key);
     }
 
     public String getKey() {
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/Graph.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/Graph.java
index 42fee1f..22ae658 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/Graph.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/Graph.java
@@ -682,6 +682,15 @@ public interface Graph extends AutoCloseable, Host {
             public static final String FEATURE_ANY_IDS = "AnyIds";
             public static final String FEATURE_ADD_PROPERTY = "AddProperty";
             public static final String FEATURE_REMOVE_PROPERTY = "RemoveProperty";
+            public static final String FEATURE_NULL_PROPERTY_VALUES = "NullPropertyValues";
+
+            /**
+             * Determines if an {@link Element} allows properties with {@code null} property values.
+             */
+            @FeatureDescriptor(name = FEATURE_NULL_PROPERTY_VALUES)
+            public default boolean supportsNullPropertyValues() {
+                return true;
+            }
 
             /**
              * Determines if an {@link Element} allows properties to be added.  This feature is set independently from
@@ -816,6 +825,15 @@ public interface Graph extends AutoCloseable, Host {
             public static final String FEATURE_UUID_IDS = "UuidIds";
             public static final String FEATURE_CUSTOM_IDS = "CustomIds";
             public static final String FEATURE_ANY_IDS = "AnyIds";
+            public static final String FEATURE_NULL_PROPERTY_VALUES = "NullPropertyValues";
+
+            /**
+             * Determines if meta-properties allow for {@code null} property values.
+             */
+            @FeatureDescriptor(name = FEATURE_NULL_PROPERTY_VALUES)
+            public default boolean supportsNullPropertyValues() {
+                return true;
+            }
 
             /**
              * Determines if a {@link VertexProperty} allows properties to be removed.
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/ElementHelper.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/ElementHelper.java
index 829880b..fb18296 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/ElementHelper.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/ElementHelper.java
@@ -87,13 +87,14 @@ public final class ElementHelper {
      * Determines whether the property key/value for the specified thing can be legally set. This is typically used as
      * a pre-condition check prior to setting a property.
      *
+     * @param allowNullValue true if {@code null} is allowed as a value to the property
      * @param key   the key of the property
-     * @param value the value of the property
+     * @param value the value of the property\
      * @throws IllegalArgumentException whether the key/value pair is legal and if not, a clear reason exception
      *                                  message is provided
      */
-    public static void validateProperty(final String key, final Object value) throws IllegalArgumentException {
-        if (null == value)
+    public static void validateProperty(final boolean allowNullValue, final String key, final Object value) throws IllegalArgumentException {
+        if (!allowNullValue && null == value)
             throw Property.Exceptions.propertyValueCanNotBeNull();
         if (null == key)
             throw Property.Exceptions.propertyKeyCanNotBeNull();
@@ -105,19 +106,20 @@ public final class ElementHelper {
 
     /**
      * Determines whether a list of key/values are legal, ensuring that there are an even number of values submitted
-     * and that the key values in the list of arguments are {@link String} or {@link org.apache.tinkerpop.gremlin.structure.Element} objects.
+     * and that the key values in the list of arguments are {@link String} or {@link Element} objects.
      *
+     * @param allowNullValues true if {@code null} is allowed as a value to the property
      * @param propertyKeyValues a list of key/value pairs
      * @throws IllegalArgumentException if something in the pairs is illegal
      */
-    public static void legalPropertyKeyValueArray(final Object... propertyKeyValues) throws IllegalArgumentException {
+    public static void legalPropertyKeyValueArray(final boolean allowNullValues, final Object... propertyKeyValues) throws IllegalArgumentException {
         if (propertyKeyValues.length % 2 != 0)
             throw Element.Exceptions.providedKeyValuesMustBeAMultipleOfTwo();
         for (int i = 0; i < propertyKeyValues.length; i = i + 2) {
             if (!(propertyKeyValues[i] instanceof String) && !(propertyKeyValues[i] instanceof T))
                 throw Element.Exceptions.providedKeyValuesMustHaveALegalKeyOnEvenIndices();
 
-            if (null == propertyKeyValues[i + 1]) {
+            if (!allowNullValues && null == propertyKeyValues[i + 1]) {
                 throw Property.Exceptions.propertyValueCanNotBeNull();
             }
         }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/star/StarGraph.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/star/StarGraph.java
index 3bcdb7d..935ebef 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/star/StarGraph.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/star/StarGraph.java
@@ -95,7 +95,7 @@ public final class StarGraph implements Graph, Serializable {
     @Override
     public Vertex addVertex(final Object... keyValues) {
         if (null == this.starVertex) {
-            ElementHelper.legalPropertyKeyValueArray(keyValues);
+            ElementHelper.legalPropertyKeyValueArray(true, keyValues);
             this.starVertex = new StarVertex(ElementHelper.getIdValue(keyValues).orElse(this.nextId()), ElementHelper.getLabelValue(keyValues).orElse(Vertex.DEFAULT_LABEL));
             ElementHelper.attachProperties(this.starVertex, VertexProperty.Cardinality.list, keyValues); // TODO: is this smart? I say no... cause vertex property ids are not preserved.
             return this.starVertex;
@@ -413,14 +413,14 @@ public final class StarGraph implements Graph, Serializable {
 
         @Override
         public <V> VertexProperty<V> property(final String key, final V value, final Object... keyValues) {
-            ElementHelper.validateProperty(key, value);
-            ElementHelper.legalPropertyKeyValueArray(keyValues);
+            ElementHelper.validateProperty(true, key, value);
+            ElementHelper.legalPropertyKeyValueArray(true, keyValues);
             return this.property(VertexProperty.Cardinality.single, key, value, keyValues);
         }
 
         Edge addOutEdge(final String label, final Vertex inVertex, final Object... keyValues) {
             ElementHelper.validateLabel(label);
-            ElementHelper.legalPropertyKeyValueArray(keyValues);
+            ElementHelper.legalPropertyKeyValueArray(true, keyValues);
             if (null == this.outEdges)
                 this.outEdges = new HashMap<>();
             List<Edge> outE = this.outEdges.get(label);
@@ -436,7 +436,7 @@ public final class StarGraph implements Graph, Serializable {
 
         Edge addInEdge(final String label, final Vertex outVertex, final Object... keyValues) {
             ElementHelper.validateLabel(label);
-            ElementHelper.legalPropertyKeyValueArray(keyValues);
+            ElementHelper.legalPropertyKeyValueArray(true, keyValues);
             if (null == this.inEdges)
                 this.inEdges = new HashMap<>();
             List<Edge> inE = this.inEdges.get(label);
@@ -452,7 +452,7 @@ public final class StarGraph implements Graph, Serializable {
 
         @Override
         public <V> VertexProperty<V> property(final VertexProperty.Cardinality cardinality, final String key, V value, final Object... keyValues) {
-            ElementHelper.legalPropertyKeyValueArray(keyValues);
+            ElementHelper.legalPropertyKeyValueArray(true, keyValues);
             if (null == this.vertexProperties)
                 this.vertexProperties = new HashMap<>();
             final List<VertexProperty> list = cardinality.equals(VertexProperty.Cardinality.single) ? new ArrayList<>(1) : this.vertexProperties.getOrDefault(key, new ArrayList<>());
@@ -645,7 +645,7 @@ public final class StarGraph implements Graph, Serializable {
 
         @Override
         public <U> Property<U> property(final String key, final U value) {
-            ElementHelper.validateProperty(key, value);
+            ElementHelper.validateProperty(true, key, value);
             if (null == metaProperties)
                 metaProperties = new HashMap<>();
             Map<String, Object> properties = metaProperties.get(this.id);
@@ -760,7 +760,7 @@ public final class StarGraph implements Graph, Serializable {
 
         @Override
         public <V> Property<V> property(final String key, final V value) {
-            ElementHelper.validateProperty(key, value);
+            ElementHelper.validateProperty(true, key, value);
             if (null == edgeProperties)
                 edgeProperties = new HashMap<>();
             Map<String, Object> properties = edgeProperties.get(this.id);
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/util/ElementHelperTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/util/ElementHelperTest.java
index de774da..b466788 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/util/ElementHelperTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/util/ElementHelperTest.java
@@ -56,7 +56,7 @@ public class ElementHelperTest {
     @Test
     public void shouldValidatePropertyAndNotAllowNullValue() {
         try {
-            ElementHelper.validateProperty("test", null);
+            ElementHelper.validateProperty(false,"test", null);
             fail("Should fail as property value cannot be null");
         } catch (IllegalArgumentException iae) {
             assertEquals(Property.Exceptions.propertyValueCanNotBeNull().getMessage(), iae.getMessage());
@@ -64,9 +64,14 @@ public class ElementHelperTest {
     }
 
     @Test
+    public void shouldValidatePropertyAndAllowNullValue() {
+        ElementHelper.validateProperty(true,"test", null);
+    }
+
+    @Test
     public void shouldValidatePropertyAndNotAllowNullKey() {
         try {
-            ElementHelper.validateProperty(null, "test");
+            ElementHelper.validateProperty(false,null, "test");
             fail("Should fail as property key cannot be null");
         } catch (IllegalArgumentException iae) {
             assertEquals(Property.Exceptions.propertyKeyCanNotBeNull().getMessage(), iae.getMessage());
@@ -76,7 +81,7 @@ public class ElementHelperTest {
     @Test
     public void shouldValidatePropertyAndNotAllowEmptyKey() {
         try {
-            ElementHelper.validateProperty("", "test");
+            ElementHelper.validateProperty(false,"", "test");
             fail("Should fail as property key cannot be empty");
         } catch (IllegalArgumentException iae) {
             assertEquals(Property.Exceptions.propertyKeyCanNotBeEmpty().getMessage(), iae.getMessage());
@@ -87,7 +92,7 @@ public class ElementHelperTest {
     public void shouldValidatePropertyAndNotAllowHiddenKey() {
         final String key = Graph.Hidden.hide("key");
         try {
-            ElementHelper.validateProperty(key, "test");
+            ElementHelper.validateProperty(false, key, "test");
             fail("Should fail as property key cannot be hidden");
         } catch (IllegalArgumentException iae) {
             assertEquals(Property.Exceptions.propertyKeyCanNotBeAHiddenKey(key).getMessage(), iae.getMessage());
@@ -96,13 +101,13 @@ public class ElementHelperTest {
 
     @Test
     public void shouldHaveValidProperty() {
-        ElementHelper.validateProperty("aKey", "value");
+        ElementHelper.validateProperty(false,"aKey", "value");
     }
 
     @Test
     public void shouldAllowEvenNumberOfKeyValues() {
         try {
-            ElementHelper.legalPropertyKeyValueArray("aKey", "test", "no-value-for-this-one");
+            ElementHelper.legalPropertyKeyValueArray(false,"aKey", "test", "no-value-for-this-one");
             fail("Should fail as there is an odd number of key-values");
         } catch (IllegalArgumentException iae) {
             assertEquals(Element.Exceptions.providedKeyValuesMustBeAMultipleOfTwo().getMessage(), iae.getMessage());
@@ -112,7 +117,7 @@ public class ElementHelperTest {
     @Test
     public void shouldNotAllowEvenNumberOfKeyValuesAndInvalidKeys() {
         try {
-            ElementHelper.legalPropertyKeyValueArray("aKey", "test", "value-for-this-one", 1, 1, "none");
+            ElementHelper.legalPropertyKeyValueArray(false,"aKey", "test", "value-for-this-one", 1, 1, "none");
             fail("Should fail as there is an even number of key-values, but a bad key");
         } catch (IllegalArgumentException iae) {
             assertEquals(Element.Exceptions.providedKeyValuesMustHaveALegalKeyOnEvenIndices().getMessage(), iae.getMessage());
@@ -121,13 +126,13 @@ public class ElementHelperTest {
 
     @Test
     public void shouldAllowEvenNumberOfKeyValuesAndValidKeys() {
-        ElementHelper.legalPropertyKeyValueArray("aKey", "test", "value-for-this-one", 1, "1", "none");
+        ElementHelper.legalPropertyKeyValueArray(false,"aKey", "test", "value-for-this-one", 1, "1", "none");
     }
 
     @Test
     public void shouldNotAllowEvenNumberOfKeyValuesAndInvalidValues() {
         try {
-            ElementHelper.legalPropertyKeyValueArray("aKey", "test", "value-for-this-one", 1, "1", null);
+            ElementHelper.legalPropertyKeyValueArray(false,"aKey", "test", "value-for-this-one", 1, "1", null);
         } catch (IllegalArgumentException iae) {
             assertEquals(Property.Exceptions.propertyValueCanNotBeNull().getMessage(), iae.getMessage());
         }
diff --git a/gremlin-test/features/map/AddEdge.feature b/gremlin-test/features/map/AddEdge.feature
index 7f4b0aa..875e474 100644
--- a/gremlin-test/features/map/AddEdge.feature
+++ b/gremlin-test/features/map/AddEdge.feature
@@ -72,6 +72,34 @@ Feature: Step - addE()
     And the graph should return 4 for count of "g.V(v1Id).bothE()"
     And the graph should return 1 for count of "g.V(v1Id).inE().has(\"weight\", 2.0)"
 
+  Scenario: g_VX1X_asXaX_outXcreatedX_addEXcreatedByX_toXaX_propertyXweight_nullX
+    Given the empty graph
+    And the graph initializer of
+      """
+      g.addV("person").property(T.id, 1).property("name", "marko").property("age", 29).as("marko").
+        addV("person").property(T.id, 2).property("name", "vadas").property("age", 27).as("vadas").
+        addV("software").property(T.id, 3).property("name", "lop").property("lang", "java").as("lop").
+        addV("person").property(T.id, 4).property("name","josh").property("age", 32).as("josh").
+        addV("software").property(T.id, 5).property("name", "ripple").property("lang", "java").as("ripple").
+        addV("person").property(T.id, 6).property("name", "peter").property("age", 35).as('peter').
+        addE("knows").from("marko").to("vadas").property(T.id, 7).property("weight", 0.5).
+        addE("knows").from("marko").to("josh").property(T.id, 8).property("weight", 1.0).
+        addE("created").from("marko").to("lop").property(T.id, 9).property("weight", 0.4).
+        addE("created").from("josh").to("ripple").property(T.id, 10).property("weight", 1.0).
+        addE("created").from("josh").to("lop").property(T.id, 11).property("weight", 0.4).
+        addE("created").from("peter").to("lop").property(T.id, 12).property("weight", 0.2)
+      """
+    And using the parameter v1Id defined as "v[marko].id"
+    And the traversal of
+      """
+      g.V(v1Id).as("a").out("created").addE("createdBy").to("a").property("weight", null)
+      """
+    When iterated to list
+    Then the result should have a count of 1
+    And the graph should return 7 for count of "g.E()"
+    And the graph should return 4 for count of "g.V(v1Id).bothE()"
+    And the graph should return 1 for count of "g.V(v1Id).inE().has(\"weight\", null)"
+
   Scenario: g_V_aggregateXxX_asXaX_selectXxX_unfold_addEXexistsWithX_toXaX_propertyXtime_nowX
     Given the empty graph
     And the graph initializer of
diff --git a/gremlin-test/features/map/AddVertex.feature b/gremlin-test/features/map/AddVertex.feature
index a60fefb..efe3fc0 100644
--- a/gremlin-test/features/map/AddVertex.feature
+++ b/gremlin-test/features/map/AddVertex.feature
@@ -93,6 +93,31 @@ Feature: Step - addV()
     Then the result should have a count of 1
     And the graph should return 1 for count of "g.V().has(\"person\",\"name\",\"stephen\")"
 
+  Scenario: g_addVXpersonX_propertyXname_nullX
+    Given the empty graph
+    And the graph initializer of
+      """
+      g.addV("person").property(T.id, 1).property("name", "marko").property("age", 29).as("marko").
+        addV("person").property(T.id, 2).property("name", "vadas").property("age", 27).as("vadas").
+        addV("software").property(T.id, 3).property("name", "lop").property("lang", "java").as("lop").
+        addV("person").property(T.id, 4).property("name","josh").property("age", 32).as("josh").
+        addV("software").property(T.id, 5).property("name", "ripple").property("lang", "java").as("ripple").
+        addV("person").property(T.id, 6).property("name", "peter").property("age", 35).as('peter').
+        addE("knows").from("marko").to("vadas").property(T.id, 7).property("weight", 0.5).
+        addE("knows").from("marko").to("josh").property(T.id, 8).property("weight", 1.0).
+        addE("created").from("marko").to("lop").property(T.id, 9).property("weight", 0.4).
+        addE("created").from("josh").to("ripple").property(T.id, 10).property("weight", 1.0).
+        addE("created").from("josh").to("lop").property(T.id, 11).property("weight", 0.4).
+        addE("created").from("peter").to("lop").property(T.id, 12).property("weight", 0.2)
+      """
+    And the traversal of
+      """
+      g.addV("person").property("name", null)
+      """
+    When iterated to list
+    Then the result should have a count of 1
+    And the graph should return 1 for count of "g.V().has(\"person\",\"name\",null)"
+
   Scenario: g_addVXpersonX_propertyXsingle_name_stephenX_propertyXsingle_name_stephenmX
     Given the empty graph
     And the graph initializer of
diff --git a/gremlin-test/features/map/Constant.feature b/gremlin-test/features/map/Constant.feature
index 9ad8d13..ed25b25 100644
--- a/gremlin-test/features/map/Constant.feature
+++ b/gremlin-test/features/map/Constant.feature
@@ -19,7 +19,6 @@ Feature: Step - constant()
 
   Scenario: g_V_constantX123X
     Given the modern graph
-    And using the parameter v1Id defined as "v[marko].id"
     And the traversal of
       """
       g.V().constant(123)
@@ -34,6 +33,22 @@ Feature: Step - constant()
       | d[123].i |
       | d[123].i |
 
+  Scenario: g_V_constantXnullX
+    Given the modern graph
+    And the traversal of
+      """
+      g.V().constant(null)
+      """
+    When iterated to list
+    Then the result should be unordered
+      | result |
+      | null |
+      | null |
+      | null |
+      | null |
+      | null |
+      | null |
+
   Scenario: g_V_chooseXhasLabelXpersonX_valuesXnameX_constantXinhumanXX
     Given the modern graph
     And the traversal of
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeTest.java
index 54c4091..f534a4b 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeTest.java
@@ -46,6 +46,7 @@ import static org.apache.tinkerpop.gremlin.structure.Column.keys;
 import static org.apache.tinkerpop.gremlin.structure.Column.values;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
 
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
@@ -58,6 +59,8 @@ public abstract class AddEdgeTest extends AbstractGremlinProcessTest {
 
     public abstract Traversal<Vertex, Edge> get_g_VX1X_asXaX_outXcreatedX_addEXcreatedByX_toXaX_propertyXweight_2X(final Object v1Id);
 
+    public abstract Traversal<Vertex, Edge> get_g_VX1X_asXaX_outXcreatedX_addEXcreatedByX_toXaX_propertyXweight_nullX(final Object v1Id);
+
     public abstract Traversal<Vertex, Edge> get_g_V_aggregateXxX_asXaX_selectXxX_unfold_addEXexistsWithX_toXaX_propertyXtime_nowX();
 
     public abstract Traversal<Vertex, Edge> get_g_V_asXaX_outXcreatedX_inXcreatedX_whereXneqXaXX_asXbX_addEXcodeveloperX_fromXaX_toXbX_propertyXyear_2009X();
@@ -121,6 +124,28 @@ public abstract class AddEdgeTest extends AbstractGremlinProcessTest {
     @Test
     @LoadGraphWith(MODERN)
     @FeatureRequirement(featureClass = Graph.Features.EdgeFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_ADD_EDGES)
+    @FeatureRequirement(featureClass = Graph.Features.EdgeFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_NULL_PROPERTY_VALUES)
+    public void g_VX1X_asXaX_outXcreatedX_addEXcreatedByX_toXaX_propertyXweight_nullX() {
+        final Traversal<Vertex, Edge> traversal = get_g_VX1X_asXaX_outXcreatedX_addEXcreatedByX_toXaX_propertyXweight_nullX(convertToVertexId("marko"));
+        printTraversalForm(traversal);
+        int count = 0;
+        while (traversal.hasNext()) {
+            final Edge edge = traversal.next();
+            assertEquals("createdBy", edge.label());
+            assertNull(g.E(edge).<Double>values("weight").next());
+            assertEquals(1, g.E(edge).properties().count().next().intValue());
+            count++;
+
+
+        }
+        assertEquals(1, count);
+        assertEquals(7, IteratorUtils.count(g.E()));
+        assertEquals(6, IteratorUtils.count(g.V()));
+    }
+
+    @Test
+    @LoadGraphWith(MODERN)
+    @FeatureRequirement(featureClass = Graph.Features.EdgeFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_ADD_EDGES)
     public void g_V_aggregateXxX_asXaX_selectXxX_unfold_addEXexistsWithX_toXaX_propertyXtime_nowX() {
         final Traversal<Vertex, Edge> traversal = get_g_V_aggregateXxX_asXaX_selectXxX_unfold_addEXexistsWithX_toXaX_propertyXtime_nowX();
         printTraversalForm(traversal);
@@ -306,6 +331,11 @@ public abstract class AddEdgeTest extends AbstractGremlinProcessTest {
         }
 
         @Override
+        public Traversal<Vertex, Edge> get_g_VX1X_asXaX_outXcreatedX_addEXcreatedByX_toXaX_propertyXweight_nullX(final Object v1Id) {
+            return g.V(v1Id).as("a").out("created").addE("createdBy").to("a").property("weight", null);
+        }
+
+        @Override
         public Traversal<Vertex, Edge> get_g_V_aggregateXxX_asXaX_selectXxX_unfold_addEXexistsWithX_toXaX_propertyXtime_nowX() {
             return g.V().aggregate("x").as("a").select("x").unfold().addE("existsWith").to("a").property("time", "now");
         }
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexTest.java
index 21fbb8c..ebad007 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexTest.java
@@ -42,6 +42,7 @@ import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.V;
 import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.select;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 /**
@@ -56,6 +57,8 @@ public abstract class AddVertexTest extends AbstractGremlinTest {
 
     public abstract Traversal<Vertex, Vertex> get_g_addVXpersonX_propertyXname_stephenX();
 
+    public abstract Traversal<Vertex, Vertex> get_g_addVXpersonX_propertyXname_nullX();
+
     public abstract Traversal<Vertex, Vertex> get_g_addVXpersonX_propertyXsingle_name_stephenX_propertyXsingle_name_stephenmX();
 
     public abstract Traversal<Vertex, Vertex> get_g_addVXpersonX_propertyXsingle_name_stephenX_propertyXsingle_name_stephenm_since_2010X();
@@ -130,6 +133,22 @@ public abstract class AddVertexTest extends AbstractGremlinTest {
     @LoadGraphWith(MODERN)
     @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_VERTICES)
     @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_PROPERTY)
+    @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_NULL_PROPERTY_VALUES)
+    public void g_addVXpersonX_propertyXname_nullX() {
+        final Traversal<Vertex, Vertex> traversal = get_g_addVXpersonX_propertyXname_nullX();
+        printTraversalForm(traversal);
+        final Vertex nulled = traversal.next();
+        assertFalse(traversal.hasNext());
+        assertEquals("person", nulled.label());
+        assertNull(nulled.value("name"));
+        assertEquals(1, IteratorUtils.count(nulled.properties()));
+        assertEquals(7, IteratorUtils.count(g.V()));
+    }
+
+    @Test
+    @LoadGraphWith(MODERN)
+    @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_VERTICES)
+    @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_PROPERTY)
     public void g_addVXpersonX_propertyXsingle_name_stephenX_propertyXsingle_name_stephenmX() {
         final Traversal<Vertex, Vertex> traversal = get_g_addVXpersonX_propertyXsingle_name_stephenX_propertyXsingle_name_stephenmX();
         printTraversalForm(traversal);
@@ -315,6 +334,11 @@ public abstract class AddVertexTest extends AbstractGremlinTest {
         }
 
         @Override
+        public Traversal<Vertex, Vertex> get_g_addVXpersonX_propertyXname_nullX() {
+            return g.addV("person").property("name", null);
+        }
+
+        @Override
         public Traversal<Vertex, Vertex> get_g_addVXpersonX_propertyXsingle_name_stephenX_propertyXsingle_name_stephenmX() {
             return g.addV("person").property(VertexProperty.Cardinality.single, "name", "stephen").property(VertexProperty.Cardinality.single, "name", "stephenm");
         }
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConstantTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConstantTest.java
index e35db43..07a7b55 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConstantTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ConstantTest.java
@@ -46,9 +46,10 @@ public abstract class ConstantTest extends AbstractGremlinProcessTest {
 
     public abstract Traversal<Vertex, Integer> get_g_V_constantX123X();
 
+    public abstract Traversal<Vertex, Void> get_g_V_constantXnullX();
+
     public abstract Traversal<Vertex, String> get_g_V_chooseXhasLabelXpersonX_valuesXnameX_constantXinhumanXX();
 
-    /** Scenario: Trivial use case from GraphTraversal */
     @Test
     @LoadGraphWith(MODERN)
     public void g_V_constantX123X() {
@@ -56,6 +57,13 @@ public abstract class ConstantTest extends AbstractGremlinProcessTest {
         printTraversalForm(traversal);
         assertEquals(Arrays.asList(123, 123, 123, 123, 123, 123), traversal.toList());
     }
+    @Test
+    @LoadGraphWith(MODERN)
+    public void g_V_constantXnullX() {
+        final Traversal<Vertex, Void> traversal = get_g_V_constantXnullX();
+        printTraversalForm(traversal);
+        assertEquals(Arrays.asList(null, null, null, null, null, null), traversal.toList());
+    }
 
     /** Scenario: Anonymous traversal within choose */
     @Test
@@ -74,6 +82,11 @@ public abstract class ConstantTest extends AbstractGremlinProcessTest {
         }
 
         @Override
+        public Traversal<Vertex, Void> get_g_V_constantXnullX() {
+            return g.V().constant(null);
+        }
+
+        @Override
         public Traversal<Vertex, String> get_g_V_chooseXhasLabelXpersonX_valuesXnameX_constantXinhumanXX() {
             return g.V().choose(hasLabel("person"), values("name"), constant("inhuman"));
         }
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/structure/PropertyTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/structure/PropertyTest.java
index fb50841..68c9245 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/structure/PropertyTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/structure/PropertyTest.java
@@ -41,6 +41,7 @@ import java.util.Map;
 import static org.apache.tinkerpop.gremlin.structure.Graph.Features.PropertyFeatures.FEATURE_PROPERTIES;
 import static org.hamcrest.CoreMatchers.is;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeThat;
 
@@ -56,6 +57,9 @@ public class PropertyTest {
     /**
      * Basic tests for the {@link org.apache.tinkerpop.gremlin.structure.Property} class.
      */
+    @ExceptionCoverage(exceptionClass = Property.Exceptions.class, methods = {
+            "propertyValueCanNotBeNull"
+    })
     public static class BasicPropertyTest extends AbstractGremlinTest {
         @Test
         @FeatureRequirementSet(FeatureRequirementSet.Package.VERTICES_ONLY)
@@ -141,11 +145,78 @@ public class PropertyTest {
                 fail("Removing an edge property that was already removed should not throw an exception");
             }
         }
+
+        @Test
+        @FeatureRequirementSet(FeatureRequirementSet.Package.VERTICES_ONLY)
+        @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_NULL_PROPERTY_VALUES, supported = false)
+        public void shouldNotAllowNullAddVertex() throws Exception {
+            try {
+                this.graph.addVertex("name", null);
+                fail("Call to addVertex() should have thrown an exception as null is not a supported property value");
+            } catch (Exception ex) {
+                validateException(Property.Exceptions.propertyValueCanNotBeNull(), ex);
+            }
+        }
+
+        @Test
+        @FeatureRequirementSet(FeatureRequirementSet.Package.SIMPLE)
+        @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_NULL_PROPERTY_VALUES, supported = false)
+        public void shouldNotAllowNullAddEdge() throws Exception {
+            try {
+                final Vertex v = this.graph.addVertex();
+                v.addEdge("self", v, "name", null);
+                fail("Call to addEdge() should have thrown an exception as null is not a supported property value");
+            } catch (Exception ex) {
+                validateException(Property.Exceptions.propertyValueCanNotBeNull(), ex);
+            }
+        }
+
+        @Test
+        @FeatureRequirementSet(FeatureRequirementSet.Package.VERTICES_ONLY)
+        @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_NULL_PROPERTY_VALUES)
+        public void shouldAllowNullAddVertex() throws Exception {
+            final Vertex v = this.graph.addVertex("name", null);
+            assertNull(v.value("name"));
+        }
+
+        @Test
+        @FeatureRequirementSet(FeatureRequirementSet.Package.SIMPLE)
+        @FeatureRequirement(featureClass = Graph.Features.EdgeFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_NULL_PROPERTY_VALUES)
+        public void shouldAllowNullAddEdge() throws Exception {
+            final Vertex v = this.graph.addVertex();
+            final Edge e = v.addEdge("self", v, "name", null);
+            assertNull(e.value("name"));
+        }
+
+        @Test
+        @FeatureRequirementSet(FeatureRequirementSet.Package.VERTICES_ONLY)
+        @FeatureRequirement(featureClass = Graph.Features.VertexPropertyFeatures.class, feature = Graph.Features.VertexPropertyFeatures.FEATURE_NULL_PROPERTY_VALUES)
+        @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_META_PROPERTIES)
+        public void shouldAllowNullAddVertexProperty() throws Exception {
+            final Vertex v = this.graph.addVertex("person");
+            final VertexProperty vp = v.property("location", "santa fe", "startTime", 1995, "endTime", null);
+            assertEquals(1995, (int) vp.value("startTime"));
+            assertNull(vp.value("endTime"));
+        }
+
+        @Test
+        @FeatureRequirementSet(FeatureRequirementSet.Package.VERTICES_ONLY)
+        @FeatureRequirement(featureClass = Graph.Features.VertexPropertyFeatures.class, feature = Graph.Features.VertexPropertyFeatures.FEATURE_NULL_PROPERTY_VALUES, supported = false)
+        @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_META_PROPERTIES)
+        public void shouldNotAllowNullAddVertexProperty() throws Exception {
+            try {
+                final Vertex v = this.graph.addVertex("person");
+                final VertexProperty vp = v.property("location", "santa fe", "startTime", 1995, "endTime", null);
+                fail("Call to property() should have thrown an exception as null is not a supported property value");
+            } catch (Exception ex) {
+                validateException(Property.Exceptions.propertyValueCanNotBeNull(), ex);
+            }
+        }
     }
 
     /**
-     * Checks that properties added to an {@link org.apache.tinkerpop.gremlin.structure.Element} are validated in a consistent way when they are added at
-     * {@link org.apache.tinkerpop.gremlin.structure.Vertex} or {@link org.apache.tinkerpop.gremlin.structure.Edge} construction by throwing an appropriate exception.
+     * Checks that properties added to an {@link Element} are validated in a consistent way when they are added at
+     * {@link Vertex} or {@link Edge} construction by throwing an appropriate exception.
      */
     @RunWith(Parameterized.class)
     @ExceptionCoverage(exceptionClass = Element.Exceptions.class, methods = {
@@ -153,7 +224,6 @@ public class PropertyTest {
             "providedKeyValuesMustHaveALegalKeyOnEvenIndices"
     })
     @ExceptionCoverage(exceptionClass = Property.Exceptions.class, methods = {
-            "propertyValueCanNotBeNull",
             "propertyKeyCanNotBeEmpty"
     })
     public static class PropertyValidationOnAddExceptionConsistencyTest extends AbstractGremlinTest {
@@ -164,7 +234,6 @@ public class PropertyTest {
                     {"providedKeyValuesMustBeAMultipleOfTwo", new Object[]{"odd", "number", "arguments"}, Element.Exceptions.providedKeyValuesMustBeAMultipleOfTwo()},
                     {"providedKeyValuesMustBeAMultipleOfTwo", new Object[]{"odd"}, Element.Exceptions.providedKeyValuesMustBeAMultipleOfTwo()},
                     {"providedKeyValuesMustHaveALegalKeyOnEvenIndices", new Object[]{"odd", "number", 123, "test"}, Element.Exceptions.providedKeyValuesMustHaveALegalKeyOnEvenIndices()},
-                    {"propertyValueCanNotBeNull", new Object[]{"odd", null}, Property.Exceptions.propertyValueCanNotBeNull()},
                     {"providedKeyValuesMustHaveALegalKeyOnEvenIndices", new Object[]{null, "val"}, Element.Exceptions.providedKeyValuesMustHaveALegalKeyOnEvenIndices()},
                     {"propertyKeyCanNotBeEmpty", new Object[]{"", "val"}, Property.Exceptions.propertyKeyCanNotBeEmpty()}});
         }
@@ -245,13 +314,11 @@ public class PropertyTest {
 
 
     /**
-     * Checks that properties added to an {@link org.apache.tinkerpop.gremlin.structure.Element} are validated in a
-     * consistent way when they are set after {@link Vertex} or {@link Edge} construction by throwing an
-     * appropriate exception.
+     * Checks that properties added to an {@link Element} are validated in a consistent way when they are set after
+     * {@link Vertex} or {@link Edge} construction by throwing an appropriate exception.
      */
     @RunWith(Parameterized.class)
     @ExceptionCoverage(exceptionClass = Property.Exceptions.class, methods = {
-            "propertyValueCanNotBeNull",
             "propertyKeyCanNotBeNull",
             "propertyKeyCanNotBeEmpty",
             "propertyKeyCanNotBeAHiddenKey"
@@ -261,7 +328,6 @@ public class PropertyTest {
         @Parameterized.Parameters(name = "expect({0})")
         public static Iterable<Object[]> data() {
             return Arrays.asList(new Object[][]{
-                    {"propertyValueCanNotBeNull", "k", null, Property.Exceptions.propertyValueCanNotBeNull()},
                     {"propertyKeyCanNotBeNull", null, "v", Property.Exceptions.propertyKeyCanNotBeNull()},
                     {"propertyKeyCanNotBeEmpty", "", "v", Property.Exceptions.propertyKeyCanNotBeEmpty()},
                     {"propertyKeyCanNotBeAHiddenKey", Graph.Hidden.hide("systemKey"), "value", Property.Exceptions.propertyKeyCanNotBeAHiddenKey(Graph.Hidden.hide("systemKey"))}});
diff --git a/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jEdge.java b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jEdge.java
index 29ccef1..2729f9f 100644
--- a/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jEdge.java
+++ b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jEdge.java
@@ -114,7 +114,7 @@ public final class Neo4jEdge extends Neo4jElement implements Edge, WrappedEdge<N
 
     @Override
     public <V> Property<V> property(final String key, final V value) {
-        ElementHelper.validateProperty(key, value);
+        ElementHelper.validateProperty(false, key, value);
         this.graph.tx().readWrite();
         try {
             this.baseElement.setProperty(key, value);
diff --git a/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jGraph.java b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jGraph.java
index aa62ce6..2a18d47 100644
--- a/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jGraph.java
+++ b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jGraph.java
@@ -127,7 +127,7 @@ public final class Neo4jGraph implements Graph, WrappedGraph<Neo4jGraphAPI> {
 
     @Override
     public Vertex addVertex(final Object... keyValues) {
-        ElementHelper.legalPropertyKeyValueArray(keyValues);
+        ElementHelper.legalPropertyKeyValueArray(false, keyValues);
         if (ElementHelper.getIdValue(keyValues).isPresent())
             throw Vertex.Exceptions.userSuppliedIdsNotSupported();
         this.tx().readWrite();
@@ -417,6 +417,11 @@ public final class Neo4jGraph implements Graph, WrappedGraph<Neo4jGraphAPI> {
             }
 
             @Override
+            public boolean supportsNullPropertyValues() {
+                return false;
+            }
+
+            @Override
             public boolean supportsUserSuppliedIds() {
                 return false;
             }
@@ -448,6 +453,11 @@ public final class Neo4jGraph implements Graph, WrappedGraph<Neo4jGraphAPI> {
             }
 
             @Override
+            public boolean supportsNullPropertyValues() {
+                return false;
+            }
+
+            @Override
             public boolean supportsMapValues() {
                 return false;
             }
diff --git a/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jVertex.java b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jVertex.java
index c5cce7d..2bbca2f 100644
--- a/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jVertex.java
+++ b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jVertex.java
@@ -56,7 +56,7 @@ public final class Neo4jVertex extends Neo4jElement implements Vertex, WrappedVe
     public Edge addEdge(final String label, final Vertex inVertex, final Object... keyValues) {
         if (null == inVertex) throw Graph.Exceptions.argumentCanNotBeNull("inVertex");
         ElementHelper.validateLabel(label);
-        ElementHelper.legalPropertyKeyValueArray(keyValues);
+        ElementHelper.legalPropertyKeyValueArray(false, keyValues);
         if (ElementHelper.getIdValue(keyValues).isPresent())
             throw Edge.Exceptions.userSuppliedIdsNotSupported();
 
@@ -91,7 +91,7 @@ public final class Neo4jVertex extends Neo4jElement implements Vertex, WrappedVe
 
     @Override
     public <V> VertexProperty<V> property(final VertexProperty.Cardinality cardinality, final String key, final V value, final Object... keyValues) {
-        ElementHelper.validateProperty(key, value);
+        ElementHelper.validateProperty(false, key, value);
         if (ElementHelper.getIdValue(keyValues).isPresent())
             throw Vertex.Exceptions.userSuppliedIdsNotSupported();
         this.graph.tx().readWrite();
diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/computer/TinkerGraphComputerView.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/computer/TinkerGraphComputerView.java
index 43998fb..b61fc2a 100644
--- a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/computer/TinkerGraphComputerView.java
+++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/computer/TinkerGraphComputerView.java
@@ -79,7 +79,7 @@ public final class TinkerGraphComputerView {
     }
 
     public <V> Property<V> addProperty(final TinkerVertex vertex, final String key, final V value) {
-        ElementHelper.validateProperty(key, value);
+        ElementHelper.validateProperty(true, key, value);
         if (isComputeKey(key)) {
             final TinkerVertexProperty<V> property = new TinkerVertexProperty<V>((TinkerVertex) vertex, key, value) {
                 @Override
diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerEdge.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerEdge.java
index bcfe485..d594fea 100644
--- a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerEdge.java
+++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerEdge.java
@@ -43,18 +43,20 @@ public final class TinkerEdge extends TinkerElement implements Edge {
     protected Map<String, Property> properties;
     protected final Vertex inVertex;
     protected final Vertex outVertex;
+    private final boolean allowNullPropertyValues;
 
     protected TinkerEdge(final Object id, final Vertex outVertex, final String label, final Vertex inVertex) {
         super(id, label);
         this.outVertex = outVertex;
         this.inVertex = inVertex;
+        this.allowNullPropertyValues = outVertex.graph().features().edge().supportsNullPropertyValues();
         TinkerHelper.autoUpdateIndex(this, T.label.getAccessor(), this.label, null);
     }
 
     @Override
     public <V> Property<V> property(final String key, final V value) {
         if (this.removed) throw elementAlreadyRemoved(Edge.class, id);
-        ElementHelper.validateProperty(key, value);
+        ElementHelper.validateProperty(allowNullPropertyValues, key, value);
         final Property oldProperty = super.property(key);
         final Property<V> newProperty = new TinkerProperty<>(this, key, value);
         if (null == this.properties) this.properties = new HashMap<>();
diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraph.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraph.java
index 67df21a..7e2cf46 100644
--- a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraph.java
+++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraph.java
@@ -83,6 +83,7 @@ public final class TinkerGraph implements Graph {
     public static final String GREMLIN_TINKERGRAPH_DEFAULT_VERTEX_PROPERTY_CARDINALITY = "gremlin.tinkergraph.defaultVertexPropertyCardinality";
     public static final String GREMLIN_TINKERGRAPH_GRAPH_LOCATION = "gremlin.tinkergraph.graphLocation";
     public static final String GREMLIN_TINKERGRAPH_GRAPH_FORMAT = "gremlin.tinkergraph.graphFormat";
+    public static final String GREMLIN_TINKERGRAPH_ALLOW_NULL_PROPERTY_VALUES = "gremlin.tinkergraph.allowNullPropertyValues";
 
     private final TinkerGraphFeatures features = new TinkerGraphFeatures();
 
@@ -99,6 +100,7 @@ public final class TinkerGraph implements Graph {
     protected final IdManager<?> edgeIdManager;
     protected final IdManager<?> vertexPropertyIdManager;
     protected final VertexProperty.Cardinality defaultVertexPropertyCardinality;
+    protected final boolean allowNullPropertyValues;
 
     private final Configuration configuration;
     private final String graphLocation;
@@ -114,6 +116,7 @@ public final class TinkerGraph implements Graph {
         vertexPropertyIdManager = selectIdManager(configuration, GREMLIN_TINKERGRAPH_VERTEX_PROPERTY_ID_MANAGER, VertexProperty.class);
         defaultVertexPropertyCardinality = VertexProperty.Cardinality.valueOf(
                 configuration.getString(GREMLIN_TINKERGRAPH_DEFAULT_VERTEX_PROPERTY_CARDINALITY, VertexProperty.Cardinality.single.name()));
+        allowNullPropertyValues = configuration.getBoolean(GREMLIN_TINKERGRAPH_ALLOW_NULL_PROPERTY_VALUES, true);
 
         graphLocation = configuration.getString(GREMLIN_TINKERGRAPH_GRAPH_LOCATION, null);
         graphFormat = configuration.getString(GREMLIN_TINKERGRAPH_GRAPH_FORMAT, null);
@@ -158,7 +161,7 @@ public final class TinkerGraph implements Graph {
 
     @Override
     public Vertex addVertex(final Object... keyValues) {
-        ElementHelper.legalPropertyKeyValueArray(keyValues);
+        ElementHelper.legalPropertyKeyValueArray(allowNullPropertyValues, keyValues);
         Object idValue = vertexIdManager.convert(ElementHelper.getIdValue(keyValues).orElse(null));
         final String label = ElementHelper.getLabelValue(keyValues).orElse(Vertex.DEFAULT_LABEL);
 
diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerHelper.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerHelper.java
index 4ed3fdd..2b1a928 100644
--- a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerHelper.java
+++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerHelper.java
@@ -49,7 +49,7 @@ public final class TinkerHelper {
 
     protected static Edge addEdge(final TinkerGraph graph, final TinkerVertex outVertex, final TinkerVertex inVertex, final String label, final Object... keyValues) {
         ElementHelper.validateLabel(label);
-        ElementHelper.legalPropertyKeyValueArray(keyValues);
+        ElementHelper.legalPropertyKeyValueArray(graph.features().edge().supportsNullPropertyValues(), keyValues);
 
         Object idValue = graph.edgeIdManager.convert(ElementHelper.getIdValue(keyValues).orElse(null));
 
diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerIndex.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerIndex.java
index f5872cf..75924da 100644
--- a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerIndex.java
+++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerIndex.java
@@ -49,7 +49,7 @@ final class TinkerIndex<T extends Element> {
     protected void put(final String key, final Object value, final T element) {
         Map<Object, Set<T>> keyMap = this.index.get(key);
         if (null == keyMap) {
-            this.index.putIfAbsent(key, new ConcurrentHashMap<Object, Set<T>>());
+            this.index.putIfAbsent(key, new ConcurrentHashMap<>());
             keyMap = this.index.get(key);
         }
         Set<T> objects = keyMap.get(value);
@@ -65,7 +65,7 @@ final class TinkerIndex<T extends Element> {
         if (null == keyMap) {
             return Collections.emptyList();
         } else {
-            Set<T> set = keyMap.get(value);
+            Set<T> set = keyMap.get(indexable(value));
             if (null == set)
                 return Collections.emptyList();
             else
@@ -78,7 +78,7 @@ final class TinkerIndex<T extends Element> {
         if (null == keyMap) {
             return 0;
         } else {
-            Set<T> set = keyMap.get(value);
+            final Set<T> set = keyMap.get(indexable(value));
             if (null == set)
                 return 0;
             else
@@ -89,7 +89,7 @@ final class TinkerIndex<T extends Element> {
     public void remove(final String key, final Object value, final T element) {
         final Map<Object, Set<T>> keyMap = this.index.get(key);
         if (null != keyMap) {
-            Set<T> objects = keyMap.get(value);
+            final Set<T> objects = keyMap.get(indexable(value));
             if (null != objects) {
                 objects.remove(element);
                 if (objects.size() == 0) {
@@ -111,17 +111,11 @@ final class TinkerIndex<T extends Element> {
 
     public void autoUpdate(final String key, final Object newValue, final Object oldValue, final T element) {
         if (this.indexedKeys.contains(key)) {
-            if (oldValue != null)
-                this.remove(key, oldValue, element);
+            this.remove(key, oldValue, element);
             this.put(key, newValue, element);
         }
     }
 
-    public void autoRemove(final String key, final Object oldValue, final T element) {
-        if (this.indexedKeys.contains(key))
-            this.remove(key, oldValue, element);
-    }
-
     public void createKeyIndex(final String key) {
         if (null == key)
             throw Graph.Exceptions.argumentCanNotBeNull("key");
@@ -133,8 +127,8 @@ final class TinkerIndex<T extends Element> {
         this.indexedKeys.add(key);
 
         (Vertex.class.isAssignableFrom(this.indexClass) ?
-                this.graph.vertices.values().<T>parallelStream() :
-                this.graph.edges.values().<T>parallelStream())
+                this.graph.vertices.values().parallelStream() :
+                this.graph.edges.values().parallelStream())
                 .map(e -> new Object[]{((T) e).property(key), e})
                 .filter(a -> ((Property) a[0]).isPresent())
                 .forEach(a -> this.put(key, ((Property) a[0]).value(), (T) a[1]));
@@ -147,7 +141,35 @@ final class TinkerIndex<T extends Element> {
         this.indexedKeys.remove(key);
     }
 
+    /**
+     * Provides a way for an index to have a {@code null} value as {@code ConcurrentHashMap} will not allow a
+     * {@code null} key.
+     */
+    public static Object indexable(final Object obj) {
+        return null == obj ? IndexedNull.instance() : obj;
+    }
+
     public Set<String> getIndexedKeys() {
         return this.indexedKeys;
     }
+
+    public static final class IndexedNull {
+        private static final IndexedNull inst = new IndexedNull();
+
+        private IndexedNull() {}
+
+        static IndexedNull instance() {
+            return inst;
+        }
+
+        @Override
+        public int hashCode() {
+            return 751912123;
+        }
+
+        @Override
+        public boolean equals(final Object o) {
+            return o instanceof IndexedNull;
+        }
+    }
 }
diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerProperty.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerProperty.java
index 0f7077b..db40346 100644
--- a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerProperty.java
+++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerProperty.java
@@ -54,9 +54,12 @@ public final class TinkerProperty<V> implements Property<V> {
         return this.value;
     }
 
+    /**
+     * The existence of this object implies the property is present, thus even a {@code null} value means "present".
+     */
     @Override
     public boolean isPresent() {
-        return null != this.value;
+        return true;
     }
 
     @Override
diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerVertex.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerVertex.java
index be08137..7fe7ce0 100644
--- a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerVertex.java
+++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerVertex.java
@@ -46,10 +46,12 @@ public final class TinkerVertex extends TinkerElement implements Vertex {
     protected Map<String, Set<Edge>> outEdges;
     protected Map<String, Set<Edge>> inEdges;
     private final TinkerGraph graph;
+    private boolean allowNullPropertyValues;
 
     protected TinkerVertex(final Object id, final String label, final TinkerGraph graph) {
         super(id, label);
         this.graph = graph;
+        this.allowNullPropertyValues = graph.features().vertex().supportsNullPropertyValues();
     }
 
     @Override
@@ -83,8 +85,8 @@ public final class TinkerVertex extends TinkerElement implements Vertex {
     @Override
     public <V> VertexProperty<V> property(final VertexProperty.Cardinality cardinality, final String key, final V value, final Object... keyValues) {
         if (this.removed) throw elementAlreadyRemoved(Vertex.class, id);
-        ElementHelper.legalPropertyKeyValueArray(keyValues);
-        ElementHelper.validateProperty(key, value);
+        ElementHelper.legalPropertyKeyValueArray(allowNullPropertyValues, keyValues);
+        ElementHelper.validateProperty(allowNullPropertyValues, key, value);
         final Optional<Object> optionalId = ElementHelper.getIdValue(keyValues);
         final Optional<VertexProperty<V>> optionalVertexProperty = ElementHelper.stageVertexProperty(this, cardinality, key, value, keyValues);
         if (optionalVertexProperty.isPresent()) return optionalVertexProperty.get();
diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerVertexProperty.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerVertexProperty.java
index 3ca871a..f01a3f2 100644
--- a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerVertexProperty.java
+++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerVertexProperty.java
@@ -45,6 +45,7 @@ public class TinkerVertexProperty<V> extends TinkerElement implements VertexProp
     private final TinkerVertex vertex;
     private final String key;
     private final V value;
+    private final boolean allowNullPropertyValues;
 
     /**
      * This constructor will not validate the ID type against the {@link Graph}.  It will always just use a
@@ -53,10 +54,11 @@ public class TinkerVertexProperty<V> extends TinkerElement implements VertexProp
      */
     public TinkerVertexProperty(final TinkerVertex vertex, final String key, final V value, final Object... propertyKeyValues) {
         super(((TinkerGraph) vertex.graph()).vertexPropertyIdManager.getNextId((TinkerGraph) vertex.graph()), key);
+        this.allowNullPropertyValues = vertex.graph().features().vertex().properties().supportsNullPropertyValues();
         this.vertex = vertex;
         this.key = key;
         this.value = value;
-        ElementHelper.legalPropertyKeyValueArray(propertyKeyValues);
+        ElementHelper.legalPropertyKeyValueArray(allowNullPropertyValues, propertyKeyValues);
         ElementHelper.attachProperties(this, propertyKeyValues);
     }
 
@@ -66,10 +68,11 @@ public class TinkerVertexProperty<V> extends TinkerElement implements VertexProp
      */
     public TinkerVertexProperty(final Object id, final TinkerVertex vertex, final String key, final V value, final Object... propertyKeyValues) {
         super(id, key);
+        this.allowNullPropertyValues = vertex.graph().features().vertex().properties().supportsNullPropertyValues();
         this.vertex = vertex;
         this.key = key;
         this.value = value;
-        ElementHelper.legalPropertyKeyValueArray(propertyKeyValues);
+        ElementHelper.legalPropertyKeyValueArray(allowNullPropertyValues, propertyKeyValues);
         ElementHelper.attachProperties(this, propertyKeyValues);
     }
 


[tinkerpop] 02/06: TINKERPOP-2235 Fix C# gherkin test harness

Posted by sp...@apache.org.
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 38f52c3b8a18478576b4c990e56d94add5df4bed
Author: Jorge Bay Gondra <jo...@gmail.com>
AuthorDate: Tue Nov 5 12:26:58 2019 +0100

    TINKERPOP-2235 Fix C# gherkin test harness
    
    Fix C# gherkin test harness for method with params and generic type parameters.
    Add null as token.
---
 .../Gherkin/CommonSteps.cs                            |  3 ++-
 .../Gherkin/TraversalEvaluation/LiteralParameter.cs   | 11 ++++++++---
 .../Gherkin/TraversalEvaluation/TraversalParser.cs    | 19 +++++++++++++------
 3 files changed, 23 insertions(+), 10 deletions(-)

diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs
index d4b2aab..504f7a9 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs
@@ -63,7 +63,8 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
                 {@"s\[(.*)\]", ToSet},
                 {@"m\[(.+)\]", ToMap},
                 {@"c\[(.+)\]", ToLambda},
-                {@"t\[(.+)\]", ToT}
+                {@"t\[(.+)\]", ToT},
+                {"null", (_, __) => null}
             }.ToDictionary(kv => new Regex("^" + kv.Key + "$", RegexOptions.Compiled), kv => kv.Value);
 
         private static readonly IDictionary<char, Func<string, object>> NumericParsers =
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/LiteralParameter.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/LiteralParameter.cs
index da0ac24..10a88a2 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/LiteralParameter.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/LiteralParameter.cs
@@ -29,10 +29,15 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation
     /// <summary>
     /// Represents a literal (number / boolean) that is allowed as a gremlin parameter.
     /// </summary>
-    public class LiteralParameter<T> : ITokenParameter, IEquatable<LiteralParameter<T>> where T : struct
+    public class LiteralParameter<T> : ITokenParameter, IEquatable<LiteralParameter<T>>
     {
         public bool Equals(LiteralParameter<T> other)
         {
+            if (Value == null)
+            {
+                return other.Value == null;
+            }
+
             return Value.Equals(other.Value);
         }
 
@@ -46,7 +51,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation
 
         public override int GetHashCode()
         {
-            return Value.GetHashCode();
+            return Value?.GetHashCode() ?? 0;
         }
 
         public T Value { get; }
@@ -79,7 +84,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation
 
     internal static class LiteralParameter
     {
-        public static LiteralParameter<TType> Create<TType>(TType value) where TType : struct
+        public static LiteralParameter<TType> Create<TType>(TType value)
         {
             return new LiteralParameter<TType>(value);
         }
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalParser.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalParser.cs
index 105932f..fb84fdd 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalParser.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalParser.cs
@@ -233,6 +233,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation
             var paramsInfo = method.GetParameters();
             var parameters = new object[paramsInfo.Length];
             genericParameterTypes = new Dictionary<string, Type>();
+
             for (var i = 0; i < paramsInfo.Length; i++)
             {
                 var info = paramsInfo[i];
@@ -246,15 +247,18 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation
                         // We've provided a value for parameter of a generic type, we can infer the
                         // type of the generic argument based on the parameter.
                         // For example, in the case of `Constant<E2>(E2 value)`
-                        // if we have the type of value we have the type of E2. 
+                        // if we have the type of value we have the type of E2.
                         genericParameterTypes.Add(info.ParameterType.Name, tokenParameter.GetParameterType());
                     }
                     else if (IsParamsArray(info) && info.ParameterType.GetElementType().IsGenericParameter)
                     {
                         // Its a method where the type parameter comes from an params Array
                         // e.g., Inject<S>(params S[] value)
-                        genericParameterTypes.Add(info.ParameterType.GetElementType().Name,
-                            tokenParameter.GetParameterType());
+                        var type = tokenParameter.GetParameterType();
+                        genericParameterTypes.Add(info.ParameterType.GetElementType().Name, type);
+
+                        // Use a placeholder value
+                        value = type.IsValueType ? Activator.CreateInstance(type) : new object();
                     }
 
                     if (info.ParameterType != tokenParameter.GetParameterType() && IsNumeric(info.ParameterType) &&
@@ -270,7 +274,6 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation
                     // For `params type[] value` we should provide an empty array
                     if (value == null)
                     {
-                        // An empty array
                         value = Array.CreateInstance(info.ParameterType.GetElementType(), 0);
                     }
                     else if (!value.GetType().IsArray)
@@ -286,8 +289,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation
                         }
 
                         var arr = Array.CreateInstance(elementType, token.Parameters.Count - i);
-                        arr.SetValue(value, 0);
-                        for (var j = 1; j < token.Parameters.Count - i; j++)
+                        for (var j = 0; j < token.Parameters.Count - i; j++)
                         {
                             arr.SetValue(token.Parameters[i + j].GetValue(), j);
                         }
@@ -449,6 +451,11 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation
                 i += parameterText.Length - 1;
                 return LiteralParameter.Create(Convert.ToBoolean(parameterText));
             }
+            if (parameterText == "null")
+            {
+                i += parameterText.Length - 1;
+                return LiteralParameter.Create<object>(null);
+            }
             if (RegexIO.IsMatch(parameterText))
             {
                 i += parameterText.Length - 1;