You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by sp...@apache.org on 2015/02/12 14:01:36 UTC
[10/77] [partial] incubator-tinkerpop git commit: moved com/tinkerpop
directories to org/apache/tinkerpop
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphml/GraphMLWriterHelper.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphml/GraphMLWriterHelper.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphml/GraphMLWriterHelper.java
new file mode 100644
index 0000000..912286d
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphml/GraphMLWriterHelper.java
@@ -0,0 +1,377 @@
+/*
+ * 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 com.tinkerpop.gremlin.structure.io.graphml;
+
+import org.apache.commons.lang.StringUtils;
+
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import java.util.Stack;
+
+/**
+ * A wrapper for the IndentingXMLStreamWriter class by Kohsuke Kawaguchi
+ */
+class GraphMLWriterHelper {
+ /**
+ * Delegating {@link javax.xml.stream.XMLStreamWriter}.
+ *
+ * @author Kohsuke Kawaguchi
+ */
+ private abstract static class DelegatingXMLStreamWriter implements XMLStreamWriter {
+ private final XMLStreamWriter writer;
+
+ public DelegatingXMLStreamWriter(final XMLStreamWriter writer) {
+ this.writer = writer;
+ }
+
+ @Override
+ public void writeStartElement(final String localName) throws XMLStreamException {
+ writer.writeStartElement(localName);
+ }
+
+ @Override
+ public void writeStartElement(final String namespaceURI, final String localName) throws XMLStreamException {
+ writer.writeStartElement(namespaceURI, localName);
+ }
+
+ @Override
+ public void writeStartElement(final String prefix, final String localName, final String namespaceURI) throws XMLStreamException {
+ writer.writeStartElement(prefix, localName, namespaceURI);
+ }
+
+ @Override
+ public void writeEmptyElement(final String namespaceURI, final String localName) throws XMLStreamException {
+ writer.writeEmptyElement(namespaceURI, localName);
+ }
+
+ @Override
+ public void writeEmptyElement(final String prefix, final String localName, final String namespaceURI) throws XMLStreamException {
+ writer.writeEmptyElement(prefix, localName, namespaceURI);
+ }
+
+ @Override
+ public void writeEmptyElement(final String localName) throws XMLStreamException {
+ writer.writeEmptyElement(localName);
+ }
+
+ @Override
+ public void writeEndElement() throws XMLStreamException {
+ writer.writeEndElement();
+ }
+
+ @Override
+ public void writeEndDocument() throws XMLStreamException {
+ writer.writeEndDocument();
+ }
+
+ @Override
+ public void close() throws XMLStreamException {
+ writer.close();
+ }
+
+ @Override
+ public void flush() throws XMLStreamException {
+ writer.flush();
+ }
+
+ @Override
+ public void writeAttribute(final String localName, final String value) throws XMLStreamException {
+ writer.writeAttribute(localName, value);
+ }
+
+ @Override
+ public void writeAttribute(final String prefix, final String namespaceURI, final String localName, final String value) throws XMLStreamException {
+ writer.writeAttribute(prefix, namespaceURI, localName, value);
+ }
+
+ @Override
+ public void writeAttribute(final String namespaceURI, final String localName, final String value) throws XMLStreamException {
+ writer.writeAttribute(namespaceURI, localName, value);
+ }
+
+ @Override
+ public void writeNamespace(final String prefix, final String namespaceURI) throws XMLStreamException {
+ writer.writeNamespace(prefix, namespaceURI);
+ }
+
+ @Override
+ public void writeDefaultNamespace(final String namespaceURI) throws XMLStreamException {
+ writer.writeDefaultNamespace(namespaceURI);
+ }
+
+ @Override
+ public void writeComment(final String data) throws XMLStreamException {
+ writer.writeComment(data);
+ }
+
+ @Override
+ public void writeProcessingInstruction(final String target) throws XMLStreamException {
+ writer.writeProcessingInstruction(target);
+ }
+
+ @Override
+ public void writeProcessingInstruction(final String target, final String data) throws XMLStreamException {
+ writer.writeProcessingInstruction(target, data);
+ }
+
+ @Override
+ public void writeCData(final String data) throws XMLStreamException {
+ writer.writeCData(data);
+ }
+
+ @Override
+ public void writeDTD(final String dtd) throws XMLStreamException {
+ writer.writeDTD(dtd);
+ }
+
+ @Override
+ public void writeEntityRef(final String name) throws XMLStreamException {
+ writer.writeEntityRef(name);
+ }
+
+ @Override
+ public void writeStartDocument() throws XMLStreamException {
+ writer.writeStartDocument();
+ }
+
+ @Override
+ public void writeStartDocument(final String version) throws XMLStreamException {
+ writer.writeStartDocument(version);
+ }
+
+ @Override
+ public void writeStartDocument(final String encoding, final String version) throws XMLStreamException {
+ writer.writeStartDocument(encoding, version);
+ }
+
+ @Override
+ public void writeCharacters(final String text) throws XMLStreamException {
+ writer.writeCharacters(text);
+ }
+
+ @Override
+ public void writeCharacters(final char[] text, final int start, final int len) throws XMLStreamException {
+ writer.writeCharacters(text, start, len);
+ }
+
+ @Override
+ public String getPrefix(final String uri) throws XMLStreamException {
+ return writer.getPrefix(uri);
+ }
+
+ @Override
+ public void setPrefix(final String prefix, final String uri) throws XMLStreamException {
+ writer.setPrefix(prefix, uri);
+ }
+
+ @Override
+ public void setDefaultNamespace(final String uri) throws XMLStreamException {
+ writer.setDefaultNamespace(uri);
+ }
+
+ @Override
+ public void setNamespaceContext(final NamespaceContext context) throws XMLStreamException {
+ writer.setNamespaceContext(context);
+ }
+
+ @Override
+ public NamespaceContext getNamespaceContext() {
+ return writer.getNamespaceContext();
+ }
+
+ @Override
+ public Object getProperty(final String name) throws IllegalArgumentException {
+ return writer.getProperty(name);
+ }
+ }
+
+ /**
+ * @author Kohsuke Kawaguchi
+ */
+ public static class IndentingXMLStreamWriter extends DelegatingXMLStreamWriter {
+ private final static Object SEEN_NOTHING = new Object();
+ private final static Object SEEN_ELEMENT = new Object();
+ private final static Object SEEN_DATA = new Object();
+
+ private Object state = SEEN_NOTHING;
+ private Stack<Object> stateStack = new Stack<>();
+
+ private String indentStep = " ";
+ private static final String lineSeparator = System.getProperty("line.separator", "\n");
+ private int depth = 0;
+
+ public IndentingXMLStreamWriter(final XMLStreamWriter writer) {
+ super(writer);
+ }
+
+ /**
+ * Return the current indent step.
+ * <p/>
+ * <p>Return the current indent step: each start tag will be
+ * indented by this number of spaces times the number of
+ * ancestors that the element has.</p>
+ *
+ * @return The number of spaces in each indentation step,
+ * or 0 or less for no indentation.
+ * @see #setIndentStep(int)
+ * @deprecated Only return the length of the indent string.
+ */
+ public int getIndentStep() {
+ return indentStep.length();
+ }
+
+
+ /**
+ * Set the current indent step.
+ *
+ * @param indentStep The new indent step (0 or less for no
+ * indentation).
+ * @see #getIndentStep()
+ * @deprecated Should use the version that takes string.
+ */
+ public void setIndentStep(int indentStep) {
+ this.indentStep = StringUtils.repeat(" ", indentStep);
+ }
+
+ public void setIndentStep(final String s) {
+ this.indentStep = s;
+ }
+
+ private void onStartElement() throws XMLStreamException {
+ stateStack.push(SEEN_ELEMENT);
+ state = SEEN_NOTHING;
+ if (depth > 0) {
+ super.writeCharacters(lineSeparator);
+ }
+ doIndent();
+ depth++;
+ }
+
+ private void onEndElement() throws XMLStreamException {
+ depth--;
+ if (state == SEEN_ELEMENT) {
+ super.writeCharacters(lineSeparator);
+ doIndent();
+ }
+ state = stateStack.pop();
+ }
+
+ private void onEmptyElement() throws XMLStreamException {
+ state = SEEN_ELEMENT;
+ if (depth > 0) {
+ super.writeCharacters(lineSeparator);
+ }
+ doIndent();
+ }
+
+ /**
+ * Print indentation for the current level.
+ *
+ * @throws javax.xml.stream.XMLStreamException If there is an error writing the indentation characters, or if a filter
+ * further down the chain raises an exception.
+ */
+ private void doIndent() throws XMLStreamException {
+ if (depth > 0) {
+ for (int i = 0; i < depth; i++)
+ super.writeCharacters(indentStep);
+ }
+ }
+
+
+ @Override
+ public void writeStartDocument() throws XMLStreamException {
+ super.writeStartDocument();
+ super.writeCharacters(lineSeparator);
+ }
+
+ @Override
+ public void writeStartDocument(final String version) throws XMLStreamException {
+ super.writeStartDocument(version);
+ super.writeCharacters(lineSeparator);
+ }
+
+ @Override
+ public void writeStartDocument(final String encoding, final String version) throws XMLStreamException {
+ super.writeStartDocument(encoding, version);
+ super.writeCharacters(lineSeparator);
+ }
+
+ @Override
+ public void writeStartElement(final String localName) throws XMLStreamException {
+ onStartElement();
+ super.writeStartElement(localName);
+ }
+
+ @Override
+ public void writeStartElement(final String namespaceURI, final String localName) throws XMLStreamException {
+ onStartElement();
+ super.writeStartElement(namespaceURI, localName);
+ }
+
+ @Override
+ public void writeStartElement(final String prefix, final String localName, final String namespaceURI) throws XMLStreamException {
+ onStartElement();
+ super.writeStartElement(prefix, localName, namespaceURI);
+ }
+
+ @Override
+ public void writeEmptyElement(final String namespaceURI, final String localName) throws XMLStreamException {
+ onEmptyElement();
+ super.writeEmptyElement(namespaceURI, localName);
+ }
+
+ @Override
+ public void writeEmptyElement(final String prefix, final String localName, final String namespaceURI) throws XMLStreamException {
+ onEmptyElement();
+ super.writeEmptyElement(prefix, localName, namespaceURI);
+ }
+
+ @Override
+ public void writeEmptyElement(final String localName) throws XMLStreamException {
+ onEmptyElement();
+ super.writeEmptyElement(localName);
+ }
+
+ @Override
+ public void writeEndElement() throws XMLStreamException {
+ onEndElement();
+ super.writeEndElement();
+ }
+
+ @Override
+ public void writeCharacters(final String text) throws XMLStreamException {
+ state = SEEN_DATA;
+ super.writeCharacters(text);
+ }
+
+ @Override
+ public void writeCharacters(final char[] text, final int start, final int len) throws XMLStreamException {
+ state = SEEN_DATA;
+ super.writeCharacters(text, start, len);
+ }
+
+ @Override
+ public void writeCData(final String data) throws XMLStreamException {
+ state = SEEN_DATA;
+ super.writeCData(data);
+ }
+ }
+}
+
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONGraph.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONGraph.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONGraph.java
new file mode 100644
index 0000000..cbe9570
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONGraph.java
@@ -0,0 +1,90 @@
+/*
+ * 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 com.tinkerpop.gremlin.structure.io.graphson;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+import com.tinkerpop.gremlin.structure.Graph;
+import com.tinkerpop.gremlin.structure.util.Comparators;
+import com.tinkerpop.gremlin.util.function.FunctionUtils;
+
+import java.io.IOException;
+import java.util.HashMap;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+class GraphSONGraph {
+ private final Graph graphToSerialize;
+
+ public GraphSONGraph(final Graph graphToSerialize) {
+ this.graphToSerialize = graphToSerialize;
+ }
+
+ public Graph getGraphToSerialize() {
+ return graphToSerialize;
+ }
+
+ static class GraphJacksonSerializer extends StdSerializer<GraphSONGraph> {
+ private final boolean normalize;
+
+ public GraphJacksonSerializer(final boolean normalize) {
+ super(GraphSONGraph.class);
+ this.normalize = normalize;
+ }
+
+ @Override
+ public void serialize(final GraphSONGraph graphSONGraph, final JsonGenerator jsonGenerator, final SerializerProvider serializerProvider)
+ throws IOException {
+ ser(graphSONGraph, jsonGenerator);
+ }
+
+ @Override
+ public void serializeWithType(final GraphSONGraph graphSONGraph, final JsonGenerator jsonGenerator,
+ final SerializerProvider serializerProvider, final TypeSerializer typeSerializer) throws IOException {
+ ser(graphSONGraph, jsonGenerator);
+ }
+
+ private void ser(final GraphSONGraph graphSONGraph, final JsonGenerator jsonGenerator) throws IOException {
+ final Graph g = graphSONGraph.getGraphToSerialize();
+ jsonGenerator.writeStartObject();
+
+ if (g.features().graph().variables().supportsVariables())
+ jsonGenerator.writeObjectField(GraphSONTokens.VARIABLES, new HashMap<>(g.variables().asMap()));
+
+ jsonGenerator.writeArrayFieldStart(GraphSONTokens.VERTICES);
+ if (normalize)
+ g.V().order().by(Comparators.VERTEX_COMPARATOR).forEachRemaining(FunctionUtils.wrapConsumer(jsonGenerator::writeObject));
+ else
+ g.iterators().vertexIterator().forEachRemaining(FunctionUtils.wrapConsumer(jsonGenerator::writeObject));
+ jsonGenerator.writeEndArray();
+
+ jsonGenerator.writeArrayFieldStart(GraphSONTokens.EDGES);
+ if (normalize)
+ g.E().order().by(Comparators.EDGE_COMPARATOR).forEachRemaining(FunctionUtils.wrapConsumer(jsonGenerator::writeObject));
+ else
+ g.iterators().edgeIterator().forEachRemaining(FunctionUtils.wrapConsumer(jsonGenerator::writeObject));
+ jsonGenerator.writeEndArray();
+
+ jsonGenerator.writeEndObject();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/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
new file mode 100644
index 0000000..de541e4
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapper.java
@@ -0,0 +1,132 @@
+/*
+ * 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 com.tinkerpop.gremlin.structure.io.graphson;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider;
+import com.tinkerpop.gremlin.structure.io.Mapper;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * An extension to the standard Jackson {@code ObjectMapper} which automatically registers the standard
+ * {@link GraphSONModule} for serializing {@link com.tinkerpop.gremlin.structure.Graph} elements. This class
+ * can be used for generalized JSON serialization tasks that require meeting GraphSON standards.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class GraphSONMapper implements Mapper<ObjectMapper> {
+
+ private final List<SimpleModule> customModules;
+ private final boolean loadCustomSerializers;
+ private final boolean normalize;
+ private final boolean embedTypes;
+
+ private GraphSONMapper(final List<SimpleModule> customModules, final boolean loadCustomSerializers,
+ final boolean normalize, final boolean embedTypes) {
+ this.customModules = customModules;
+ this.loadCustomSerializers = loadCustomSerializers;
+ this.normalize = normalize;
+ this.embedTypes = embedTypes;
+ }
+
+ @Override
+ public ObjectMapper createMapper() {
+ final ObjectMapper om = new ObjectMapper();
+ om.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
+
+ if (embedTypes)
+ om.enableDefaultTypingAsProperty(ObjectMapper.DefaultTyping.NON_FINAL, GraphSONTokens.CLASS);
+
+ if (normalize)
+ om.enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS);
+
+ // this provider toStrings all unknown classes and converts keys in Map objects that are Object to String.
+ final DefaultSerializerProvider provider = new GraphSONSerializerProvider();
+ provider.setDefaultKeySerializer(new GraphSONModule.GraphSONKeySerializer());
+ om.setSerializerProvider(provider);
+
+ om.registerModule(new GraphSONModule(normalize));
+ customModules.forEach(om::registerModule);
+
+ // plugin external serialization modules
+ if (loadCustomSerializers)
+ om.findAndRegisterModules();
+
+ // keep streams open to accept multiple values (e.g. multiple vertices)
+ om.getFactory().disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
+
+ return om;
+ }
+
+ public static Builder build() {
+ return new Builder();
+ }
+
+ public static class Builder {
+ private List<SimpleModule> customModules = new ArrayList<>();
+ private boolean loadCustomModules = false;
+ private boolean normalize = false;
+ private boolean embedTypes = false;
+
+ private Builder() {
+ }
+
+ /**
+ * Supply a mapper module for serialization/deserialization.
+ */
+ public Builder addCustomModule(final SimpleModule custom) {
+ this.customModules.add(custom);
+ return this;
+ }
+
+ /**
+ * Try to load {@code SimpleModule} instances from the current classpath. These are loaded in addition to
+ * the one supplied to the {@link #addCustomModule(com.fasterxml.jackson.databind.module.SimpleModule)};
+ */
+ public Builder loadCustomModules(final boolean loadCustomModules) {
+ this.loadCustomModules = loadCustomModules;
+ return this;
+ }
+
+ /**
+ * Forces keys to be sorted.
+ */
+ public Builder normalize(final boolean normalize) {
+ this.normalize = normalize;
+ return this;
+ }
+
+ /**
+ * Embeds Java types into generated JSON to clarify their origins.
+ */
+ public Builder embedTypes(final boolean embedTypes) {
+ this.embedTypes = embedTypes;
+ return this;
+ }
+
+ public GraphSONMapper create() {
+ return new GraphSONMapper(customModules, loadCustomModules, normalize, embedTypes);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/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
new file mode 100644
index 0000000..6a512b3
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java
@@ -0,0 +1,299 @@
+/*
+ * 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 com.tinkerpop.gremlin.structure.io.graphson;
+
+import com.fasterxml.jackson.core.JsonGenerationException;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.fasterxml.jackson.databind.ser.std.StdKeySerializer;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+import com.tinkerpop.gremlin.process.Path;
+import com.tinkerpop.gremlin.process.util.metric.Metrics;
+import com.tinkerpop.gremlin.process.util.metric.TraversalMetrics;
+import com.tinkerpop.gremlin.structure.*;
+import com.tinkerpop.gremlin.structure.util.detached.DetachedVertexProperty;
+import com.tinkerpop.gremlin.util.iterator.IteratorUtils;
+
+import java.io.IOException;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class GraphSONModule extends SimpleModule {
+
+ public GraphSONModule(final boolean normalize) {
+ super("graphson");
+ addSerializer(Edge.class, new EdgeJacksonSerializer());
+ addSerializer(Vertex.class, new VertexJacksonSerializer());
+ addSerializer(GraphSONVertex.class, new GraphSONVertex.VertexJacksonSerializer());
+ addSerializer(GraphSONGraph.class, new GraphSONGraph.GraphJacksonSerializer(normalize));
+ addSerializer(GraphSONVertexProperty.class, new GraphSONVertexProperty.GraphSONVertexPropertySerializer());
+ addSerializer(VertexProperty.class, new VertexPropertyJacksonSerializer());
+ addSerializer(Property.class, new PropertyJacksonSerializer());
+ addSerializer(TraversalMetrics.class, new TraversalMetricsJacksonSerializer());
+ addSerializer(Path.class, new PathJacksonSerializer());
+ }
+
+ static class VertexPropertyJacksonSerializer extends StdSerializer<VertexProperty> {
+ public VertexPropertyJacksonSerializer() {
+ super(VertexProperty.class);
+ }
+
+ @Override
+ public void serialize(final VertexProperty property, final JsonGenerator jsonGenerator, final SerializerProvider serializerProvider)
+ throws IOException {
+ ser(property, jsonGenerator);
+ }
+
+ @Override
+ public void serializeWithType(final VertexProperty property, final JsonGenerator jsonGenerator,
+ final SerializerProvider serializerProvider, final TypeSerializer typeSerializer) throws IOException {
+ ser(property, jsonGenerator);
+ }
+
+ private static void ser(final VertexProperty property, final JsonGenerator jsonGenerator) throws IOException {
+ final Map<String, Object> m = new HashMap<>();
+ m.put(GraphSONTokens.ID, property.id());
+ m.put(GraphSONTokens.VALUE, property.value());
+ m.put(GraphSONTokens.LABEL, property.label());
+ m.put(GraphSONTokens.PROPERTIES, props(property));
+
+ jsonGenerator.writeObject(m);
+ }
+
+ private static Map<String, Object> props(final VertexProperty property) {
+ if (property instanceof DetachedVertexProperty) {
+ try {
+ return IteratorUtils.collectMap(property.iterators().propertyIterator(), Property::key, Property::value);
+ } catch (UnsupportedOperationException uoe) {
+ return new HashMap<>();
+ }
+ } else {
+ return (property.graph().features().vertex().supportsMetaProperties()) ?
+ IteratorUtils.collectMap(property.iterators().propertyIterator(), Property::key, Property::value) :
+ new HashMap<>();
+ }
+ }
+ }
+
+ static class PropertyJacksonSerializer extends StdSerializer<Property> {
+ public PropertyJacksonSerializer() {
+ super(Property.class);
+ }
+
+ @Override
+ public void serialize(final Property property, final JsonGenerator jsonGenerator, final SerializerProvider serializerProvider)
+ throws IOException {
+ ser(property, jsonGenerator);
+ }
+
+ @Override
+ public void serializeWithType(final Property property, final JsonGenerator jsonGenerator,
+ final SerializerProvider serializerProvider, final TypeSerializer typeSerializer) throws IOException {
+ ser(property, jsonGenerator);
+ }
+
+ private static void ser(final Property property, final JsonGenerator jsonGenerator) throws IOException {
+ final Map<String, Object> m = new HashMap<>();
+ m.put(GraphSONTokens.VALUE, property.value());
+ jsonGenerator.writeObject(m);
+ }
+ }
+
+
+ static class EdgeJacksonSerializer extends StdSerializer<Edge> {
+ public EdgeJacksonSerializer() {
+ super(Edge.class);
+ }
+
+ @Override
+ public void serialize(final Edge edge, final JsonGenerator jsonGenerator, final SerializerProvider serializerProvider)
+ throws IOException {
+ ser(edge, jsonGenerator);
+ }
+
+ @Override
+ public void serializeWithType(final Edge edge, final JsonGenerator jsonGenerator,
+ final SerializerProvider serializerProvider, final TypeSerializer typeSerializer) throws IOException {
+ ser(edge, jsonGenerator);
+ }
+
+ private static void ser(final Edge edge, final JsonGenerator jsonGenerator) throws IOException {
+ final Map<String, Object> m = new HashMap<>();
+ m.put(GraphSONTokens.ID, edge.id());
+ m.put(GraphSONTokens.LABEL, edge.label());
+ m.put(GraphSONTokens.TYPE, GraphSONTokens.EDGE);
+
+ final Map<String, Object> properties = IteratorUtils.collectMap(edge.iterators().propertyIterator(), Property::key, Property::value);
+ m.put(GraphSONTokens.PROPERTIES, properties);
+
+ final Vertex inV = edge.iterators().vertexIterator(Direction.IN).next();
+ m.put(GraphSONTokens.IN, inV.id());
+ m.put(GraphSONTokens.IN_LABEL, inV.label());
+
+ final Vertex outV = edge.iterators().vertexIterator(Direction.OUT).next();
+ m.put(GraphSONTokens.OUT, outV.id());
+ m.put(GraphSONTokens.OUT_LABEL, outV.label());
+
+ jsonGenerator.writeObject(m);
+ }
+ }
+
+ static class VertexJacksonSerializer extends StdSerializer<Vertex> {
+
+ public VertexJacksonSerializer() {
+ super(Vertex.class);
+ }
+
+ @Override
+ public void serialize(final Vertex vertex, final JsonGenerator jsonGenerator, final SerializerProvider serializerProvider)
+ throws IOException {
+ ser(vertex, jsonGenerator);
+ }
+
+ @Override
+ public void serializeWithType(final Vertex vertex, final JsonGenerator jsonGenerator,
+ final SerializerProvider serializerProvider, final TypeSerializer typeSerializer) throws IOException {
+ ser(vertex, jsonGenerator);
+
+ }
+
+ private static void ser(final Vertex vertex, final JsonGenerator jsonGenerator)
+ throws IOException {
+ final Map<String, Object> m = new HashMap<>();
+ m.put(GraphSONTokens.ID, vertex.id());
+ m.put(GraphSONTokens.LABEL, vertex.label());
+ m.put(GraphSONTokens.TYPE, GraphSONTokens.VERTEX);
+
+ // convert to GraphSONVertexProperty so that the label does not get serialized in the output - it is
+ // redundant because the key in the map is the same as the label.
+ final Iterator<GraphSONVertexProperty> vertexPropertyList = IteratorUtils.map(vertex.iterators().propertyIterator(), GraphSONVertexProperty::new);
+ final Object properties = IteratorUtils.groupBy(vertexPropertyList, vp -> vp.getToSerialize().key());
+ m.put(GraphSONTokens.PROPERTIES, properties);
+
+ jsonGenerator.writeObject(m);
+ }
+ }
+
+ static class PathJacksonSerializer extends StdSerializer<Path> {
+ public PathJacksonSerializer() {
+ super(Path.class);
+ }
+
+ @Override
+ public void serialize(final Path path, final JsonGenerator jsonGenerator, final SerializerProvider serializerProvider)
+ throws IOException, JsonGenerationException {
+ ser(path, jsonGenerator);
+ }
+
+ @Override
+ public void serializeWithType(final Path path, final JsonGenerator jsonGenerator,
+ final SerializerProvider serializerProvider, final TypeSerializer typeSerializer)
+ throws IOException, JsonProcessingException {
+ ser(path, jsonGenerator);
+ }
+
+ private static void ser(final Path path, final JsonGenerator jsonGenerator)
+ throws IOException {
+ final Map<String, Object> m = new HashMap<>();
+ m.put(GraphSONTokens.LABELS, path.labels());
+ m.put(GraphSONTokens.OBJECTS, path.objects());
+ jsonGenerator.writeObject(m);
+ }
+ }
+
+ static class TraversalMetricsJacksonSerializer extends StdSerializer<TraversalMetrics> {
+
+ public TraversalMetricsJacksonSerializer() {
+ super(TraversalMetrics.class);
+ }
+
+ @Override
+ public void serialize(final TraversalMetrics property, final JsonGenerator jsonGenerator, final SerializerProvider serializerProvider)
+ throws IOException {
+ serializeInternal(property, jsonGenerator);
+ }
+
+ @Override
+ public void serializeWithType(final TraversalMetrics property, final JsonGenerator jsonGenerator,
+ final SerializerProvider serializerProvider, final TypeSerializer typeSerializer) throws IOException {
+ serializeInternal(property, jsonGenerator);
+ }
+
+ private static void serializeInternal(final TraversalMetrics traversalMetrics, final JsonGenerator jsonGenerator) throws IOException {
+ final Map<String, Object> m = new HashMap<>();
+
+ m.put(GraphSONTokens.DURATION, traversalMetrics.getDuration(TimeUnit.MILLISECONDS));
+ List<Map<String, Object>> metrics = new ArrayList<>();
+ traversalMetrics.getMetrics().forEach(it -> metrics.add(metricsToMap(it)));
+ m.put(GraphSONTokens.METRICS, metrics);
+
+ jsonGenerator.writeObject(m);
+ }
+
+ private static Map<String, Object> metricsToMap(final Metrics metrics) {
+ final Map<String, Object> m = new HashMap<>();
+ m.put(GraphSONTokens.ID, metrics.getId());
+ m.put(GraphSONTokens.NAME, metrics.getName());
+ m.put(GraphSONTokens.COUNT, metrics.getCount());
+ m.put(GraphSONTokens.DURATION, metrics.getDuration(TimeUnit.MILLISECONDS));
+ m.put(GraphSONTokens.PERCENT_DURATION, metrics.getPercentDuration());
+
+ if (!metrics.getAnnotations().isEmpty()) {
+ m.put(GraphSONTokens.ANNOTATIONS, metrics.getAnnotations());
+ }
+
+ if (!metrics.getNested().isEmpty()) {
+ List<Map<String, Object>> nested = new ArrayList<>();
+ metrics.getNested().forEach(it -> nested.add(metricsToMap(it)));
+ m.put(GraphSONTokens.METRICS, nested);
+ }
+ return m;
+ }
+ }
+
+ /**
+ * Maps in the JVM can have {@link Object} as a key, but in JSON they must be a {@link String}.
+ */
+ static class GraphSONKeySerializer extends StdKeySerializer {
+ @Override
+ public void serialize(final Object o, final JsonGenerator jsonGenerator, final SerializerProvider serializerProvider) throws IOException {
+ ser(o, jsonGenerator, serializerProvider);
+ }
+
+ @Override
+ public void serializeWithType(final Object o, final JsonGenerator jsonGenerator,
+ final SerializerProvider serializerProvider, final TypeSerializer typeSerializer) throws IOException {
+ ser(o, jsonGenerator, serializerProvider);
+ }
+
+ private void ser(final Object o, final JsonGenerator jsonGenerator,
+ final SerializerProvider serializerProvider) throws IOException {
+ if (Element.class.isAssignableFrom(o.getClass()))
+ jsonGenerator.writeFieldName((((Element) o).id()).toString());
+ else
+ super.serialize(o, jsonGenerator, serializerProvider);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/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
new file mode 100644
index 0000000..c5103cf
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONReader.java
@@ -0,0 +1,265 @@
+/*
+ * 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 com.tinkerpop.gremlin.structure.io.graphson;
+
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.tinkerpop.gremlin.process.T;
+import com.tinkerpop.gremlin.structure.Direction;
+import com.tinkerpop.gremlin.structure.Edge;
+import com.tinkerpop.gremlin.structure.Graph;
+import com.tinkerpop.gremlin.structure.Vertex;
+import com.tinkerpop.gremlin.structure.VertexProperty;
+import com.tinkerpop.gremlin.structure.io.GraphReader;
+import com.tinkerpop.gremlin.structure.util.batch.BatchGraph;
+import com.tinkerpop.gremlin.structure.util.detached.DetachedEdge;
+import com.tinkerpop.gremlin.structure.util.detached.DetachedVertex;
+import com.tinkerpop.gremlin.util.function.FunctionUtils;
+import org.javatuples.Pair;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+
+/**
+ * A @{link GraphReader} that constructs a graph from a JSON-based representation of a graph and its elements.
+ * This implementation only supports JSON data types and is therefore lossy with respect to data types (e.g. a
+ * float will become a double, element IDs may not be retrieved in the format they were serialized, etc.).
+ * {@link Edge} and {@link Vertex} objects are serialized to {@code Map} instances. If an
+ * {@link com.tinkerpop.gremlin.structure.Element} is used as a key, it is coerced to its identifier. Other complex
+ * objects are converted via {@link Object#toString()} unless there is a mapper serializer supplied.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class GraphSONReader implements GraphReader {
+ private final ObjectMapper mapper;
+ private final long batchSize;
+ private final String vertexIdKey;
+ private final String edgeIdKey;
+
+ final TypeReference<Map<String, Object>> mapTypeReference = new TypeReference<Map<String, Object>>() {
+ };
+
+ public GraphSONReader(final GraphSONMapper mapper, final long batchSize,
+ final String vertexIdKey, final String edgeIdKey) {
+ this.mapper = mapper.createMapper();
+ this.batchSize = batchSize;
+ this.vertexIdKey = vertexIdKey;
+ this.edgeIdKey = edgeIdKey;
+ }
+
+ @Override
+ public void readGraph(final InputStream inputStream, final Graph graphToWriteTo) throws IOException {
+ final BatchGraph graph;
+ try {
+ // will throw an exception if not constructed properly
+ graph = BatchGraph.build(graphToWriteTo)
+ .vertexIdKey(vertexIdKey)
+ .edgeIdKey(edgeIdKey)
+ .bufferSize(batchSize).create();
+ } catch (Exception ex) {
+ throw new IOException("Could not instantiate BatchGraph wrapper", ex);
+ }
+
+ final JsonFactory factory = mapper.getFactory();
+
+ try (JsonParser parser = factory.createParser(inputStream)) {
+ if (parser.nextToken() != JsonToken.START_OBJECT)
+ throw new IOException("Expected data to start with an Object");
+
+ while (parser.nextToken() != JsonToken.END_OBJECT) {
+ final String fieldName = parser.getCurrentName();
+ parser.nextToken();
+
+ if (fieldName.equals(GraphSONTokens.VARIABLES)) {
+ final Map<String, Object> graphVariables = parser.readValueAs(mapTypeReference);
+ if (graphToWriteTo.features().graph().variables().supportsVariables())
+ graphVariables.entrySet().forEach(entry -> graphToWriteTo.variables().set(entry.getKey(), entry.getValue()));
+ } else if (fieldName.equals(GraphSONTokens.VERTICES)) {
+ while (parser.nextToken() != JsonToken.END_ARRAY) {
+ final Map<String, Object> vertexData = parser.readValueAs(mapTypeReference);
+ readVertexData(vertexData, detachedVertex -> {
+ final Iterator<Vertex> iterator = graph.iterators().vertexIterator(detachedVertex.id());
+ final Vertex v = iterator.hasNext() ? iterator.next() : graph.addVertex(T.label, detachedVertex.label(), T.id, detachedVertex.id());
+ detachedVertex.iterators().propertyIterator().forEachRemaining(p -> createVertexProperty(graphToWriteTo, v, p, false));
+ return v;
+ });
+ }
+ } else if (fieldName.equals(GraphSONTokens.EDGES)) {
+ while (parser.nextToken() != JsonToken.END_ARRAY) {
+ final Map<String, Object> edgeData = parser.readValueAs(mapTypeReference);
+ readEdgeData(edgeData, detachedEdge -> {
+ final Vertex vOut = graph.iterators().vertexIterator(detachedEdge.iterators().vertexIterator(Direction.OUT).next().id()).next();
+ final Vertex vIn = graph.iterators().vertexIterator(detachedEdge.iterators().vertexIterator(Direction.IN).next().id()).next();
+ // batchgraph checks for edge id support and uses it if possible.
+ final Edge e = vOut.addEdge(edgeData.get(GraphSONTokens.LABEL).toString(), vIn, T.id, detachedEdge.id());
+ detachedEdge.iterators().propertyIterator().forEachRemaining(p -> e.<Object>property(p.key(), p.value()));
+ return e;
+ });
+ }
+ } else
+ throw new IllegalStateException(String.format("Unexpected token in GraphSON - %s", fieldName));
+ }
+
+ graph.tx().commit();
+ } catch (Exception ex) {
+ throw new IOException(ex);
+ }
+ }
+
+ @Override
+ public Iterator<Vertex> readVertices(final InputStream inputStream, final Direction direction,
+ final Function<DetachedVertex, Vertex> vertexMaker,
+ final Function<DetachedEdge, Edge> edgeMaker) throws IOException {
+ final BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
+ return br.lines().<Vertex>map(FunctionUtils.wrapFunction(line -> readVertex(new ByteArrayInputStream(line.getBytes()), direction, vertexMaker, edgeMaker))).iterator();
+ }
+
+ @Override
+ public Edge readEdge(final InputStream inputStream, final Function<DetachedEdge, Edge> edgeMaker) throws IOException {
+ final Map<String, Object> edgeData = mapper.readValue(inputStream, mapTypeReference);
+ return readEdgeData(edgeData, edgeMaker);
+ }
+
+ @Override
+ public Vertex readVertex(final InputStream inputStream, final Function<DetachedVertex, Vertex> vertexMaker) throws IOException {
+ final Map<String, Object> vertexData = mapper.readValue(inputStream, mapTypeReference);
+ return readVertexData(vertexData, vertexMaker);
+ }
+
+ @Override
+ public Vertex readVertex(final InputStream inputStream, final Direction direction,
+ final Function<DetachedVertex, Vertex> vertexMaker,
+ final Function<DetachedEdge, Edge> edgeMaker) throws IOException {
+ final Map<String, Object> vertexData = mapper.readValue(inputStream, mapTypeReference);
+ final Vertex v = readVertexData(vertexData, vertexMaker);
+
+ if (vertexData.containsKey(GraphSONTokens.OUT_E) && (direction == Direction.BOTH || direction == Direction.OUT))
+ readVertexEdges(edgeMaker, vertexData, GraphSONTokens.OUT_E);
+
+ if (vertexData.containsKey(GraphSONTokens.IN_E) && (direction == Direction.BOTH || direction == Direction.IN))
+ readVertexEdges(edgeMaker, vertexData, GraphSONTokens.IN_E);
+
+ return v;
+ }
+
+ private static void createVertexProperty(final Graph graphToWriteTo, final Vertex v, final VertexProperty<Object> p, final boolean hidden) {
+ final List<Object> propertyArgs = new ArrayList<>();
+ if (graphToWriteTo.features().vertex().properties().supportsUserSuppliedIds())
+ propertyArgs.addAll(Arrays.asList(T.id, p.id()));
+ p.iterators().propertyIterator().forEachRemaining(it -> propertyArgs.addAll(Arrays.asList(it.key(), it.value())));
+ v.property(p.key(), p.value(), propertyArgs.toArray());
+ }
+
+ private static void readVertexEdges(final Function<DetachedEdge, Edge> edgeMaker, final Map<String, Object> vertexData, final String direction) throws IOException {
+ final List<Map<String, Object>> edgeDatas = (List<Map<String, Object>>) vertexData.get(direction);
+ for (Map<String, Object> edgeData : edgeDatas) {
+ readEdgeData(edgeData, edgeMaker);
+ }
+ }
+
+ private static Edge readEdgeData(final Map<String, Object> edgeData, final Function<DetachedEdge, Edge> edgeMaker) throws IOException {
+ final Map<String, Object> properties = (Map<String, Object>) edgeData.get(GraphSONTokens.PROPERTIES);
+
+ final DetachedEdge edge = new DetachedEdge(edgeData.get(GraphSONTokens.ID),
+ edgeData.get(GraphSONTokens.LABEL).toString(),
+ properties,
+ Pair.with(edgeData.get(GraphSONTokens.OUT), edgeData.get(GraphSONTokens.OUT_LABEL).toString()),
+ Pair.with(edgeData.get(GraphSONTokens.IN), edgeData.get(GraphSONTokens.IN_LABEL).toString()));
+
+ return edgeMaker.apply(edge);
+ }
+
+ private static Vertex readVertexData(final Map<String, Object> vertexData, final Function<DetachedVertex, Vertex> vertexMaker) throws IOException {
+ final Map<String, Object> vertexProperties = (Map<String, Object>) vertexData.get(GraphSONTokens.PROPERTIES);
+ final DetachedVertex vertex = new DetachedVertex(vertexData.get(GraphSONTokens.ID),
+ vertexData.get(GraphSONTokens.LABEL).toString(),
+ vertexProperties);
+
+ return vertexMaker.apply(vertex);
+ }
+
+ public static Builder build() {
+ return new Builder();
+ }
+
+ public static class Builder {
+ private long batchSize = BatchGraph.DEFAULT_BUFFER_SIZE;
+ private String vertexIdKey = T.id.getAccessor();
+ private String edgeIdKey = T.id.getAccessor();
+
+ private GraphSONMapper mapper = GraphSONMapper.build().create();
+
+ private Builder() {
+ }
+
+ /**
+ * The name of the key to supply to
+ * {@link com.tinkerpop.gremlin.structure.util.batch.BatchGraph.Builder#vertexIdKey} when reading data into
+ * the {@link Graph}.
+ */
+ public Builder vertexIdKey(final String vertexIdKey) {
+ this.vertexIdKey = vertexIdKey;
+ return this;
+ }
+
+ /**
+ * The name of the key to supply to
+ * {@link com.tinkerpop.gremlin.structure.util.batch.BatchGraph.Builder#edgeIdKey} when reading data into
+ * the {@link Graph}.
+ */
+ public Builder edgeIdKey(final String edgeIdKey) {
+ this.edgeIdKey = edgeIdKey;
+ return this;
+ }
+
+ /**
+ * Number of mutations to perform before a commit is executed.
+ */
+ public Builder batchSize(final long batchSize) {
+ this.batchSize = batchSize;
+ return this;
+ }
+
+ /**
+ * Override all of the {@link GraphSONMapper} builder
+ * options with this mapper. If this value is set to something other than null then that value will be
+ * used to construct the writer.
+ */
+ public Builder mapper(final GraphSONMapper mapper) {
+ this.mapper = mapper;
+ return this;
+ }
+
+ public GraphSONReader create() {
+ return new GraphSONReader(mapper, batchSize, vertexIdKey, edgeIdKey);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/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
new file mode 100644
index 0000000..089ab63
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializerProvider.java
@@ -0,0 +1,57 @@
+/*
+ * 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 com.tinkerpop.gremlin.structure.io.graphson;
+
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializationConfig;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider;
+import com.fasterxml.jackson.databind.ser.SerializerFactory;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+
+/**
+ * Implementation of the {@code DefaultSerializerProvider} for Jackson that users the {@code ToStringSerializer} for
+ * unknown types.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+class GraphSONSerializerProvider extends DefaultSerializerProvider {
+ private static final long serialVersionUID = 1L;
+ private static final ToStringSerializer toStringSerializer = new ToStringSerializer();
+
+ public GraphSONSerializerProvider() {
+ super();
+ }
+
+ protected GraphSONSerializerProvider(final SerializerProvider src,
+ final SerializationConfig config, final SerializerFactory f) {
+ super(src, config, f);
+ }
+
+ @Override
+ public JsonSerializer<Object> getUnknownTypeSerializer(final Class<?> aClass) {
+ return toStringSerializer;
+ }
+
+ @Override
+ public GraphSONSerializerProvider createInstance(final SerializationConfig config,
+ final SerializerFactory jsf) {
+ return new GraphSONSerializerProvider(this, config, jsf);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTokens.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTokens.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTokens.java
new file mode 100644
index 0000000..16614b0
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTokens.java
@@ -0,0 +1,51 @@
+/*
+ * 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 com.tinkerpop.gremlin.structure.io.graphson;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public final class GraphSONTokens {
+ public static final String CLASS = "@class";
+ public static final String ID = "id";
+ public static final String TYPE = "type";
+ public static final String VALUE = "value";
+ public static final String PROPERTIES = "properties";
+ public static final String VARIABLES = "variables";
+ public static final String EDGE = "edge";
+ public static final String EDGES = "edges";
+ public static final String VERTEX = "vertex";
+ public static final String VERTICES = "vertices";
+ public static final String IN = "inV";
+ public static final String OUT = "outV";
+ public static final String IN_E = "inE";
+ public static final String OUT_E = "outE";
+ public static final String LABEL = "label";
+ public static final String LABELS = "labels";
+ public static final String OBJECTS = "objects";
+ public static final String IN_LABEL = "inVLabel";
+ public static final String OUT_LABEL = "outVLabel";
+ // TraversalMetrics Tokens
+ public static final String METRICS = "metrics";
+ public static final String DURATION = "dur";
+ public static final String NAME = "name";
+ public static final String PERCENT_DURATION = "percentDur";
+ public static final String COUNT = "count";
+ public static final String ANNOTATIONS = "annotations";
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONVertex.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONVertex.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONVertex.java
new file mode 100644
index 0000000..12d697a
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONVertex.java
@@ -0,0 +1,94 @@
+/*
+ * 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 com.tinkerpop.gremlin.structure.io.graphson;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+import com.tinkerpop.gremlin.structure.Direction;
+import com.tinkerpop.gremlin.structure.Property;
+import com.tinkerpop.gremlin.structure.Vertex;
+import com.tinkerpop.gremlin.util.iterator.IteratorUtils;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+class GraphSONVertex {
+ private final Direction direction;
+ private final Vertex vertexToSerialize;
+
+ public GraphSONVertex(final Vertex vertexToSerialize, final Direction direction) {
+ this.direction = direction;
+ this.vertexToSerialize = vertexToSerialize;
+ }
+
+ public Direction getDirection() {
+ return direction;
+ }
+
+ public Vertex getVertexToSerialize() {
+ return vertexToSerialize;
+ }
+
+ static class VertexJacksonSerializer extends StdSerializer<GraphSONVertex> {
+
+ public VertexJacksonSerializer() {
+ super(GraphSONVertex.class);
+ }
+
+ @Override
+ public void serialize(final GraphSONVertex directionalVertex, final JsonGenerator jsonGenerator, final SerializerProvider serializerProvider)
+ throws IOException {
+ ser(directionalVertex, jsonGenerator);
+ }
+
+ @Override
+ public void serializeWithType(final GraphSONVertex directionalVertex, final JsonGenerator jsonGenerator,
+ final SerializerProvider serializerProvider, final TypeSerializer typeSerializer) throws IOException {
+ ser(directionalVertex, jsonGenerator);
+ }
+
+ public static void ser(final GraphSONVertex directionalVertex, final JsonGenerator jsonGenerator) throws IOException {
+ final Vertex vertex = directionalVertex.getVertexToSerialize();
+ final Map<String, Object> m = new HashMap<>();
+ m.put(GraphSONTokens.ID, vertex.id());
+ m.put(GraphSONTokens.LABEL, vertex.label());
+ m.put(GraphSONTokens.TYPE, GraphSONTokens.VERTEX);
+
+ final Object properties = IteratorUtils.groupBy(vertex.iterators().propertyIterator(), Property::key);
+ m.put(GraphSONTokens.PROPERTIES, properties);
+
+ if (directionalVertex.getDirection() == Direction.BOTH || directionalVertex.getDirection() == Direction.OUT) {
+ m.put(GraphSONTokens.OUT_E, IteratorUtils.fill(vertex.iterators().edgeIterator(Direction.OUT), new ArrayList()));
+ }
+
+ if (directionalVertex.getDirection() == Direction.BOTH || directionalVertex.getDirection() == Direction.IN) {
+ m.put(GraphSONTokens.IN_E, IteratorUtils.fill(vertex.iterators().edgeIterator(Direction.IN), new ArrayList()));
+ }
+
+ jsonGenerator.writeObject(m);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONVertexProperty.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONVertexProperty.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONVertexProperty.java
new file mode 100644
index 0000000..3ee93fe
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONVertexProperty.java
@@ -0,0 +1,93 @@
+/*
+ * 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 com.tinkerpop.gremlin.structure.io.graphson;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+import com.tinkerpop.gremlin.structure.Property;
+import com.tinkerpop.gremlin.structure.VertexProperty;
+import com.tinkerpop.gremlin.structure.util.detached.DetachedVertexProperty;
+import com.tinkerpop.gremlin.util.iterator.IteratorUtils;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Serializes the {@link VertexProperty} but does so without a label. This serializer should be used when the
+ * property is serialized as part of a {@link Map} where the label isn't required. In those cases, the key is
+ * the same as the label and therefore redundant.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class GraphSONVertexProperty {
+ private final VertexProperty toSerialize;
+
+ public GraphSONVertexProperty(final VertexProperty toSerialize) {
+ this.toSerialize = toSerialize;
+ }
+
+ public VertexProperty getToSerialize() {
+ return toSerialize;
+ }
+
+ static class GraphSONVertexPropertySerializer extends StdSerializer<GraphSONVertexProperty> {
+ public GraphSONVertexPropertySerializer() {
+ super(GraphSONVertexProperty.class);
+ }
+
+ @Override
+ public void serialize(final GraphSONVertexProperty property, final JsonGenerator jsonGenerator, final SerializerProvider serializerProvider)
+ throws IOException {
+ ser(property, jsonGenerator);
+ }
+
+ @Override
+ public void serializeWithType(final GraphSONVertexProperty property, final JsonGenerator jsonGenerator,
+ final SerializerProvider serializerProvider, final TypeSerializer typeSerializer) throws IOException {
+ ser(property, jsonGenerator);
+ }
+
+ private static void ser(final GraphSONVertexProperty graphSONVertexProperty, final JsonGenerator jsonGenerator) throws IOException {
+ final VertexProperty property = graphSONVertexProperty.getToSerialize();
+ final Map<String, Object> m = new HashMap<>();
+ m.put(GraphSONTokens.ID, property.id());
+ m.put(GraphSONTokens.VALUE, property.value());
+ m.put(GraphSONTokens.PROPERTIES, props(property));
+
+ jsonGenerator.writeObject(m);
+ }
+
+ private static Map<String, Object> props(final VertexProperty property) {
+ if (property instanceof DetachedVertexProperty) {
+ try {
+ return IteratorUtils.collectMap(property.iterators().propertyIterator(), Property::key, Property::value);
+ } catch (UnsupportedOperationException uoe) {
+ return new HashMap<>();
+ }
+ } else {
+ return (property.graph().features().vertex().supportsMetaProperties()) ?
+ IteratorUtils.collectMap(property.iterators().propertyIterator(), Property::key, Property::value) :
+ new HashMap<>();
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONWriter.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONWriter.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONWriter.java
new file mode 100644
index 0000000..4b6a2e5
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONWriter.java
@@ -0,0 +1,122 @@
+/*
+ * 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 com.tinkerpop.gremlin.structure.io.graphson;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.tinkerpop.gremlin.process.Traversal;
+import com.tinkerpop.gremlin.structure.Direction;
+import com.tinkerpop.gremlin.structure.Edge;
+import com.tinkerpop.gremlin.structure.Graph;
+import com.tinkerpop.gremlin.structure.Vertex;
+import com.tinkerpop.gremlin.structure.io.GraphWriter;
+
+import java.io.*;
+
+/**
+ * A @{link GraphWriter} that writes a graph and its elements to a JSON-based representation. This implementation
+ * only supports JSON data types and is therefore lossy with respect to data types (e.g. a float will become a double).
+ * Further note that serialized {@code Map} objects do not support complex types for keys. {@link Edge} and
+ * {@link Vertex} objects are serialized to {@code Map} instances. If an
+ * {@link com.tinkerpop.gremlin.structure.Element} is used as a key, it is coerced to its identifier. Other complex
+ * objects are converted via {@link Object#toString()} unless a mapper serializer is supplied.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class GraphSONWriter implements GraphWriter {
+ private final ObjectMapper mapper;
+
+ private GraphSONWriter(final GraphSONMapper mapper) {
+ this.mapper = mapper.createMapper();
+ }
+
+ @Override
+ public void writeGraph(final OutputStream outputStream, final Graph g) throws IOException {
+ this.mapper.writeValue(outputStream, new GraphSONGraph(g));
+ }
+
+ @Override
+ public void writeVertex(final OutputStream outputStream, final Vertex v, final Direction direction) throws IOException {
+ this.mapper.writeValue(outputStream, new GraphSONVertex(v, direction));
+ }
+
+ @Override
+ public void writeVertex(final OutputStream outputStream, final Vertex v) throws IOException {
+ this.mapper.writeValue(outputStream, v);
+ }
+
+ @Override
+ public void writeEdge(final OutputStream outputStream, final Edge e) throws IOException {
+ this.mapper.writeValue(outputStream, e);
+ }
+
+ @Override
+ public void writeVertices(final OutputStream outputStream, final Traversal<?, Vertex> traversal, final Direction direction) throws IOException {
+ final BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream));
+ try (final ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+ while (traversal.hasNext()) {
+ writeVertex(baos, traversal.next(), direction);
+ writer.write(new String(baos.toByteArray()));
+ writer.newLine();
+ baos.reset();
+ }
+ }
+
+ writer.flush();
+ }
+
+ @Override
+ public void writeVertices(final OutputStream outputStream, final Traversal<?, Vertex> traversal) throws IOException {
+ final BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream));
+ try (final ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+ while (traversal.hasNext()) {
+ writeVertex(baos, traversal.next());
+ writer.write(new String(baos.toByteArray()));
+ writer.newLine();
+ baos.reset();
+ }
+ }
+
+ writer.flush();
+ }
+
+ public static Builder build() {
+ return new Builder();
+ }
+
+ public static class Builder {
+
+ private GraphSONMapper mapper = GraphSONMapper.build().create();
+
+ private Builder() {
+ }
+
+ /**
+ * Override all of the builder options with this mapper. If this value is set to something other than
+ * null then that value will be used to construct the writer.
+ */
+ public Builder mapper(final GraphSONMapper mapper) {
+ this.mapper = mapper;
+ return this;
+ }
+
+ public GraphSONWriter create() {
+ return new GraphSONWriter(mapper);
+ }
+ }
+}