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 <sp...@genoprime.com> Authored: Thu Mar 8 08:08:40 2018 -0500 Committer: Stephen Mallette <sp...@genoprime.com> 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 ----------------------------------------------------------------------