Merge branch 'tp32' into tp33
Conflicts:
gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/AbstractGraphSONTypeSerializer.java
gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperPartialEmbeddedTypeTest.java
Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo
Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/a3624f7e
Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/a3624f7e
Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/a3624f7e
Branch: refs/heads/tp33
Commit: a3624f7e2f23d290ac2ad475f88716f98eeb8b7c
Parents: dd3a54e 7bff988
Author: Stephen Mallette <[email protected]>
Authored: Thu Mar 8 08:08:40 2018 -0500
Committer: Stephen Mallette <[email protected]>
Committed: Thu Mar 8 08:08:40 2018 -0500
----------------------------------------------------------------------
CHANGELOG.asciidoc | 1 +
.../AbstractGraphSONTypeSerializer.java | 80 +-------------------
.../io/graphson/GraphSONTypeSerializerV2d0.java | 38 ++++++++++
.../io/graphson/GraphSONTypeSerializerV3d0.java | 72 +++++++++++-------
.../GraphSONMapperPartialEmbeddedTypeTest.java | 33 ++++++++
gremlin-shaded/pom.xml | 2 +-
6 files changed, 120 insertions(+), 106 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a3624f7e/CHANGELOG.asciidoc
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a3624f7e/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/AbstractGraphSONTypeSerializer.java
----------------------------------------------------------------------
diff --cc
gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/AbstractGraphSONTypeSerializer.java
index 6eb65e1,0000000..fa90b2c
mode 100644,000000..100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/AbstractGraphSONTypeSerializer.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/AbstractGraphSONTypeSerializer.java
@@@ -1,173 -1,0 +1,99 @@@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.structure.io.graphson;
+
+import org.apache.tinkerpop.shaded.jackson.annotation.JsonTypeInfo;
+import org.apache.tinkerpop.shaded.jackson.core.JsonGenerator;
++import org.apache.tinkerpop.shaded.jackson.core.JsonToken;
++import org.apache.tinkerpop.shaded.jackson.core.type.WritableTypeId;
+import org.apache.tinkerpop.shaded.jackson.databind.BeanProperty;
+import org.apache.tinkerpop.shaded.jackson.databind.jsontype.TypeIdResolver;
+import org.apache.tinkerpop.shaded.jackson.databind.jsontype.TypeSerializer;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Extension of the Jackson's default TypeSerializer. An instance of this
object will be passed to the serializers
+ * on which they can safely call the utility methods to serialize types and
making it compatible with the version
+ * 2.0+ of GraphSON.
+ *
+ * @author Kevin Gallardo (https://kgdo.me)
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public abstract class AbstractGraphSONTypeSerializer extends TypeSerializer {
+
+ protected final TypeIdResolver idRes;
+ protected final String propertyName;
+ protected final TypeInfo typeInfo;
+ protected final String valuePropertyName;
+ protected final Map<Class, Class> classMap = new HashMap<>();
+
+ AbstractGraphSONTypeSerializer(final TypeIdResolver idRes, final String
propertyName, final TypeInfo typeInfo,
+ final String valuePropertyName) {
+ this.idRes = idRes;
+ this.propertyName = propertyName;
+ this.typeInfo = typeInfo;
+ this.valuePropertyName = valuePropertyName;
+ }
+
+
+ @Override
+ public TypeSerializer forProperty(final BeanProperty beanProperty) {
+ return this;
+ }
+
+ @Override
+ public JsonTypeInfo.As getTypeInclusion() {
- return null;
++ return JsonTypeInfo.As.WRAPPER_OBJECT;
+ }
+
+ @Override
+ public String getPropertyName() {
+ return propertyName;
+ }
+
+ @Override
+ public TypeIdResolver getTypeIdResolver() {
+ return idRes;
+ }
+
- @Override
- public void writeTypePrefixForScalar(final Object o, final JsonGenerator
jsonGenerator) throws IOException {
- if (canWriteTypeId()) {
- writeTypePrefix(jsonGenerator,
getTypeIdResolver().idFromValueAndType(o, getClassFromObject(o)));
- }
- }
-
- @Override
- public void writeTypePrefixForObject(final Object o, final JsonGenerator
jsonGenerator) throws IOException {
- jsonGenerator.writeStartObject();
- // TODO: FULL_TYPES should be implemented here as : if
(fullTypesModeEnabled()) writeTypePrefix(Map);
- }
-
- @Override
- public void writeTypePrefixForArray(final Object o, final JsonGenerator
jsonGenerator) throws IOException {
- jsonGenerator.writeStartArray();
- // TODO: FULL_TYPES should be implemented here as : if
(fullTypesModeEnabled()) writeTypePrefix(List);
- }
-
- @Override
- public void writeTypeSuffixForScalar(final Object o, final JsonGenerator
jsonGenerator) throws IOException {
- if (canWriteTypeId()) {
- writeTypeSuffix(jsonGenerator);
- }
- }
-
- @Override
- public void writeTypeSuffixForObject(final Object o, final JsonGenerator
jsonGenerator) throws IOException {
- jsonGenerator.writeEndObject();
- // TODO: FULL_TYPES should be implemented here as : if
(fullTypesModeEnabled()) writeTypeSuffix(Map);
- }
-
- @Override
- public void writeTypeSuffixForArray(final Object o, final JsonGenerator
jsonGenerator) throws IOException {
- jsonGenerator.writeEndArray();
- // TODO: FULL_TYPES should be implemented here as : if
(fullTypesModeEnabled()) writeTypeSuffix(List);
- }
-
- @Override
- public void writeCustomTypePrefixForScalar(final Object o, final
JsonGenerator jsonGenerator, final String s) throws IOException {
- if (canWriteTypeId()) {
- writeTypePrefix(jsonGenerator, s);
- }
- }
-
- @Override
- public void writeCustomTypePrefixForObject(final Object o, final
JsonGenerator jsonGenerator, final String s) throws IOException {
- jsonGenerator.writeStartObject();
- // TODO: FULL_TYPES should be implemented here as : if
(fullTypesModeEnabled()) writeTypePrefix(s);
- }
-
- @Override
- public void writeCustomTypePrefixForArray(final Object o, final
JsonGenerator jsonGenerator, final String s) throws IOException {
- jsonGenerator.writeStartArray();
- // TODO: FULL_TYPES should be implemented here as : if
(fullTypesModeEnabled()) writeTypePrefix(s);
- }
-
- @Override
- public void writeCustomTypeSuffixForScalar(final Object o, final
JsonGenerator jsonGenerator, final String s) throws IOException {
- if (canWriteTypeId()) {
- writeTypeSuffix(jsonGenerator);
- }
- }
-
- @Override
- public void writeCustomTypeSuffixForObject(final Object o, final
JsonGenerator jsonGenerator, final String s) throws IOException {
- jsonGenerator.writeEndObject();
- // TODO: FULL_TYPES should be implemented here as : if
(fullTypesModeEnabled()) writeTypeSuffix(s);
- }
-
- @Override
- public void writeCustomTypeSuffixForArray(final Object o, final
JsonGenerator jsonGenerator, final String s) throws IOException {
- jsonGenerator.writeEndArray();
- // TODO: FULL_TYPES should be implemented here as : if
(fullTypesModeEnabled()) writeTypeSuffix(s);
- }
-
+ protected boolean canWriteTypeId() {
+ return typeInfo != null
+ && typeInfo == TypeInfo.PARTIAL_TYPES;
+ }
+
+ protected void writeTypePrefix(final JsonGenerator jsonGenerator, final
String s) throws IOException {
+ jsonGenerator.writeStartObject();
+ jsonGenerator.writeStringField(this.getPropertyName(), s);
+ jsonGenerator.writeFieldName(this.valuePropertyName);
+ }
+
+ protected void writeTypeSuffix(final JsonGenerator jsonGenerator) throws
IOException {
+ jsonGenerator.writeEndObject();
+ }
+
+ /**
+ * We force only **one** translation of a Java object to a domain
specific object. i.e. users register typeIDs
+ * and serializers/deserializers for the predefined types we have in the
spec. Graph, Vertex, Edge,
+ * VertexProperty, etc... And **not** their implementations (TinkerGraph,
DetachedVertex, TinkerEdge, etc..)
+ */
+ protected abstract Class getClassFromObject(final Object o);
+}
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a3624f7e/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeSerializerV2d0.java
----------------------------------------------------------------------
diff --cc
gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeSerializerV2d0.java
index 993f110,0000000..f0d437f
mode 100644,000000..100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeSerializerV2d0.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeSerializerV2d0.java
@@@ -1,112 -1,0 +1,150 @@@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.structure.io.graphson;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Operator;
+import org.apache.tinkerpop.gremlin.process.traversal.Order;
+import org.apache.tinkerpop.gremlin.process.traversal.Path;
+import org.apache.tinkerpop.gremlin.process.traversal.Pop;
+import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
+import org.apache.tinkerpop.gremlin.process.traversal.Scope;
+import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+import
org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
+import org.apache.tinkerpop.gremlin.process.traversal.util.Metrics;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalMetrics;
+import org.apache.tinkerpop.gremlin.structure.Column;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Property;
+import org.apache.tinkerpop.gremlin.structure.T;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.apache.tinkerpop.gremlin.util.function.Lambda;
++import org.apache.tinkerpop.shaded.jackson.core.JsonGenerator;
++import org.apache.tinkerpop.shaded.jackson.core.JsonToken;
++import org.apache.tinkerpop.shaded.jackson.core.type.WritableTypeId;
+import org.apache.tinkerpop.shaded.jackson.databind.jsontype.TypeIdResolver;
+
++import java.io.IOException;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+
+/**
+ * GraphSON 2.0 {@code TypeSerializer}.
+ *
+ * @author Kevin Gallardo (https://kgdo.me)
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class GraphSONTypeSerializerV2d0 extends
AbstractGraphSONTypeSerializer {
+
+ GraphSONTypeSerializerV2d0(final TypeIdResolver idRes, final String
propertyName, final TypeInfo typeInfo,
+ final String valuePropertyName) {
+ super(idRes, propertyName, typeInfo, valuePropertyName);
+ }
+
+ @Override
++ public WritableTypeId writeTypePrefix(final JsonGenerator jsonGenerator,
final WritableTypeId writableTypeId) throws IOException {
++ if (writableTypeId.valueShape == JsonToken.VALUE_STRING) {
++ if (canWriteTypeId()) {
++ writeTypePrefix(jsonGenerator,
getTypeIdResolver().idFromValueAndType(writableTypeId.forValue,
getClassFromObject(writableTypeId.forValue)));
++ }
++ } else if (writableTypeId.valueShape == JsonToken.START_OBJECT) {
++ jsonGenerator.writeStartObject();
++ } else if (writableTypeId.valueShape == JsonToken.START_ARRAY) {
++ jsonGenerator.writeStartArray();
++ } else {
++ throw new IllegalStateException("Could not write prefix");
++ }
++
++ return writableTypeId;
++ }
++
++ @Override
++ public WritableTypeId writeTypeSuffix(final JsonGenerator jsonGenerator,
final WritableTypeId writableTypeId) throws IOException {
++ if (writableTypeId.valueShape == JsonToken.VALUE_STRING) {
++ if (canWriteTypeId()) {
++ writeTypeSuffix(jsonGenerator);
++ }
++ } else if (writableTypeId.valueShape == JsonToken.START_OBJECT) {
++ jsonGenerator.writeEndObject();
++ } else if (writableTypeId.valueShape == JsonToken.START_ARRAY) {
++ jsonGenerator.writeEndArray();
++ } else {
++ throw new IllegalStateException("Could not write suffix");
++ }
++
++ return writableTypeId;
++ }
++
++ @Override
+ protected Class getClassFromObject(final Object o) {
+ final Class c = o.getClass();
+ if (classMap.containsKey(c))
+ return classMap.get(c);
+
+ final Class mapped;
+ if (Vertex.class.isAssignableFrom(c))
+ mapped = Vertex.class;
+ else if (Edge.class.isAssignableFrom(c))
+ mapped = Edge.class;
+ else if (Path.class.isAssignableFrom(c))
+ mapped = Path.class;
+ else if (VertexProperty.class.isAssignableFrom(c))
+ mapped = VertexProperty.class;
+ else if (Metrics.class.isAssignableFrom(c))
+ mapped = Metrics.class;
+ else if (TraversalMetrics.class.isAssignableFrom(c))
+ mapped = TraversalMetrics.class;
+ else if (Property.class.isAssignableFrom(c))
+ mapped = Property.class;
+ else if (ByteBuffer.class.isAssignableFrom(c))
+ mapped = ByteBuffer.class;
+ else if (InetAddress.class.isAssignableFrom(c))
+ mapped = InetAddress.class;
+ else if (Traverser.class.isAssignableFrom(c))
+ mapped = Traverser.class;
+ else if (Lambda.class.isAssignableFrom(c))
+ mapped = Lambda.class;
+ else if (VertexProperty.Cardinality.class.isAssignableFrom(c))
+ mapped = VertexProperty.Cardinality.class;
+ else if (Column.class.isAssignableFrom(c))
+ mapped = Column.class;
+ else if (Direction.class.isAssignableFrom(c))
+ mapped = Direction.class;
+ else if (Operator.class.isAssignableFrom(c))
+ mapped = Operator.class;
+ else if (Order.class.isAssignableFrom(c))
+ mapped = Order.class;
+ else if (Pop.class.isAssignableFrom(c))
+ mapped = Pop.class;
+ else if (SackFunctions.Barrier.class.isAssignableFrom(c))
+ mapped = SackFunctions.Barrier.class;
+ else if (TraversalOptionParent.Pick.class.isAssignableFrom(c))
+ mapped = TraversalOptionParent.Pick.class;
+ else if (Scope.class.isAssignableFrom(c))
+ mapped = Scope.class;
+ else if (T.class.isAssignableFrom(c))
+ mapped = T.class;
+ else
+ mapped = c;
+
+ classMap.put(c, mapped);
+ return mapped;
+ }
+}
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a3624f7e/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeSerializerV3d0.java
----------------------------------------------------------------------
diff --cc
gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeSerializerV3d0.java
index 0724e25,0000000..ae1df47
mode 100644,000000..100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeSerializerV3d0.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeSerializerV3d0.java
@@@ -1,167 -1,0 +1,183 @@@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.structure.io.graphson;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Operator;
+import org.apache.tinkerpop.gremlin.process.traversal.Order;
+import org.apache.tinkerpop.gremlin.process.traversal.Path;
+import org.apache.tinkerpop.gremlin.process.traversal.Pop;
+import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
+import org.apache.tinkerpop.gremlin.process.traversal.Scope;
+import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+import
org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
+import org.apache.tinkerpop.gremlin.process.traversal.util.Metrics;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalMetrics;
+import org.apache.tinkerpop.gremlin.structure.Column;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Property;
+import org.apache.tinkerpop.gremlin.structure.T;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.apache.tinkerpop.gremlin.util.function.Lambda;
+import org.apache.tinkerpop.shaded.jackson.core.JsonGenerator;
++import org.apache.tinkerpop.shaded.jackson.core.JsonToken;
++import org.apache.tinkerpop.shaded.jackson.core.type.WritableTypeId;
+import org.apache.tinkerpop.shaded.jackson.databind.jsontype.TypeIdResolver;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * GraphSON 2.0 {@code TypeSerializer}.
+ *
+ * @author Kevin Gallardo (https://kgdo.me)
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class GraphSONTypeSerializerV3d0 extends
AbstractGraphSONTypeSerializer {
+
+ GraphSONTypeSerializerV3d0(final TypeIdResolver idRes, final String
propertyName, final TypeInfo typeInfo,
+ final String valuePropertyName) {
+ super(idRes, propertyName, typeInfo, valuePropertyName);
+ }
+
+ @Override
- public void writeTypePrefixForObject(final Object o, final JsonGenerator
jsonGenerator) throws IOException {
- if (o instanceof Map) {
- writeTypePrefix(jsonGenerator,
getTypeIdResolver().idFromValueAndType(o, getClassFromObject(o)));
- jsonGenerator.writeStartArray();
++ public WritableTypeId writeTypePrefix(final JsonGenerator jsonGenerator,
final WritableTypeId writableTypeId) throws IOException {
++ if (writableTypeId.valueShape == JsonToken.VALUE_STRING) {
++ if (canWriteTypeId()) {
++ writeTypePrefix(jsonGenerator,
getTypeIdResolver().idFromValueAndType(writableTypeId.forValue,
getClassFromObject(writableTypeId.forValue)));
++ }
++ } else if (writableTypeId.valueShape == JsonToken.START_OBJECT) {
++ if (writableTypeId.forValue instanceof Map) {
++ writeTypePrefix(jsonGenerator,
getTypeIdResolver().idFromValueAndType(writableTypeId.forValue,
getClassFromObject(writableTypeId.forValue)));
++ jsonGenerator.writeStartArray();
++ } else {
++ jsonGenerator.writeStartObject();
++ }
++ } else if (writableTypeId.valueShape == JsonToken.START_ARRAY) {
++ if (writableTypeId.forValue instanceof List ||
writableTypeId.forValue instanceof Set) {
++ writeTypePrefix(jsonGenerator,
getTypeIdResolver().idFromValueAndType(writableTypeId.forValue,
getClassFromObject(writableTypeId.forValue)));
++ jsonGenerator.writeStartArray();
++ } else {
++ jsonGenerator.writeStartArray();
++ }
+ } else {
- jsonGenerator.writeStartObject();
++ throw new IllegalStateException("Could not write prefix");
+ }
- }
+
- @Override
- public void writeTypeSuffixForObject(final Object o, final JsonGenerator
jsonGenerator) throws IOException {
- if (o instanceof Map) {
- jsonGenerator.writeEndArray();
- writeTypeSuffix(jsonGenerator);
- } else {
- jsonGenerator.writeEndObject();
- }
++ return writableTypeId;
+ }
+
+ @Override
- public void writeTypePrefixForArray(final Object o, final JsonGenerator
jsonGenerator) throws IOException {
- if (o instanceof List || o instanceof Set) {
- writeTypePrefix(jsonGenerator,
getTypeIdResolver().idFromValueAndType(o, getClassFromObject(o)));
- jsonGenerator.writeStartArray();
++ public WritableTypeId writeTypeSuffix(final JsonGenerator jsonGenerator,
final WritableTypeId writableTypeId) throws IOException {
++ if (writableTypeId.valueShape == JsonToken.VALUE_STRING) {
++ if (canWriteTypeId()) {
++ writeTypeSuffix(jsonGenerator);
++ }
++ } else if (writableTypeId.valueShape == JsonToken.START_OBJECT) {
++ if (writableTypeId.forValue instanceof Map) {
++ jsonGenerator.writeEndArray();
++ writeTypeSuffix(jsonGenerator);
++ } else {
++ jsonGenerator.writeEndObject();
++ }
++ } else if (writableTypeId.valueShape == JsonToken.START_ARRAY) {
++ if (writableTypeId.forValue instanceof List ||
writableTypeId.forValue instanceof Set) {
++ jsonGenerator.writeEndArray();
++ writeTypeSuffix(jsonGenerator);
++ } else {
++ jsonGenerator.writeEndArray();
++ }
+ } else {
- jsonGenerator.writeStartArray();
++ throw new IllegalStateException("Could not write suffix");
+ }
- }
+
- @Override
- public void writeTypeSuffixForArray(final Object o, final JsonGenerator
jsonGenerator) throws IOException {
- if (o instanceof List || o instanceof Set) {
- jsonGenerator.writeEndArray();
- writeTypeSuffix(jsonGenerator);
- } else {
- jsonGenerator.writeEndArray();
- }
++ return writableTypeId;
+ }
+
+ @Override
+ protected Class getClassFromObject(final Object o) {
+ final Class c = o.getClass();
+ if (classMap.containsKey(c))
+ return classMap.get(c);
+
+ final Class mapped;
+ if (Map.class.isAssignableFrom(c)) {
+ if (Tree.class.isAssignableFrom(c))
+ mapped = Tree.class;
+ else
+ mapped = Map.class;
+ } else if (List.class.isAssignableFrom(c))
+ mapped = List.class;
+ else if (Set.class.isAssignableFrom(c))
+ mapped = Set.class;
+ else if (Vertex.class.isAssignableFrom(c))
+ mapped = Vertex.class;
+ else if (Edge.class.isAssignableFrom(c))
+ mapped = Edge.class;
+ else if (Path.class.isAssignableFrom(c))
+ mapped = Path.class;
+ else if (VertexProperty.class.isAssignableFrom(c))
+ mapped = VertexProperty.class;
+ else if (Metrics.class.isAssignableFrom(c))
+ mapped = Metrics.class;
+ else if (TraversalMetrics.class.isAssignableFrom(c))
+ mapped = TraversalMetrics.class;
+ else if (Property.class.isAssignableFrom(c))
+ mapped = Property.class;
+ else if (ByteBuffer.class.isAssignableFrom(c))
+ mapped = ByteBuffer.class;
+ else if (InetAddress.class.isAssignableFrom(c))
+ mapped = InetAddress.class;
+ else if (Traverser.class.isAssignableFrom(c))
+ mapped = Traverser.class;
+ else if (Lambda.class.isAssignableFrom(c))
+ mapped = Lambda.class;
+ else if (VertexProperty.Cardinality.class.isAssignableFrom(c))
+ mapped = VertexProperty.Cardinality.class;
+ else if (Column.class.isAssignableFrom(c))
+ mapped = Column.class;
+ else if (Direction.class.isAssignableFrom(c))
+ mapped = Direction.class;
+ else if (Operator.class.isAssignableFrom(c))
+ mapped = Operator.class;
+ else if (Order.class.isAssignableFrom(c))
+ mapped = Order.class;
+ else if (Pop.class.isAssignableFrom(c))
+ mapped = Pop.class;
+ else if (SackFunctions.Barrier.class.isAssignableFrom(c))
+ mapped = SackFunctions.Barrier.class;
+ else if (TraversalOptionParent.Pick.class.isAssignableFrom(c))
+ mapped = TraversalOptionParent.Pick.class;
+ else if (Scope.class.isAssignableFrom(c))
+ mapped = Scope.class;
+ else if (T.class.isAssignableFrom(c))
+ mapped = T.class;
+ else
+ mapped = c;
+
+ classMap.put(c, mapped);
+ return mapped;
+ }
+}
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a3624f7e/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperPartialEmbeddedTypeTest.java
----------------------------------------------------------------------
diff --cc
gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperPartialEmbeddedTypeTest.java
index 7935050,0000000..4e86ebd
mode 100644,000000..100644
---
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperPartialEmbeddedTypeTest.java
+++
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperPartialEmbeddedTypeTest.java
@@@ -1,325 -1,0 +1,358 @@@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.structure.io.graphson;
+
+import
org.apache.tinkerpop.gremlin.process.remote.traversal.DefaultRemoteTraverser;
++import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
+import org.apache.tinkerpop.gremlin.process.traversal.P;
+import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
++import org.apache.tinkerpop.shaded.jackson.databind.JsonMappingException;
+import org.apache.tinkerpop.shaded.jackson.databind.ObjectMapper;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.time.Instant;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import static org.hamcrest.MatcherAssert.assertThat;
++import static org.hamcrest.core.IsInstanceOf.instanceOf;
+import static org.hamcrest.core.StringContains.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * Tests automatic typed serialization/deserialization for GraphSON 2.0+.
+ *
+ * @author Kevin Gallardo (https://kgdo.me)
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+@RunWith(Parameterized.class)
+public class GraphSONMapperPartialEmbeddedTypeTest extends
AbstractGraphSONTest {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static Iterable<Object[]> data() {
+ return Arrays.asList(new Object[][]{
+ {"v2", GraphSONMapper.build().version(GraphSONVersion.V2_0)
+
.addCustomModule(GraphSONXModuleV2d0.build().create(false))
+
.typeInfo(TypeInfo.PARTIAL_TYPES).create().createMapper()},
+ {"v3", GraphSONMapper.build().version(GraphSONVersion.V3_0)
+
.addCustomModule(GraphSONXModuleV3d0.build().create(false))
+
.typeInfo(TypeInfo.PARTIAL_TYPES).create().createMapper()}
+ });
+ }
+
+ @Parameterized.Parameter(1)
+ public ObjectMapper mapper;
+
+
+ @Parameterized.Parameter(0)
+ public String version;
+
+ @Test
++ public void elementOrderShouldNotMatter() throws Exception {
++ final String bytecodeJSONFail1 =
"{\"@type\":\"g:Bytecode\",\"@value\":{\"step\":[[\"addV\",\"poc_int\"],[\"property\",\"bigint1value\",{\"@type\":\"g:Int32\",\"@value\":-4294967295}]]}}";
++ final String bytecodeJSONFail2 =
"{\"@value\":{\"step\":[[\"addV\",\"poc_int\"],[\"property\",\"bigint1value\",{\"@value\":-4294967295,\"@type\":\"g:Int32\"}]]},\"@type\":\"g:Bytecode\"}";
++
++ // first validate the failures of TINKERPOP-1738 - prior to the
jackson fix on 2.9.4 one of these would have
++ // passed based on the ordering of the properties
++ try {
++ mapper.readValue(bytecodeJSONFail1, Bytecode.class);
++ fail("Should have thrown an error because 'bigint1value' is not
an int32");
++ } catch (Exception ex) {
++ assertThat(ex, instanceOf(JsonMappingException.class));
++ }
++
++ try {
++ mapper.readValue(bytecodeJSONFail2, Bytecode.class);
++ fail("Should have thrown an error because 'bigint1value' is not
an int32");
++ } catch (Exception ex) {
++ assertThat(ex, instanceOf(JsonMappingException.class));
++ }
++
++ // now do a legit parsing based on order
++ final String bytecodeJSON1 =
"{\"@type\":\"g:Bytecode\",\"@value\":{\"step\":[[\"addV\",\"poc_int\"],[\"property\",\"bigint1value\",{\"@type\":\"g:Int64\",\"@value\":-4294967295}]]}}";
++ final String bytecodeJSON2 =
"{\"@value\":{\"step\":[[\"addV\",\"poc_int\"],[\"property\",\"bigint1value\",{\"@value\":-4294967295,\"@type\":\"g:Int64\"}]]},\"@type\":\"g:Bytecode\"}";
++
++ final Bytecode bytecode1 = mapper.readValue(bytecodeJSON1,
Bytecode.class);
++ final Bytecode bytecode2 = mapper.readValue(bytecodeJSON2,
Bytecode.class);
++ assertEquals(bytecode1, bytecode2);
++ }
++
++ @Test
+ public void
shouldSerializeDeserializeNestedCollectionsAndMapAndTypedValuesCorrectly()
throws Exception {
+ // Trying to fail the TypeDeserializer type detection
+ final UUID uuid = UUID.randomUUID();
+ final List<Object> myList = new ArrayList<>();
+
+ final List<Object> myList2 = new ArrayList<>();
+ myList2.add(UUID.randomUUID());
+ myList2.add(33L);
+ myList2.add(84);
+ final Map<String,Object> map2 = new HashMap<>();
+ map2.put("eheh", UUID.randomUUID());
+ map2.put("normal", "normal");
+ myList2.add(map2);
+
+ final Map<String, Object> map1 = new HashMap<>();
+ map1.put("hello", "world");
+ map1.put("test", uuid);
+ map1.put("hehe", myList2);
+ myList.add(map1);
+
+ myList.add("kjkj");
+ myList.add(UUID.randomUUID());
+ assertEquals(myList, serializeDeserializeAuto(mapper, myList));
+
+ // no "@value" property
+ String s = "{\""+GraphSONTokens.VALUETYPE+"\":\"" +
GraphSONTokens.GREMLIN_TYPE_NAMESPACE + ":UUID\", \"test\":2}";
+ Map<String,Object> map = new LinkedHashMap<>();
+ map.put(GraphSONTokens.VALUETYPE,
GraphSONTokens.GREMLIN_TYPE_NAMESPACE + ":UUID");
+ map.put("test", 2);
+ Object res = mapper.readValue(s, Object.class);
+ assertEquals(map, res);
+
+ // "@value" and "@type" property reversed
+ s = "{\""+GraphSONTokens.VALUEPROP+"\":2, \"" +
GraphSONTokens.VALUETYPE + "\":\"" + GraphSONTokens.GREMLIN_TYPE_NAMESPACE +
":Int64\"}";
+ res = mapper.readValue(s, Object.class);
+ assertEquals(res, 2L);
+ assertEquals(res.getClass(), Long.class);
+
+ // no "@type" property.
+ s = "{\""+GraphSONTokens.VALUEPROP + "\":2, \"id\":2}";
+ map = new LinkedHashMap<>();
+ map.put(GraphSONTokens.VALUEPROP, 2);
+ map.put("id", 2);
+ res = mapper.readValue(s, Object.class);
+ assertEquals(res, map);
+ }
+
+ @Test
+ public void shouldFailIfMoreThanTwoPropertiesInATypePattern() {
+ String s = "{\"" + GraphSONTokens.VALUEPROP + "\":2, \"" +
GraphSONTokens.VALUETYPE + "\":\""+GraphSONTokens.GREMLIN_TYPE_NAMESPACE
+":Int64\", \"hello\": \"world\"}";
+ try {
+ mapper.readValue(s, Object.class);
+ fail("Should have failed deserializing because there's more than
properties in the type.");
+ } catch (IOException e) {
+ assertThat(e.getMessage(), containsString("Detected the type
pattern in the JSON payload but the map containing the types and values
contains other fields. This is not allowed by the deserializer."));
+ }
+ s = "{\"" + GraphSONTokens.VALUETYPE +
"\":\""+GraphSONTokens.GREMLIN_TYPE_NAMESPACE +":Int64\",\"" +
GraphSONTokens.VALUEPROP + "\":2, \"hello\": \"world\"}";
+ try {
+ mapper.readValue(s, Object.class);
+ fail("Should have failed deserializing because there's more than
properties in the type.");
+ } catch (IOException e) {
+ assertThat(e.getMessage(), containsString("Detected the type
pattern in the JSON payload but the map containing the types and values
contains other fields. This is not allowed by the deserializer."));
+ }
+ }
+
+ @Test
+ public void shouldFailIfTypeSpecifiedIsNotSameTypeInPayload() {
+ final ZoneOffset o = ZonedDateTime.now().getOffset();
+ final ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ try {
+ mapper.writeValue(stream, o);
+ final InputStream inputStream = new
ByteArrayInputStream(stream.toByteArray());
+ // What has been serialized is a ZoneOffset with the type, but
the user explicitly requires another type.
+ mapper.readValue(inputStream, Instant.class);
+ fail("Should have failed decoding the value");
+ } catch (Exception e) {
+ assertThat(e.getMessage(), containsString("Could not deserialize
the JSON value as required. Nested exception: java.lang.InstantiationException:
Cannot deserialize the value with the detected type contained in the JSON ('" +
GraphSONTokens.GREMLINX_TYPE_NAMESPACE + ":ZoneOffset') to the type specified
in parameter to the object mapper (class java.time.Instant). Those types are
incompatible."));
+ }
+ }
+
+ @Test
+ public void shouldHandleRawPOJOs() throws Exception {
+ final FunObject funObject = new FunObject();
+ funObject.setVal("test");
+ assertEquals(funObject.toString(), serializeDeserialize(mapper,
funObject, FunObject.class).toString());
+ assertEquals(funObject.getClass(), serializeDeserialize(mapper,
funObject, FunObject.class).getClass());
+ }
+
+ @Test
+ public void shouldHandleMapWithTypesUsingEmbedTypeSettingV2d0() throws
Exception {
+ final ObjectMapper mapper = GraphSONMapper.build()
+ .version(GraphSONVersion.V2_0)
+ .typeInfo(TypeInfo.PARTIAL_TYPES)
+ .create()
+ .createMapper();
+
+ final Map<String,Object> m = new HashMap<>();
+ m.put("test", 100L);
+
+ final String json = mapper.writeValueAsString(m);
+ final Map read = mapper.readValue(json, HashMap.class);
+
+ assertEquals(100L, read.get("test"));
+ }
+
+ @Test
+ public void shouldNotHandleMapWithTypesUsingEmbedTypeSettingV2d0() throws
Exception {
+ final ObjectMapper mapper = GraphSONMapper.build()
+ .version(GraphSONVersion.V2_0)
+ .typeInfo(TypeInfo.NO_TYPES)
+ .create()
+ .createMapper();
+
+ final Map<String,Object> m = new HashMap<>();
+ m.put("test", 100L);
+
+ final String json = mapper.writeValueAsString(m);
+ final Map read = mapper.readValue(json, HashMap.class);
+
+ assertEquals(100, read.get("test"));
+ }
+
+ @Test
+ public void shouldHandleMapWithTypesUsingEmbedTypeSettingV1d0() throws
Exception {
+ final ObjectMapper mapper = GraphSONMapper.build()
+ .version(GraphSONVersion.V1_0)
+ .typeInfo(TypeInfo.PARTIAL_TYPES)
+ .create()
+ .createMapper();
+
+ final Map<String,Object> m = new HashMap<>();
+ m.put("test", 100L);
+
+ final String json = mapper.writeValueAsString(m);
+ final Map read = mapper.readValue(json, HashMap.class);
+
+ assertEquals(100L, read.get("test"));
+ }
+
+ @Test
+ public void shouldNotHandleMapWithTypesUsingEmbedTypeSettingV1d0() throws
Exception {
+ final ObjectMapper mapper = GraphSONMapper.build()
+ .version(GraphSONVersion.V1_0)
+ .typeInfo(TypeInfo.NO_TYPES)
+ .create()
+ .createMapper();
+
+ final Map<String,Object> m = new HashMap<>();
+ m.put("test", 100L);
+
+ final String json = mapper.writeValueAsString(m);
+ final Map read = mapper.readValue(json, HashMap.class);
+
+ assertEquals(100, read.get("test"));
+ }
+
+ @Test
+ public void shouldLooseTypesInfoWithGraphSONNoType() throws Exception {
+ final ObjectMapper mapper = GraphSONMapper.build()
+ .version(GraphSONVersion.V2_0)
+ .typeInfo(TypeInfo.NO_TYPES)
+ .create()
+ .createMapper();
+
+ final UUID uuid = UUID.randomUUID();
+ final List<Object> myList = new ArrayList<>();
+
+ final List<Object> myList2 = new ArrayList<>();
+ myList2.add(UUID.randomUUID());
+ myList2.add(33L);
+ myList2.add(84);
+ final Map<String,Object> map2 = new HashMap<>();
+ map2.put("eheh", UUID.randomUUID());
+ map2.put("normal", "normal");
+ myList2.add(map2);
+
+ final Map<String, Object> map1 = new HashMap<>();
+ map1.put("hello", "world");
+ map1.put("test", uuid);
+ map1.put("hehe", myList2);
+ myList.add(map1);
+
+ myList.add("kjkj");
+ myList.add(UUID.randomUUID());
+
+ final String json = mapper.writeValueAsString(myList);
+ final Object read = mapper.readValue(json, Object.class);
+
+ // Not equals because of type loss
+ assertNotEquals(myList, read);
+ }
+
+ @Test
+ public void shouldHandleDefaultRemoteTraverser() throws Exception {
+ final DefaultRemoteTraverser<String> o = new
DefaultRemoteTraverser<>("test", 100);
+ assertEquals(o, serializeDeserialize(mapper, o, Traverser.class));
+ }
+
+ @Test
+ public void shouldHandleVariantsOfP() throws Exception {
+ final List<P> variantsOfP = Arrays.asList(
+ P.between(1,2),
+ P.eq(1),
+ P.gt(1),
+ P.gte(1),
+ P.inside(1,2),
+ P.lt(1),
+ P.lte(1),
+ P.neq(1),
+ P.not(P.eq(1)),
+ P.outside(1,2),
+ P.within(1),
+ P.within(1,2,3,4),
+ P.within(Arrays.asList(1,2,3,4)),
+ P.without(1),
+ P.without(1,2,3,4),
+ P.without(Arrays.asList(1,2,3,4)),
+ P.eq(1).and(P.eq(2)),
+ P.eq(1).or(P.eq(2)));
+
+ for (P p : variantsOfP) {
+ assertEquals(p, serializeDeserialize(mapper, p, P.class));
+ }
+ }
+
+ // Class needs to be defined as statics as it's a nested class.
+ public static class FunObject {
+ private String val;
+
+ public FunObject() {
+ }
+
+ public String getVal() {
+ return this.val;
+ }
+
+ public void setVal(String s) {
+ this.val = s;
+ }
+
+ public String toString() {
+ return this.val;
+ }
+ }
+
+
+}
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a3624f7e/gremlin-shaded/pom.xml
----------------------------------------------------------------------