TINKERPOP-1427 Added g:Map for GraphSON 3.0
Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/60874a59 Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/60874a59 Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/60874a59 Branch: refs/heads/master Commit: 60874a59dd0471bcad2c171dd37e5398b15e3f5b Parents: 12bbbf4 Author: Stephen Mallette <sp...@genoprime.com> Authored: Sat Jul 1 14:16:57 2017 -0400 Committer: Stephen Mallette <sp...@genoprime.com> Committed: Thu Jul 13 12:52:04 2017 -0400 ---------------------------------------------------------------------- .../AbstractGraphSONTypeSerializer.java | 173 +++++++++++ .../structure/io/graphson/GraphSONMapper.java | 5 +- .../structure/io/graphson/GraphSONModule.java | 11 +- .../structure/io/graphson/GraphSONReader.java | 9 +- .../io/graphson/GraphSONSerializerProvider.java | 1 - .../io/graphson/GraphSONSerializersV2d0.java | 2 +- .../io/graphson/GraphSONSerializersV3d0.java | 28 +- .../graphson/GraphSONTypeResolverBuilder.java | 10 +- .../io/graphson/GraphSONTypeSerializer.java | 245 --------------- .../io/graphson/GraphSONTypeSerializerV2d0.java | 112 +++++++ .../io/graphson/GraphSONTypeSerializerV3d0.java | 139 +++++++++ .../io/graphson/GraphSONXModuleV2d0.java | 8 +- .../io/graphson/GraphSONXModuleV3d0.java | 8 +- .../io/graphson/JavaTimeSerializersV3d0.java | 5 + .../io/graphson/JavaUtilSerializersV3d0.java | 109 +++++-- .../io/graphson/TraversalSerializersV3d0.java | 146 +++++++-- .../star/StarGraphGraphSONSerializerV3d0.java | 165 +++++++++++ .../GraphSONMapperEmbeddedTypeTest.java | 39 ++- .../GraphSONMapperPartialEmbeddedTypeTest.java | 297 +++++++++++++++++++ .../io/graphson/GraphSONMapperTest.java | 3 + ...aphSONMapperV2d0PartialEmbeddedTypeTest.java | 278 ----------------- .../ser/GraphSONMessageSerializerV3d0Test.java | 25 +- .../jsr223/PythonGraphSONJavaTranslator.java | 5 +- .../server/GremlinDriverIntegrateTest.java | 34 +++ .../GremlinServerAuditLogIntegrateTest.java | 2 + .../server/GremlinServerHttpIntegrateTest.java | 2 +- .../server/GremlinServerIntegrateTest.java | 2 +- .../gremlin/structure/io/IoCustomTest.java | 7 +- .../tinkerpop/gremlin/structure/io/IoTest.java | 4 +- .../gremlin/structure/io/util/CustomId.java | 64 +++- .../GraphSONTypedCompatibilityTest.java | 7 +- .../_3_3_0/authenticationchallenge-v3d0.json | 10 +- .../_3_3_0/authenticationresponse-v3d0.json | 4 +- .../io/graphson/_3_3_0/metrics-v3d0.json | 55 ++-- .../structure/io/graphson/_3_3_0/path-v3d0.json | 122 +------- .../io/graphson/_3_3_0/sessionclose-v3d0.json | 5 +- .../io/graphson/_3_3_0/sessioneval-v3d0.json | 15 +- .../_3_3_0/sessionevalaliased-v3d0.json | 21 +- .../graphson/_3_3_0/sessionlesseval-v3d0.json | 12 +- .../_3_3_0/sessionlessevalaliased-v3d0.json | 18 +- .../io/graphson/_3_3_0/standardresult-v3d0.json | 10 +- .../graphson/_3_3_0/traversalmetrics-v3d0.json | 112 ++++--- .../structure/io/graphson/_3_3_0/tree-v3d0.json | 8 +- .../structure/TinkerIoRegistryV3d0.java | 3 +- 44 files changed, 1443 insertions(+), 897 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/60874a59/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/AbstractGraphSONTypeSerializer.java ---------------------------------------------------------------------- diff --git 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 new file mode 100644 index 0000000..6eb65e1 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/AbstractGraphSONTypeSerializer.java @@ -0,0 +1,173 @@ +/* + * 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.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; + } + + @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); +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/60874a59/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapper.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapper.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapper.java index 6b6bbf7..eb3c9ad 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapper.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapper.java @@ -94,9 +94,12 @@ public class GraphSONMapper implements Mapper<ObjectMapper> { om.findAndRegisterModules(); // graphson 3.0 only allows type - there is no option to remove embedded types + if (version == GraphSONVersion.V3_0 && typeInfo == TypeInfo.NO_TYPES) + throw new IllegalStateException(String.format("GraphSON 3.0 does not support %s", TypeInfo.NO_TYPES)); + if (version == GraphSONVersion.V3_0 || (version == GraphSONVersion.V2_0 && typeInfo != TypeInfo.NO_TYPES)) { final GraphSONTypeIdResolver graphSONTypeIdResolver = new GraphSONTypeIdResolver(); - final TypeResolverBuilder typer = new GraphSONTypeResolverBuilder() + final TypeResolverBuilder typer = new GraphSONTypeResolverBuilder(version) .typesEmbedding(getTypeInfo()) .valuePropertyName(GraphSONTokens.VALUEPROP) .init(JsonTypeInfo.Id.CUSTOM, graphSONTypeIdResolver) http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/60874a59/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java index 019112b..22dc6b2 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java @@ -71,6 +71,7 @@ import org.apache.tinkerpop.gremlin.structure.VertexProperty; import org.apache.tinkerpop.gremlin.structure.util.star.DirectionalStarGraph; import org.apache.tinkerpop.gremlin.structure.util.star.StarGraphGraphSONSerializerV1d0; import org.apache.tinkerpop.gremlin.structure.util.star.StarGraphGraphSONSerializerV2d0; +import org.apache.tinkerpop.gremlin.structure.util.star.StarGraphGraphSONSerializerV3d0; import org.apache.tinkerpop.gremlin.util.function.Lambda; import java.time.Duration; @@ -122,6 +123,9 @@ abstract class GraphSONModule extends TinkerPopJacksonModule { put(Double.class, "Double"); put(Float.class, "Float"); + put(Map.Entry.class, "Entry"); + put(Map.class, "Map"); + // Tinkerpop Graph objects put(Lambda.class, "Lambda"); put(Vertex.class, "Vertex"); @@ -196,11 +200,12 @@ abstract class GraphSONModule extends TinkerPopJacksonModule { addSerializer(TraversalMetrics.class, new GraphSONSerializersV3d0.TraversalMetricsJacksonSerializer()); addSerializer(TraversalExplanation.class, new GraphSONSerializersV3d0.TraversalExplanationJacksonSerializer()); addSerializer(Path.class, new GraphSONSerializersV3d0.PathJacksonSerializer()); - addSerializer(DirectionalStarGraph.class, new StarGraphGraphSONSerializerV2d0(normalize)); + addSerializer(DirectionalStarGraph.class, new StarGraphGraphSONSerializerV3d0(normalize)); addSerializer(Tree.class, new GraphSONSerializersV3d0.TreeJacksonSerializer()); // java.util addSerializer(Map.Entry.class, new JavaUtilSerializersV3d0.MapEntryJacksonSerializer()); + addSerializer(Map.class, new JavaUtilSerializersV3d0.MapJacksonSerializer()); // need to explicitly add serializers for those types because Jackson doesn't do it at all. addSerializer(Integer.class, new GraphSONSerializersV3d0.IntegerGraphSONSerializer()); @@ -237,6 +242,10 @@ abstract class GraphSONModule extends TinkerPopJacksonModule { addDeserializer(TraversalMetrics.class, new GraphSONSerializersV3d0.TraversalMetricsJacksonDeserializer()); addDeserializer(Tree.class, new GraphSONSerializersV3d0.TreeJacksonDeserializer()); + // java.util + addDeserializer(Map.Entry.class, new JavaUtilSerializersV3d0.MapEntryJacksonDeserializer()); + addDeserializer(Map.class, new JavaUtilSerializersV3d0.MapJacksonDeserializer()); + // numbers addDeserializer(Integer.class, new GraphSONSerializersV3d0.IntegerJackonsDeserializer()); addDeserializer(Double.class, new GraphSONSerializersV3d0.DoubleJackonsDeserializer()); http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/60874a59/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONReader.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONReader.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONReader.java index 3f63b96..056d3c8 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONReader.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONReader.java @@ -52,6 +52,7 @@ import java.io.OutputStream; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Function; @@ -73,8 +74,8 @@ public final class GraphSONReader implements GraphReader { private final GraphSONVersion version; private boolean unwrapAdjacencyList = false; - final TypeReference<Map<String, Object>> mapTypeReference = new TypeReference<Map<String, Object>>() { - }; + final TypeReference<Map<String, Object>> mapTypeReference = new TypeReference<Map<String, Object>>() {}; + final TypeReference<LinkedHashMap<String, Object>> linkedHashMapTypeReference = new TypeReference<LinkedHashMap<String, Object>>() {}; private GraphSONReader(final Builder builder) { mapper = builder.mapper.createMapper(); @@ -173,7 +174,9 @@ public final class GraphSONReader implements GraphReader { final Function<Attachable<Vertex>, Vertex> vertexAttachMethod, final Function<Attachable<Edge>, Edge> edgeAttachMethod, final Direction attachEdgesOfThisDirection) throws IOException { - final Map<String, Object> vertexData = mapper.readValue(inputStream, mapTypeReference); + // graphson v3 has special handling for generic Map instances, by forcing to linkedhashmap (which is probably + // what it should have been anyway) stargraph format can remain unchanged across all versions + final Map<String, Object> vertexData = mapper.readValue(inputStream, version == GraphSONVersion.V3_0 ? linkedHashMapTypeReference : mapTypeReference); final StarGraph starGraph = StarGraphGraphSONDeserializer.readStarGraphVertex(vertexData); if (vertexAttachMethod != null) vertexAttachMethod.apply(starGraph.getStarVertex()); http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/60874a59/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializerProvider.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializerProvider.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializerProvider.java index bd7f966..7744b79 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializerProvider.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializerProvider.java @@ -44,7 +44,6 @@ final class GraphSONSerializerProvider extends DefaultSerializerProvider { setDefaultKeySerializer(new GraphSONSerializersV2d0.GraphSONKeySerializer()); unknownTypeSerializer = new ToStringGraphSONSerializer(); } else { - setDefaultKeySerializer(new GraphSONSerializersV3d0.GraphSONKeySerializer()); unknownTypeSerializer = new ToStringGraphSONSerializer(); } } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/60874a59/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV2d0.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV2d0.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV2d0.java index 095bfe6..717dcfa 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV2d0.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV2d0.java @@ -703,4 +703,4 @@ class GraphSONSerializersV2d0 { return true; } } -} +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/60874a59/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV3d0.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV3d0.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV3d0.java index 6f6e011..5fe8e74 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV3d0.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV3d0.java @@ -64,6 +64,7 @@ import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -610,20 +611,21 @@ class GraphSONSerializersV3d0 { } } - static class MetricsJacksonDeserializer extends AbstractObjectDeserializer<Metrics> { + static class MetricsJacksonDeserializer extends StdDeserializer<Metrics> { public MetricsJacksonDeserializer() { super(Metrics.class); } @Override - public Metrics createObject(final Map<String, Object> metricsData) { + public Metrics deserialize(final JsonParser jsonParser, final DeserializationContext deserializationContext) throws IOException, JsonProcessingException { + final Map<String, Object> metricsData = deserializationContext.readValue(jsonParser, Map.class); final MutableMetrics m = new MutableMetrics((String)metricsData.get(GraphSONTokens.ID), (String)metricsData.get(GraphSONTokens.NAME)); m.setDuration(Math.round((Double) metricsData.get(GraphSONTokens.DURATION) * 1000000), TimeUnit.NANOSECONDS); - for (Map.Entry<String, Long> count : ((Map<String, Long>)metricsData.getOrDefault(GraphSONTokens.COUNTS, new HashMap<>(0))).entrySet()) { + for (Map.Entry<String, Long> count : ((Map<String, Long>)metricsData.getOrDefault(GraphSONTokens.COUNTS, new LinkedHashMap<>(0))).entrySet()) { m.setCount(count.getKey(), count.getValue()); } - for (Map.Entry<String, Long> count : ((Map<String, Long>) metricsData.getOrDefault(GraphSONTokens.ANNOTATIONS, new HashMap<>(0))).entrySet()) { + for (Map.Entry<String, Long> count : ((Map<String, Long>) metricsData.getOrDefault(GraphSONTokens.ANNOTATIONS, new LinkedHashMap<>(0))).entrySet()) { m.setAnnotation(count.getKey(), count.getValue()); } for (MutableMetrics nested : (List<MutableMetrics>)metricsData.getOrDefault(GraphSONTokens.METRICS, new ArrayList<>(0))) { @@ -631,21 +633,33 @@ class GraphSONSerializersV3d0 { } return m; } + + @Override + public boolean isCachable() { + return true; + } } - static class TraversalMetricsJacksonDeserializer extends AbstractObjectDeserializer<TraversalMetrics> { + static class TraversalMetricsJacksonDeserializer extends StdDeserializer<TraversalMetrics> { public TraversalMetricsJacksonDeserializer() { super(TraversalMetrics.class); } @Override - public TraversalMetrics createObject(final Map<String, Object> traversalMetricsData) { + public TraversalMetrics deserialize(final JsonParser jsonParser, final DeserializationContext deserializationContext) throws IOException, JsonProcessingException { + final Map<String, Object> traversalMetricsData = deserializationContext.readValue(jsonParser, Map.class); + return new DefaultTraversalMetrics( Math.round((Double) traversalMetricsData.get(GraphSONTokens.DURATION) * 1000000), (List<MutableMetrics>) traversalMetricsData.get(GraphSONTokens.METRICS) ); } + + @Override + public boolean isCachable() { + return true; + } } static class TreeJacksonDeserializer extends StdDeserializer<Tree> { @@ -703,4 +717,4 @@ class GraphSONSerializersV3d0 { return true; } } -} +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/60874a59/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeResolverBuilder.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeResolverBuilder.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeResolverBuilder.java index 7d3d03c..0da512e 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeResolverBuilder.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeResolverBuilder.java @@ -34,11 +34,17 @@ import java.util.Collection; * deserializers. Contains the typeInfo level that should be provided by the GraphSONMapper. * * @author Kevin Gallardo (https://kgdo.me) + * @author Stephen Mallette (http://stephen.genoprime.com) */ public class GraphSONTypeResolverBuilder extends StdTypeResolverBuilder { private TypeInfo typeInfo; private String valuePropertyName; + private final GraphSONVersion version; + + public GraphSONTypeResolverBuilder(final GraphSONVersion version) { + this.version = version; + } @Override public TypeDeserializer buildTypeDeserializer(final DeserializationConfig config, final JavaType baseType, @@ -52,7 +58,9 @@ public class GraphSONTypeResolverBuilder extends StdTypeResolverBuilder { public TypeSerializer buildTypeSerializer(final SerializationConfig config, final JavaType baseType, final Collection<NamedType> subtypes) { final TypeIdResolver idRes = this.idResolver(config, baseType, subtypes, true, false); - return new GraphSONTypeSerializer(idRes, this.getTypeProperty(), typeInfo, valuePropertyName); + return version == GraphSONVersion.V2_0 ? + new GraphSONTypeSerializerV2d0(idRes, this.getTypeProperty(), typeInfo, valuePropertyName) : + new GraphSONTypeSerializerV3d0(idRes, this.getTypeProperty(), typeInfo, valuePropertyName); } public GraphSONTypeResolverBuilder valuePropertyName(final String valuePropertyName) { http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/60874a59/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeSerializer.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeSerializer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeSerializer.java deleted file mode 100644 index 78c670a..0000000 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeSerializer.java +++ /dev/null @@ -1,245 +0,0 @@ -/* - * 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.HashMapSupplier; -import org.apache.tinkerpop.gremlin.util.function.Lambda; -import org.apache.tinkerpop.shaded.jackson.annotation.JsonTypeInfo; -import org.apache.tinkerpop.shaded.jackson.core.JsonGenerator; -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.net.InetAddress; -import java.nio.ByteBuffer; -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) - */ -public class GraphSONTypeSerializer extends TypeSerializer { - - private final TypeIdResolver idRes; - private final String propertyName; - private final TypeInfo typeInfo; - private final String valuePropertyName; - private final Map<Class, Class> classMap = new HashMap<>(); - - GraphSONTypeSerializer(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; - } - - @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); - } - - private boolean canWriteTypeId() { - return typeInfo != null - && typeInfo == TypeInfo.PARTIAL_TYPES; - } - - private void writeTypePrefix(final JsonGenerator jsonGenerator, final String s) throws IOException { - jsonGenerator.writeStartObject(); - jsonGenerator.writeStringField(this.getPropertyName(), s); - jsonGenerator.writeFieldName(this.valuePropertyName); - } - - private 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..) - */ - private 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/60874a59/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeSerializerV2d0.java ---------------------------------------------------------------------- diff --git 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 new file mode 100644 index 0000000..993f110 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeSerializerV2d0.java @@ -0,0 +1,112 @@ +/* + * 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.databind.jsontype.TypeIdResolver; + +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 + 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/60874a59/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeSerializerV3d0.java ---------------------------------------------------------------------- diff --git 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 new file mode 100644 index 0000000..246d38f --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeSerializerV3d0.java @@ -0,0 +1,139 @@ +/* + * 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.databind.jsontype.TypeIdResolver; + +import java.io.IOException; +import java.net.InetAddress; +import java.nio.ByteBuffer; +import java.util.Map; + +/** + * 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 || o instanceof Map.Entry) { + writeTypePrefix(jsonGenerator, getTypeIdResolver().idFromValueAndType(o, getClassFromObject(o))); + jsonGenerator.writeStartArray(); + } else { + jsonGenerator.writeStartObject(); + } + } + + @Override + public void writeTypeSuffixForObject(final Object o, final JsonGenerator jsonGenerator) throws IOException { + if (o instanceof Map || o instanceof Map.Entry) { + jsonGenerator.writeEndArray(); + writeTypeSuffix(jsonGenerator); + } else { + jsonGenerator.writeEndObject(); + } + } + + @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)) + mapped = Map.class; + else if (Map.Entry.class.isAssignableFrom(c)) + mapped = Map.Entry.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/60874a59/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONXModuleV2d0.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONXModuleV2d0.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONXModuleV2d0.java index 336d11c..0d4ba53 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONXModuleV2d0.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONXModuleV2d0.java @@ -18,10 +18,6 @@ */ package org.apache.tinkerpop.gremlin.structure.io.graphson; -/** - * @author Stephen Mallette (http://stephen.genoprime.com) - */ - import java.math.BigDecimal; import java.math.BigInteger; import java.net.InetAddress; @@ -45,6 +41,8 @@ import java.util.Map; /** * Version 2.0 of GraphSON extensions. + * + * @author Stephen Mallette (http://stephen.genoprime.com) */ public final class GraphSONXModuleV2d0 extends GraphSONModule { @@ -139,4 +137,4 @@ public final class GraphSONXModuleV2d0 extends GraphSONModule { return new GraphSONXModuleV2d0(normalize); } } -} +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/60874a59/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONXModuleV3d0.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONXModuleV3d0.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONXModuleV3d0.java index 777c6c4..38caa09 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONXModuleV3d0.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONXModuleV3d0.java @@ -18,10 +18,6 @@ */ package org.apache.tinkerpop.gremlin.structure.io.graphson; -/** - * @author Stephen Mallette (http://stephen.genoprime.com) - */ - import java.math.BigDecimal; import java.math.BigInteger; import java.net.InetAddress; @@ -45,6 +41,8 @@ import java.util.Map; /** * Version 3.0 of GraphSON extensions. + * + * @author Stephen Mallette (http://stephen.genoprime.com) */ public final class GraphSONXModuleV3d0 extends GraphSONModule { @@ -139,4 +137,4 @@ public final class GraphSONXModuleV3d0 extends GraphSONModule { return new GraphSONXModuleV3d0(normalize); } } -} +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/60874a59/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/JavaTimeSerializersV3d0.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/JavaTimeSerializersV3d0.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/JavaTimeSerializersV3d0.java index 1cb75e0..c1668e2 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/JavaTimeSerializersV3d0.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/JavaTimeSerializersV3d0.java @@ -85,6 +85,11 @@ final class JavaTimeSerializersV3d0 { public T deserialize(final JsonParser jsonParser, final DeserializationContext deserializationContext) throws IOException { return parse(jsonParser.getText()); } + + @Override + public boolean isCachable() { + return true; + } } final static class DurationJacksonSerializer extends AbstractJavaTimeSerializer<Duration> { http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/60874a59/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/JavaUtilSerializersV3d0.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/JavaUtilSerializersV3d0.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/JavaUtilSerializersV3d0.java index 816a3f7..900eeb8 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/JavaUtilSerializersV3d0.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/JavaUtilSerializersV3d0.java @@ -18,16 +18,22 @@ */ package org.apache.tinkerpop.gremlin.structure.io.graphson; -import org.apache.tinkerpop.gremlin.structure.Element; import org.apache.tinkerpop.shaded.jackson.core.JsonGenerator; -import org.apache.tinkerpop.shaded.jackson.databind.SerializationFeature; +import org.apache.tinkerpop.shaded.jackson.core.JsonParser; +import org.apache.tinkerpop.shaded.jackson.core.JsonProcessingException; +import org.apache.tinkerpop.shaded.jackson.core.JsonToken; +import org.apache.tinkerpop.shaded.jackson.databind.DeserializationContext; import org.apache.tinkerpop.shaded.jackson.databind.SerializerProvider; +import org.apache.tinkerpop.shaded.jackson.databind.deser.std.StdDeserializer; import org.apache.tinkerpop.shaded.jackson.databind.jsontype.TypeSerializer; +import org.apache.tinkerpop.shaded.jackson.databind.ser.std.StdScalarSerializer; import org.apache.tinkerpop.shaded.jackson.databind.ser.std.StdSerializer; import java.io.IOException; -import java.util.Date; +import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; +import java.util.Set; /** * GraphSON serializers for classes in {@code java.util.*} for the version 3.0 of GraphSON. @@ -36,6 +42,31 @@ final class JavaUtilSerializersV3d0 { private JavaUtilSerializersV3d0() {} + ////////////////////////////// SERIALIZERS ///////////////////////////////// + + final static class MapJacksonSerializer extends StdSerializer<Map> { + public MapJacksonSerializer() { + super(Map.class); + } + + @Override + public void serialize(final Map map, final JsonGenerator jsonGenerator, final SerializerProvider serializerProvider) + throws IOException { + for(Map.Entry entry : (Set<Map.Entry>) map.entrySet()) { + jsonGenerator.writeObject(entry.getKey()); + jsonGenerator.writeObject(entry.getValue()); + } + } + + @Override + public void serializeWithType(final Map map, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider, final TypeSerializer typeSerializer) throws IOException { + typeSerializer.writeTypePrefixForObject(map, jsonGenerator); + serialize(map, jsonGenerator, serializerProvider); + typeSerializer.writeTypeSuffixForObject(map, jsonGenerator); + } + } + final static class MapEntryJacksonSerializer extends StdSerializer<Map.Entry> { public MapEntryJacksonSerializer() { @@ -45,41 +76,61 @@ final class JavaUtilSerializersV3d0 { @Override public void serialize(final Map.Entry entry, final JsonGenerator jsonGenerator, final SerializerProvider serializerProvider) throws IOException { - jsonGenerator.writeStartObject(); - ser(entry, jsonGenerator, serializerProvider); - jsonGenerator.writeEndObject(); + jsonGenerator.writeObject(entry.getKey()); + jsonGenerator.writeObject(entry.getValue()); } @Override public void serializeWithType(final Map.Entry entry, final JsonGenerator jsonGenerator, final SerializerProvider serializerProvider, final TypeSerializer typeSerializer) throws IOException { typeSerializer.writeTypePrefixForObject(entry, jsonGenerator); - ser(entry, jsonGenerator, serializerProvider); + serialize(entry, jsonGenerator, serializerProvider); typeSerializer.writeTypeSuffixForObject(entry, jsonGenerator); } + } + + ////////////////////////////// DESERIALIZERS ///////////////////////////////// + + + static class MapJacksonDeserializer extends StdDeserializer<Map> { + + protected MapJacksonDeserializer() { + super(Map.class); + } + + @Override + public Map deserialize(final JsonParser jsonParser, final DeserializationContext deserializationContext) throws IOException, JsonProcessingException { + final Map<Object,Object> m = new LinkedHashMap<>(); + + while (jsonParser.nextToken() != JsonToken.END_ARRAY) { + final Object key = deserializationContext.readValue(jsonParser, Object.class); + jsonParser.nextToken(); + final Object val = deserializationContext.readValue(jsonParser, Object.class); + m.put(key, val); + } + + return m; + } + } + + static class MapEntryJacksonDeserializer extends StdDeserializer<Map.Entry> { + + protected MapEntryJacksonDeserializer() { + super(Map.Entry.class); + } + + @Override + public Map.Entry deserialize(final JsonParser jsonParser, final DeserializationContext deserializationContext) throws IOException, JsonProcessingException { + final Map<Object,Object> m = new HashMap<>(); + + while (jsonParser.nextToken() != JsonToken.END_ARRAY) { + final Object key = deserializationContext.readValue(jsonParser, Object.class); + jsonParser.nextToken(); + final Object val = deserializationContext.readValue(jsonParser, Object.class); + m.put(key, val); + } - private static void ser(final Map.Entry entry, final JsonGenerator jsonGenerator, - final SerializerProvider serializerProvider) throws IOException { - // this treatment of keys is consistent with the current GraphSONKeySerializer which extends the - // StdKeySerializer - final Object key = entry.getKey(); - final Class cls = key.getClass(); - String k; - if (cls == String.class) - k = (String) key; - else if (Element.class.isAssignableFrom(cls)) - k = ((Element) key).id().toString(); - else if(Date.class.isAssignableFrom(cls)) { - if (serializerProvider.isEnabled(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS)) - k = String.valueOf(((Date) key).getTime()); - else - k = serializerProvider.getConfig().getDateFormat().format((Date) key); - } else if(cls == Class.class) - k = ((Class) key).getName(); - else - k = key.toString(); - - serializerProvider.defaultSerializeField(k, entry.getValue(), jsonGenerator); + return m.entrySet().iterator().next(); } } } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/60874a59/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TraversalSerializersV3d0.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TraversalSerializersV3d0.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TraversalSerializersV3d0.java index e4f6df9..57b6736 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TraversalSerializersV3d0.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TraversalSerializersV3d0.java @@ -35,14 +35,19 @@ import org.apache.tinkerpop.gremlin.util.function.Lambda; import org.apache.tinkerpop.shaded.jackson.core.JsonGenerator; import org.apache.tinkerpop.shaded.jackson.core.JsonParser; import org.apache.tinkerpop.shaded.jackson.core.JsonProcessingException; +import org.apache.tinkerpop.shaded.jackson.core.JsonToken; import org.apache.tinkerpop.shaded.jackson.databind.DeserializationContext; +import org.apache.tinkerpop.shaded.jackson.databind.JavaType; +import org.apache.tinkerpop.shaded.jackson.databind.JsonNode; import org.apache.tinkerpop.shaded.jackson.databind.SerializerProvider; import org.apache.tinkerpop.shaded.jackson.databind.deser.std.StdDeserializer; import org.apache.tinkerpop.shaded.jackson.databind.jsontype.TypeSerializer; import org.apache.tinkerpop.shaded.jackson.databind.ser.std.StdScalarSerializer; import org.apache.tinkerpop.shaded.jackson.databind.ser.std.StdSerializer; +import org.apache.tinkerpop.shaded.jackson.databind.type.TypeFactory; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; @@ -242,29 +247,40 @@ final class TraversalSerializersV3d0 { // DESERIALIZERS // ////////////////// - final static class BytecodeJacksonDeserializer extends AbstractObjectDeserializer<Bytecode> { + final static class BytecodeJacksonDeserializer extends StdDeserializer<Bytecode> { + private static final JavaType listJavaType = TypeFactory.defaultInstance().constructCollectionType(ArrayList.class, Object.class); + private static final JavaType listListJavaType = TypeFactory.defaultInstance().constructCollectionType(ArrayList.class, listJavaType); public BytecodeJacksonDeserializer() { super(Bytecode.class); } @Override - public Bytecode createObject(final Map<String, Object> data) { + public Bytecode deserialize(final JsonParser jsonParser, final DeserializationContext deserializationContext) throws IOException, JsonProcessingException { final Bytecode bytecode = new Bytecode(); - if (data.containsKey(GraphSONTokens.SOURCE)) { - final List<List<Object>> instructions = (List) data.get(GraphSONTokens.SOURCE); - for (final List<Object> instruction : instructions) { - bytecode.addSource((String) instruction.get(0), Arrays.copyOfRange(instruction.toArray(), 1, instruction.size())); - } - } - if (data.containsKey(GraphSONTokens.STEP)) { - final List<List<Object>> instructions = (List) data.get(GraphSONTokens.STEP); - for (final List<Object> instruction : instructions) { - bytecode.addStep((String) instruction.get(0), Arrays.copyOfRange(instruction.toArray(), 1, instruction.size())); + + while (jsonParser.nextToken() != JsonToken.END_OBJECT) { + if (jsonParser.getCurrentName().equals(GraphSONTokens.SOURCE)) { + jsonParser.nextToken(); + final List<List<Object>> instructions = deserializationContext.readValue(jsonParser, listListJavaType); + for (final List<Object> instruction : instructions) { + bytecode.addSource((String) instruction.get(0), Arrays.copyOfRange(instruction.toArray(), 1, instruction.size())); + } + } else if (jsonParser.getCurrentName().equals(GraphSONTokens.STEP)) { + jsonParser.nextToken(); + final List<List<Object>> instructions = deserializationContext.readValue(jsonParser, listListJavaType); + for (final List<Object> instruction : instructions) { + bytecode.addStep((String) instruction.get(0), Arrays.copyOfRange(instruction.toArray(), 1, instruction.size())); + } } } return bytecode; } + + @Override + public boolean isCachable() { + return true; + } } final static class EnumJacksonDeserializer<A extends Enum> extends StdDeserializer<A> { @@ -283,18 +299,34 @@ final class TraversalSerializersV3d0 { } throw new IOException("Unknown enum type: " + enumClass); } + + @Override + public boolean isCachable() { + return true; + } } - final static class PJacksonDeserializer extends AbstractObjectDeserializer<P> { + final static class PJacksonDeserializer extends StdDeserializer<P> { public PJacksonDeserializer() { super(P.class); } @Override - public P createObject(final Map<String, Object> data) { - final String predicate = (String) data.get(GraphSONTokens.PREDICATE); - final Object value = data.get(GraphSONTokens.VALUE); + public P deserialize(final JsonParser jsonParser, final DeserializationContext deserializationContext) throws IOException, JsonProcessingException { + String predicate = null; + Object value = null; + + while (jsonParser.nextToken() != JsonToken.END_OBJECT) { + if (jsonParser.getCurrentName().equals(GraphSONTokens.PREDICATE)) { + jsonParser.nextToken(); + predicate = jsonParser.getText(); + } else if (jsonParser.getCurrentName().equals(GraphSONTokens.VALUE)) { + jsonParser.nextToken(); + value = deserializationContext.readValue(jsonParser, Object.class); + } + } + if (predicate.equals(GraphSONTokens.AND) || predicate.equals(GraphSONTokens.OR)) { return predicate.equals(GraphSONTokens.AND) ? new AndP((List<P>) value) : new OrP((List<P>) value); } else { @@ -324,20 +356,38 @@ final class TraversalSerializersV3d0 { } } } + + @Override + public boolean isCachable() { + return true; + } } - final static class LambdaJacksonDeserializer extends AbstractObjectDeserializer<Lambda> { + final static class LambdaJacksonDeserializer extends StdDeserializer<Lambda> { public LambdaJacksonDeserializer() { super(Lambda.class); } @Override - public Lambda createObject(final Map<String, Object> data) { - final String script = (String) data.get(GraphSONTokens.SCRIPT); - final String language = (String) data.get(GraphSONTokens.LANGUAGE); - final int arguments = ((Number) data.getOrDefault(GraphSONTokens.ARGUMENTS, -1)).intValue(); - // + public Lambda deserialize(final JsonParser jsonParser, final DeserializationContext deserializationContext) throws IOException, JsonProcessingException { + String script = null; + String language = null; + int arguments = -1; + + while (jsonParser.nextToken() != JsonToken.END_OBJECT) { + if (jsonParser.getCurrentName().equals(GraphSONTokens.SCRIPT)) { + jsonParser.nextToken(); + script = jsonParser.getText(); + } else if (jsonParser.getCurrentName().equals(GraphSONTokens.LANGUAGE)) { + jsonParser.nextToken(); + language = jsonParser.getText(); + } else if (jsonParser.getCurrentName().equals(GraphSONTokens.ARGUMENTS)) { + jsonParser.nextToken(); + arguments = jsonParser.getIntValue(); + } + } + if (-1 == arguments || arguments > 2) return new Lambda.UnknownArgLambda(script, language, arguments); else if (0 == arguments) @@ -347,29 +397,69 @@ final class TraversalSerializersV3d0 { else return new Lambda.TwoArgLambda<>(script, language); } + + @Override + public boolean isCachable() { + return true; + } } - final static class BindingJacksonDeserializer extends AbstractObjectDeserializer<Bytecode.Binding> { + final static class BindingJacksonDeserializer extends StdDeserializer<Bytecode.Binding> { public BindingJacksonDeserializer() { super(Bytecode.Binding.class); } @Override - public Bytecode.Binding createObject(final Map<String, Object> data) { - return new Bytecode.Binding<>((String) data.get(GraphSONTokens.KEY), data.get(GraphSONTokens.VALUE)); + public Bytecode.Binding deserialize(final JsonParser jsonParser, final DeserializationContext deserializationContext) throws IOException, JsonProcessingException { + String k = null; + Object v = null; + + while (jsonParser.nextToken() != JsonToken.END_OBJECT) { + if (jsonParser.getCurrentName().equals(GraphSONTokens.KEY)) { + jsonParser.nextToken(); + k = jsonParser.getText(); + } else if (jsonParser.getCurrentName().equals(GraphSONTokens.VALUE)) { + jsonParser.nextToken(); + v = deserializationContext.readValue(jsonParser, Object.class); + } + } + return new Bytecode.Binding<>(k, v); + } + + @Override + public boolean isCachable() { + return true; } } - static class TraverserJacksonDeserializer extends AbstractObjectDeserializer<Traverser> { + static class TraverserJacksonDeserializer extends StdDeserializer<Traverser> { public TraverserJacksonDeserializer() { super(Traverser.class); } @Override - public Traverser createObject(final Map<String, Object> data) { - return new DefaultRemoteTraverser<>(data.get(GraphSONTokens.VALUE), (Long) data.get(GraphSONTokens.BULK)); + public Traverser deserialize(final JsonParser jsonParser, final DeserializationContext deserializationContext) throws IOException, JsonProcessingException { + long bulk = 1; + Object v = null; + + while (jsonParser.nextToken() != JsonToken.END_OBJECT) { + if (jsonParser.getCurrentName().equals(GraphSONTokens.BULK)) { + jsonParser.nextToken(); + bulk = deserializationContext.readValue(jsonParser, Long.class); + } else if (jsonParser.getCurrentName().equals(GraphSONTokens.VALUE)) { + jsonParser.nextToken(); + v = deserializationContext.readValue(jsonParser, Object.class); + } + } + + return new DefaultRemoteTraverser<>(v, bulk); + } + + @Override + public boolean isCachable() { + return true; } } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/60874a59/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/star/StarGraphGraphSONSerializerV3d0.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/star/StarGraphGraphSONSerializerV3d0.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/star/StarGraphGraphSONSerializerV3d0.java new file mode 100644 index 0000000..9a9682c --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/star/StarGraphGraphSONSerializerV3d0.java @@ -0,0 +1,165 @@ +/* + * 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.util.star; + +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.VertexProperty; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONTokens; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONUtil; +import org.apache.tinkerpop.gremlin.structure.util.Comparators; +import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils; +import org.apache.tinkerpop.shaded.jackson.core.JsonGenerationException; +import org.apache.tinkerpop.shaded.jackson.core.JsonGenerator; +import org.apache.tinkerpop.shaded.jackson.core.JsonProcessingException; +import org.apache.tinkerpop.shaded.jackson.databind.SerializerProvider; +import org.apache.tinkerpop.shaded.jackson.databind.jsontype.TypeSerializer; +import org.apache.tinkerpop.shaded.jackson.databind.ser.std.StdSerializer; + +import java.io.IOException; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +/** + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public class StarGraphGraphSONSerializerV3d0 extends StdSerializer<DirectionalStarGraph> { + private final boolean normalize; + public StarGraphGraphSONSerializerV3d0(final boolean normalize) { + super(DirectionalStarGraph.class); + this.normalize = normalize; + } + + @Override + public void serialize(final DirectionalStarGraph starGraph, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider) throws IOException, JsonGenerationException { + ser(starGraph, jsonGenerator, serializerProvider, null); + } + + @Override + public void serializeWithType(final DirectionalStarGraph starGraph, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider, + final TypeSerializer typeSerializer) throws IOException, JsonProcessingException { + ser(starGraph, jsonGenerator, serializerProvider, typeSerializer); + } + + private void ser(final DirectionalStarGraph directionalStarGraph, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider, + final TypeSerializer typeSerializer) throws IOException, JsonProcessingException { + final StarGraph starGraph = directionalStarGraph.getStarGraphToSerialize(); + GraphSONUtil.writeStartObject(starGraph, jsonGenerator, typeSerializer); + GraphSONUtil.writeWithType(GraphSONTokens.ID, starGraph.starVertex.id, jsonGenerator, serializerProvider, typeSerializer); + jsonGenerator.writeStringField(GraphSONTokens.LABEL, starGraph.starVertex.label); + if (directionalStarGraph.getDirection() != null) writeEdges(directionalStarGraph, jsonGenerator, serializerProvider, typeSerializer, Direction.IN); + if (directionalStarGraph.getDirection() != null) writeEdges(directionalStarGraph, jsonGenerator, serializerProvider, typeSerializer, Direction.OUT); + if (starGraph.starVertex.vertexProperties != null && !starGraph.starVertex.vertexProperties.isEmpty()) { + jsonGenerator.writeFieldName(GraphSONTokens.PROPERTIES); + GraphSONUtil.writeStartObject(starGraph, jsonGenerator, typeSerializer); + final Set<String> keys = normalize ? new TreeSet<>(starGraph.starVertex.vertexProperties.keySet()) : starGraph.starVertex.vertexProperties.keySet(); + for (final String k : keys) { + final List<VertexProperty> vp = starGraph.starVertex.vertexProperties.get(k); + jsonGenerator.writeFieldName(k); + GraphSONUtil.writeStartArray(k, jsonGenerator, typeSerializer); + + final List<VertexProperty> vertexProperties = normalize ?sort(vp, Comparators.PROPERTY_COMPARATOR) : vp; + for (final VertexProperty property : vertexProperties) { + GraphSONUtil.writeStartObject(property, jsonGenerator, typeSerializer); + GraphSONUtil.writeWithType(GraphSONTokens.ID, property.id(), jsonGenerator, serializerProvider, typeSerializer); + GraphSONUtil.writeWithType(GraphSONTokens.VALUE, property.value(), jsonGenerator, serializerProvider, typeSerializer); + + final Iterator<Property> metaProperties = normalize ? + IteratorUtils.list(property.properties(), Comparators.PROPERTY_COMPARATOR).iterator() : property.properties(); + if (metaProperties.hasNext()) { + jsonGenerator.writeFieldName(GraphSONTokens.PROPERTIES); + GraphSONUtil.writeStartObject(metaProperties, jsonGenerator, typeSerializer); + + while (metaProperties.hasNext()) { + final Property<Object> meta = metaProperties.next(); + GraphSONUtil.writeWithType(meta.key(), meta.value(), jsonGenerator, serializerProvider, typeSerializer); + } + GraphSONUtil.writeEndObject(metaProperties, jsonGenerator, typeSerializer); + } + GraphSONUtil.writeEndObject(property, jsonGenerator, typeSerializer); + } + GraphSONUtil.writeEndArray(k, jsonGenerator, typeSerializer); + } + GraphSONUtil.writeEndObject(starGraph, jsonGenerator, typeSerializer); + } + GraphSONUtil.writeEndObject(starGraph, jsonGenerator, typeSerializer); + } + + private void writeEdges(final DirectionalStarGraph directionalStarGraph, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider, + final TypeSerializer typeSerializer, + final Direction direction) throws IOException, JsonProcessingException { + // only write edges if there are some AND if the user requested them to be serialized AND if they match + // the direction being serialized by the format + final StarGraph starGraph = directionalStarGraph.getStarGraphToSerialize(); + final Direction edgeDirectionToSerialize = directionalStarGraph.getDirection(); + final Map<String, List<Edge>> starEdges = direction.equals(Direction.OUT) ? starGraph.starVertex.outEdges : starGraph.starVertex.inEdges; + final boolean writeEdges = null != starEdges && edgeDirectionToSerialize != null + && (edgeDirectionToSerialize == direction || edgeDirectionToSerialize == Direction.BOTH); + if (writeEdges) { + jsonGenerator.writeFieldName(direction == Direction.IN ? GraphSONTokens.IN_E : GraphSONTokens.OUT_E); + GraphSONUtil.writeStartObject(directionalStarGraph, jsonGenerator, typeSerializer); + final Set<String> keys = normalize ? new TreeSet<>(starEdges.keySet()) : starEdges.keySet(); + for (final String k : keys) { + final List<Edge> edges = starEdges.get(k); + jsonGenerator.writeFieldName(k); + GraphSONUtil.writeStartArray(k, jsonGenerator, typeSerializer); + + final List<Edge> edgesToWrite = normalize ? sort(edges, Comparators.EDGE_COMPARATOR) : edges; + for (final Edge edge : edgesToWrite) { + GraphSONUtil.writeStartObject(edge, jsonGenerator, typeSerializer); + GraphSONUtil.writeWithType(GraphSONTokens.ID, edge.id(), jsonGenerator, serializerProvider, typeSerializer); + GraphSONUtil.writeWithType(direction.equals(Direction.OUT) ? GraphSONTokens.IN : GraphSONTokens.OUT, + direction.equals(Direction.OUT) ? edge.inVertex().id() : edge.outVertex().id(), + jsonGenerator, serializerProvider, typeSerializer); + + final Iterator<Property<Object>> edgeProperties = normalize ? + IteratorUtils.list(edge.properties(), Comparators.PROPERTY_COMPARATOR).iterator() : edge.properties(); + if (edgeProperties.hasNext()) { + jsonGenerator.writeFieldName(GraphSONTokens.PROPERTIES); + GraphSONUtil.writeStartObject(edge, jsonGenerator, typeSerializer); + while (edgeProperties.hasNext()) { + final Property<Object> meta = edgeProperties.next(); + GraphSONUtil.writeWithType(meta.key(), meta.value(), jsonGenerator, serializerProvider, typeSerializer); + } + GraphSONUtil.writeEndObject(edge, jsonGenerator, typeSerializer); + } + GraphSONUtil.writeEndObject(edge, jsonGenerator, typeSerializer); + } + GraphSONUtil.writeEndArray(k, jsonGenerator, typeSerializer); + } + GraphSONUtil.writeEndObject(directionalStarGraph, jsonGenerator, typeSerializer); + } + } + + private static <S> List<S> sort(final List<S> listToSort, final Comparator comparator) { + Collections.sort(listToSort, comparator); + return listToSort; + } + +}