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:37 UTC
[11/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/Transaction.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/Transaction.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/Transaction.java
new file mode 100644
index 0000000..7720885
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/Transaction.java
@@ -0,0 +1,384 @@
+/*
+ * 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;
+
+import java.io.Closeable;
+import java.util.Collections;
+import java.util.Set;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+/**
+ * A set of methods that allow for control of transactional behavior of a {@link Graph} instance.
+ *
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ * @author TinkerPop Community (http://tinkerpop.com)
+ */
+public interface Transaction extends Closeable {
+
+ /**
+ * Opens a transaction.
+ */
+ public void open();
+
+ /**
+ * Commits a transaction.
+ */
+ public void commit();
+
+ /**
+ * Rolls back a transaction.
+ */
+ public void rollback();
+
+ /**
+ * Submit a unit of work that represents a transaction returning a {@link Workload} that can be automatically
+ * retried in the event of failure.
+ */
+ public <R> Workload<R> submit(final Function<Graph, R> work);
+
+ /**
+ * Creates a transaction that can be executed across multiple threads.
+ */
+ public <G extends Graph> G create();
+
+ /**
+ * Determines if a transaction is currently open.
+ */
+ public boolean isOpen();
+
+ /**
+ * An internal function that signals a read or a write has occurred - not meant to be called directly by end users.
+ */
+ public void readWrite();
+
+ /**
+ * Closes the transaction where the default close behavior will be executed.
+ */
+ @Override
+ public void close();
+
+ /**
+ * Describes how a transaction is started when a read or a write occurs. This value can be set using standard
+ * behaviors defined in {@link READ_WRITE_BEHAVIOR} or a mapper {@link Consumer} function.
+ */
+ public Transaction onReadWrite(final Consumer<Transaction> consumer);
+
+ /**
+ * Describes what happens to a transaction on a call to {@link com.tinkerpop.gremlin.structure.Graph#close()}.
+ * This value can be set using standard behavior defined in {@link CLOSE_BEHAVIOR} or a mapper {@link Consumer}
+ * function.
+ */
+ public Transaction onClose(final Consumer<Transaction> consumer);
+
+ public static class Exceptions {
+ public static IllegalStateException transactionAlreadyOpen() {
+ return new IllegalStateException("Stop the current transaction before opening another");
+ }
+
+ public static IllegalStateException transactionMustBeOpenToReadWrite() {
+ return new IllegalStateException("Open a transaction before attempting to read/write the transaction");
+ }
+
+ public static IllegalStateException openTransactionsOnClose() {
+ return new IllegalStateException("Commit or rollback all outstanding transactions before closing the transaction");
+ }
+
+ public static UnsupportedOperationException threadedTransactionsNotSupported() {
+ return new UnsupportedOperationException("Graph does not support threaded transactions");
+ }
+
+ public static IllegalArgumentException onCloseBehaviorCannotBeNull() {
+ return new IllegalArgumentException("Transaction behavior for onClose cannot be null");
+ }
+
+ public static IllegalArgumentException onReadWriteBehaviorCannotBeNull() {
+ return new IllegalArgumentException("Transaction behavior for onReadWrite cannot be null");
+ }
+ }
+
+ /**
+ * Behaviors to supply to the {@link #onClose(java.util.function.Consumer)}.
+ */
+ public static enum CLOSE_BEHAVIOR implements Consumer<Transaction> {
+ /**
+ * Any open transaction will commit on close.
+ */
+ COMMIT {
+ @Override
+ public void accept(final Transaction transaction) {
+ if (transaction.isOpen()) transaction.commit();
+ }
+ },
+
+ /**
+ * Any open transaction will rollback on close.
+ */
+ ROLLBACK {
+ @Override
+ public void accept(final Transaction transaction) {
+ if (transaction.isOpen()) transaction.rollback();
+ }
+ },
+
+ /**
+ * Open transactions on close will throw an exception
+ */
+ MANUAL {
+ @Override
+ public void accept(final Transaction transaction) {
+ if (transaction.isOpen()) throw Exceptions.openTransactionsOnClose();
+ }
+ }
+ }
+
+ /**
+ * Behaviors to supply to the {@link #onReadWrite(java.util.function.Consumer)}.
+ */
+ public static enum READ_WRITE_BEHAVIOR implements Consumer<Transaction> {
+ /**
+ * Transactions are automatically started when a read or a write occurs.
+ */
+ AUTO {
+ @Override
+ public void accept(final Transaction transaction) {
+ if (!transaction.isOpen()) transaction.open();
+ }
+ },
+
+ /**
+ * Transactions must be explicitly opened for operations to occur on the graph.
+ */
+ MANUAL {
+ @Override
+ public void accept(final Transaction transaction) {
+ if (!transaction.isOpen()) throw Exceptions.transactionMustBeOpenToReadWrite();
+ }
+ }
+ }
+
+ /**
+ * A {@link Workload} represents a unit of work constructed by the
+ * {@link Workload#submit(java.util.function.Function)} method on the {@link Transaction} interface.
+ * The unit of work is a {@link java.util.function.Function} typically containing mutations to the {@link Graph}. The
+ * {@link Workload} is responsible for executing the unit of work in the context of a retry strategy, such that a
+ * failed unit of work is rolled back and executed again until retries are exhausted or the unit of work succeeds
+ * with a commit operation.
+ *
+ * @param <R> The type of the result from the unit of work.
+ */
+ public static class Workload<R> {
+ public static final long DEFAULT_DELAY_MS = 20;
+ public static final int DEFAULT_TRIES = 8;
+
+ private final Function<Graph, R> workToDo;
+ private final Graph g;
+
+ /**
+ * Creates a new {@link Workload} that will be tried to be executed within a transaction.
+ *
+ * @param g The {@link Graph} instance on which the work will be performed.
+ * @param work The work to be executed on the Graph instance which will optionally return a value.
+ */
+ public Workload(final Graph g, final Function<Graph, R> work) {
+ this.g = g;
+ this.workToDo = work;
+ }
+
+ /**
+ * Try to execute a {@link Workload} with a mapper retry strategy.
+ *
+ * @param retryStrategy The first argument to this function is the Graph instance and the second is
+ * the encapsulated work to be performed. The function should ultimately return the
+ * result of the encapsulated work function.
+ * @return The result of the encapsulated work.
+ */
+ public R attempt(final BiFunction<Graph, Function<Graph, R>, R> retryStrategy) {
+ return retryStrategy.apply(g, workToDo);
+ }
+
+ /**
+ * Executes the {@link Workload} committing if possible and rolling back on failure. On failure, an exception
+ * is reported.
+ */
+ public R oneAndDone() {
+ return attempt((g, w) -> {
+ try {
+ R result = w.apply(g);
+ g.tx().commit();
+
+ return result;
+ } catch (Throwable t) {
+ g.tx().rollback();
+ throw new RuntimeException(t);
+ }
+ });
+ }
+
+ /**
+ * Executes the {@link Workload} committing if possible and rolling back on failure. On failure no exception
+ * is reported.
+ */
+ public R fireAndForget() {
+ return attempt((g, w) -> {
+ R result = null;
+ try {
+ result = w.apply(g);
+ g.tx().commit();
+ } catch (Throwable t) {
+ g.tx().rollback();
+ }
+
+ return result;
+ });
+ }
+
+ /**
+ * Executes the {@link Workload} with the default number of retries and with the default number of
+ * milliseconds delay between each try.
+ */
+ public R retry() {
+ return retry(DEFAULT_TRIES);
+ }
+
+ /**
+ * Executes the {@link Workload} with a number of retries and with the default number of milliseconds delay
+ * between each try.
+ */
+ public R retry(final int tries) {
+ return retry(tries, DEFAULT_DELAY_MS);
+ }
+
+ /**
+ * Executes the {@link Workload} with a number of retries and with a number of milliseconds delay between
+ * each try.
+ */
+ public R retry(final int tries, final long delay) {
+ return retry(tries, delay, Collections.emptySet());
+ }
+
+ /**
+ * Executes the {@link Workload} with a number of retries and with a number of milliseconds delay between each
+ * try and will only retry on the set of supplied exceptions. Exceptions outside of that set will generate a
+ * RuntimeException and immediately fail.
+ */
+ public R retry(final int tries, final long delay, final Set<Class> exceptionsToRetryOn) {
+ return attempt(retry(tries, exceptionsToRetryOn, i -> delay));
+ }
+
+ /**
+ * Executes the {@link Workload} with the default number of retries and with a exponentially increasing
+ * number of milliseconds between each retry using the default retry delay.
+ */
+ public R exponentialBackoff() {
+ return exponentialBackoff(DEFAULT_TRIES);
+ }
+
+ /**
+ * Executes the {@link Workload} with a number of retries and with a exponentially increasing number of
+ * milliseconds between each retry using the default retry delay.
+ */
+ public R exponentialBackoff(final int tries) {
+ return exponentialBackoff(tries, DEFAULT_DELAY_MS);
+ }
+
+ /**
+ * Executes the {@link Workload} with a number of retries and with a exponentially increasing number of
+ * milliseconds between each retry.
+ */
+ public R exponentialBackoff(final int tries, final long initialDelay) {
+ return exponentialBackoff(tries, initialDelay, Collections.emptySet());
+ }
+
+ /**
+ * Executes the {@link Workload} with a number of retries and with a exponentially increasing number of
+ * milliseconds between each retry. It will only retry on the set of supplied exceptions. Exceptions outside
+ * of that set will generate a {@link RuntimeException} and immediately fail.
+ */
+ public R exponentialBackoff(final int tries, final long initialDelay, final Set<Class> exceptionsToRetryOn) {
+ return attempt(retry(tries, exceptionsToRetryOn, retryCount -> (long) (initialDelay * Math.pow(2, retryCount))));
+ }
+
+ /**
+ * Creates a generic retry function to be passed to the {@link Workload#attempt(java.util.function.BiFunction)}
+ * method.
+ */
+ private static <R> BiFunction<Graph, Function<Graph, R>, R> retry(final int tries,
+ final Set<Class> exceptionsToRetryOn,
+ final Function<Integer, Long> delay) {
+ return (g, w) -> {
+ R returnValue;
+
+ // this is the default exception...it may get reassgined during retries
+ Exception previousException = new RuntimeException("Exception initialized when trying commit");
+
+ // try to commit a few times
+ for (int ix = 0; ix < tries; ix++) {
+
+ // increase time after each failed attempt though there is no delay on the first try
+ if (ix > 0)
+ try {
+ Thread.sleep(delay.apply(ix));
+ } catch (InterruptedException ignored) {
+ }
+
+ try {
+ // ensure that a transaction is open for this try. even if there was an open transaction
+ // from the first try it would be rolled back on failure and unless automatic transactions
+ // are used, the transaction would be closed on the next retry.
+ if (!g.tx().isOpen()) g.tx().open();
+
+ returnValue = w.apply(g);
+ g.tx().commit();
+
+ // need to exit the function here so that retries don't happen
+ return returnValue;
+ } catch (Exception ex) {
+ g.tx().rollback();
+
+ // retry if this is an allowed exception otherwise, just throw and go
+ boolean retry = false;
+ if (exceptionsToRetryOn.size() == 0)
+ retry = true;
+ else {
+ for (Class exceptionToRetryOn : exceptionsToRetryOn) {
+ if (ex.getClass().equals(exceptionToRetryOn)) {
+ retry = true;
+ break;
+ }
+ }
+ }
+
+ if (!retry) {
+ throw new RuntimeException(ex);
+ }
+
+ previousException = ex;
+ }
+ }
+
+ // the exception just won't go away after all the retries
+ throw new RuntimeException(previousException);
+ };
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/Vertex.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/Vertex.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/Vertex.java
new file mode 100644
index 0000000..b2fb418
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/Vertex.java
@@ -0,0 +1,171 @@
+/*
+ * 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;
+
+import com.tinkerpop.gremlin.process.graph.traversal.VertexTraversal;
+import com.tinkerpop.gremlin.structure.util.ElementHelper;
+
+import java.util.Iterator;
+import java.util.Optional;
+
+/**
+ * A {@link Vertex} maintains pointers to both a set of incoming and outgoing {@link Edge} objects. The outgoing edges
+ * are those edges for which the {@link Vertex} is the tail. The incoming edges are those edges for which the
+ * {@link Vertex} is the head.
+ * <p/>
+ * Diagrammatically:
+ * <pre>
+ * ---inEdges---> vertex ---outEdges--->.
+ * </pre>
+ *
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public interface Vertex extends Element, VertexTraversal {
+
+ /**
+ * The default label to use for a vertex.
+ */
+ public static final String DEFAULT_LABEL = "vertex";
+
+ /**
+ * Add an outgoing edge to the vertex with provided label and edge properties as key/value pairs.
+ * These key/values must be provided in an even number where the odd numbered arguments are {@link String}
+ * property keys and the even numbered arguments are the related property values.
+ *
+ * @param label The label of the edge
+ * @param inVertex The vertex to receive an incoming edge from the current vertex
+ * @param keyValues The key/value pairs to turn into edge properties
+ * @return the newly created edge
+ */
+ public Edge addEdge(final String label, final Vertex inVertex, final Object... keyValues);
+
+ @Override
+ public default <V> VertexProperty<V> property(final String key) {
+ final Iterator<VertexProperty<V>> iterator = this.iterators().propertyIterator(key);
+ if (iterator.hasNext()) {
+ final VertexProperty<V> property = iterator.next();
+ if (iterator.hasNext())
+ throw Vertex.Exceptions.multiplePropertiesExistForProvidedKey(key);
+ else
+ return property;
+ } else {
+ return VertexProperty.<V>empty();
+ }
+ }
+
+ @Override
+ public <V> VertexProperty<V> property(final String key, final V value);
+
+ public default <V> VertexProperty<V> property(final String key, final V value, final Object... keyValues) {
+ ElementHelper.legalPropertyKeyValueArray(keyValues);
+ final Optional<Object> optionalId = ElementHelper.getIdValue(keyValues);
+ if (optionalId.isPresent() && !graph().features().vertex().properties().supportsUserSuppliedIds())
+ throw VertexProperty.Exceptions.userSuppliedIdsNotSupported();
+
+ final VertexProperty<V> vertexProperty = this.property(key, value);
+ ElementHelper.attachProperties(vertexProperty, keyValues);
+ return vertexProperty;
+ }
+
+ public default <V> VertexProperty<V> property(final VertexProperty.Cardinality cardinality, final String key, final V value, final Object... keyValues) {
+ if (cardinality.equals(VertexProperty.Cardinality.list))
+ return this.property(key, value, keyValues);
+ else if (cardinality.equals(VertexProperty.Cardinality.single)) {
+ this.iterators().propertyIterator(key).forEachRemaining(VertexProperty::remove);
+ return this.property(key, value, keyValues);
+ } else if (cardinality.equals(VertexProperty.Cardinality.set)) {
+ final Iterator<VertexProperty<V>> iterator = this.iterators().propertyIterator(key);
+ while (iterator.hasNext()) {
+ final VertexProperty<V> property = iterator.next();
+ if (property.value().equals(value)) {
+ ElementHelper.attachProperties(property, keyValues);
+ return property;
+ }
+ }
+ return this.property(key, value, keyValues);
+ } else {
+ throw new IllegalArgumentException("The provided cardinality is unknown: " + cardinality);
+ }
+ }
+
+ /**
+ * Get the {@link Vertex.Iterators} implementation associated with this {@code Vertex}.
+ * <p/>
+ * {@inheritDoc}
+ */
+ @Override
+ public Vertex.Iterators iterators();
+
+ /**
+ * An interface that provides access to iterators over {@link VertexProperty} objects, {@link Edge} objects
+ * and adjacent vertices, associated with the {@code Vertex}, without constructing a
+ * {@link com.tinkerpop.gremlin.process.Traversal} object.
+ */
+ public interface Iterators extends Element.Iterators {
+ /**
+ * Gets an {@link Iterator} of incident edges.
+ *
+ * @param direction The incident direction of the edges to retrieve off this vertex
+ * @param edgeLabels The labels of the edges to retrieve. If no labels are provided, then get all edges.
+ * @return An iterator of edges meeting the provided specification
+ */
+ public Iterator<Edge> edgeIterator(final Direction direction, final String... edgeLabels);
+
+ /**
+ * Gets an {@link Iterator} of adjacent vertices.
+ *
+ * @param direction The adjacency direction of the vertices to retrieve off this vertex
+ * @param edgeLabels The labels of the edges associated with the vertices to retrieve. If no labels are provided, then get all edges.
+ * @return An iterator of vertices meeting the provided specification
+ */
+ public Iterator<Vertex> vertexIterator(final Direction direction, final String... edgeLabels);
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public <V> Iterator<VertexProperty<V>> propertyIterator(final String... propertyKeys);
+ }
+
+ /**
+ * Common exceptions to use with a vertex.
+ */
+ public static class Exceptions {
+ public static UnsupportedOperationException userSuppliedIdsNotSupported() {
+ return new UnsupportedOperationException("Vertex does not support user supplied identifiers");
+ }
+
+ public static UnsupportedOperationException userSuppliedIdsOfThisTypeNotSupported() {
+ return new UnsupportedOperationException("Vertex does not support user supplied identifiers of this type");
+ }
+
+ public static IllegalStateException vertexRemovalNotSupported() {
+ return new IllegalStateException("Vertex removal are not supported");
+ }
+
+ public static IllegalStateException edgeAdditionsNotSupported() {
+ return new IllegalStateException("Edge additions not supported");
+ }
+
+ public static IllegalStateException multiplePropertiesExistForProvidedKey(final String propertyKey) {
+ return new IllegalStateException("Multiple properties exist for the provided key, use Vertex.properties(" + propertyKey + ')');
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/VertexProperty.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/VertexProperty.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/VertexProperty.java
new file mode 100644
index 0000000..0eba9e6
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/VertexProperty.java
@@ -0,0 +1,118 @@
+/*
+ * 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;
+
+import com.tinkerpop.gremlin.process.graph.traversal.VertexPropertyTraversal;
+import com.tinkerpop.gremlin.structure.util.empty.EmptyVertexProperty;
+
+import java.util.Iterator;
+
+/**
+ * A {@code VertexProperty} is similar to a {@link Property} in that it denotes a key/value pair associated with an
+ * {@link Vertex}, however it is different in the sense that it also represents an entity that it is an {@link Element}
+ * that can have properties of its own.
+ * <br/>
+ * A property is much like a Java8 {@link java.util.Optional} in that a property can be not present (i.e. empty).
+ * The key of a property is always a String and the value of a property is an arbitrary Java object.
+ * Each underlying graph engine will typically have constraints on what Java objects are allowed to be used as values.
+ *
+ * @author Matthias Broecheler (me@matthiasb.com)
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public interface VertexProperty<V> extends Property<V>, Element, VertexPropertyTraversal {
+
+ public static final String DEFAULT_LABEL = "vertexProperty";
+
+ public enum Cardinality {
+ single, list, set
+ }
+
+ /**
+ * Gets the {@link Vertex} that owns this {@code VertexProperty}.
+ */
+ @Override
+ public Vertex element();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ @Graph.Helper
+ public default Graph graph() {
+ return this.element().graph();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ @Graph.Helper
+ public default String label() {
+ return this.key();
+ }
+
+ /**
+ * Constructs an empty {@code VertexProperty}.
+ */
+ public static <V> VertexProperty<V> empty() {
+ return EmptyVertexProperty.instance();
+ }
+
+ /**
+ * Gets the {@link VertexProperty.Iterators} set.
+ * <p/>
+ * {@inheritDoc}
+ */
+ @Override
+ public VertexProperty.Iterators iterators();
+
+ /**
+ * An interface that provides access to iterators over properties, without constructing a
+ * {@link com.tinkerpop.gremlin.process.Traversal} object.
+ */
+ public interface Iterators extends Element.Iterators {
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public <U> Iterator<Property<U>> propertyIterator(final String... propertyKeys);
+ }
+
+ /**
+ * Common exceptions to use with a property.
+ */
+ public static class Exceptions {
+ public static UnsupportedOperationException userSuppliedIdsNotSupported() {
+ return new UnsupportedOperationException("VertexProperty does not support user supplied identifiers");
+ }
+
+ public static UnsupportedOperationException userSuppliedIdsOfThisTypeNotSupported() {
+ return new UnsupportedOperationException("VertexProperty does not support user supplied identifiers of this type");
+ }
+
+ public static UnsupportedOperationException multiPropertiesNotSupported() {
+ return new UnsupportedOperationException("Multiple properties on a vertex is not supported");
+ }
+
+ public static UnsupportedOperationException metaPropertiesNotSupported() {
+ return new UnsupportedOperationException("Properties on a vertex property is not supported");
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/DefaultIo.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/DefaultIo.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/DefaultIo.java
new file mode 100644
index 0000000..3013a76
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/DefaultIo.java
@@ -0,0 +1,82 @@
+/*
+ * 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;
+
+import com.tinkerpop.gremlin.structure.Graph;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Default implementation of the {@link Graph.Io} interface which overrides none of the default methods.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class DefaultIo implements Graph.Io {
+ private final Graph g;
+
+ public DefaultIo(final Graph g) {
+ this.g = g;
+ }
+
+ @Override
+ public void writeKryo(final String file) throws IOException {
+ try (final OutputStream out = new FileOutputStream(file)) {
+ kryoWriter().create().writeGraph(out, g);
+ }
+ }
+
+ @Override
+ public void readKryo(final String file) throws IOException {
+ try (final InputStream in = new FileInputStream(file)) {
+ kryoReader().create().readGraph(in, g);
+ }
+ }
+
+ @Override
+ public void writeGraphML(final String file) throws IOException {
+ try (final OutputStream out = new FileOutputStream(file)) {
+ graphMLWriter().create().writeGraph(out, g);
+ }
+ }
+
+ @Override
+ public void readGraphML(final String file) throws IOException {
+ try (final InputStream in = new FileInputStream(file)) {
+ graphMLReader().create().readGraph(in, g);
+ }
+ }
+
+ @Override
+ public void writeGraphSON(final String file) throws IOException {
+ try (final OutputStream out = new FileOutputStream(file)) {
+ graphSONWriter().create().writeGraph(out, g);
+ }
+ }
+
+ @Override
+ public void readGraphSON(final String file) throws IOException {
+ try (final InputStream in = new FileInputStream(file)) {
+ graphSONReader().create().readGraph(in, g);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/GraphMigrator.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/GraphMigrator.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/GraphMigrator.java
new file mode 100644
index 0000000..b923093
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/GraphMigrator.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;
+
+import com.tinkerpop.gremlin.structure.Graph;
+import com.tinkerpop.gremlin.structure.io.kryo.KryoReader;
+import com.tinkerpop.gremlin.structure.io.kryo.KryoWriter;
+
+import java.io.IOException;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+
+/**
+ * {@link GraphMigrator} takes the data in one graph and pipes it to another graph. Uses the {@link KryoReader}
+ * and {@link KryoWriter} by default.
+ *
+ * @author Alex Averbuch (alex.averbuch@gmail.com)
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public final class GraphMigrator {
+
+ private static final KryoReader defaultKryoReader = KryoReader.build().create();
+ private static final KryoWriter defaultKryoWriter = KryoWriter.build().create();
+
+ /**
+ * Use Kryo to pipe the data from one graph to another graph. Uses all default settings for reader/writers.
+ * Refer to {@link KryoReader} and {@link KryoWriter} for those settings. To use features like incremental
+ * loading, construct the reader/writers manually and utilize
+ * {@link #migrateGraph(com.tinkerpop.gremlin.structure.Graph, com.tinkerpop.gremlin.structure.Graph, GraphReader, GraphWriter)}
+ */
+ public static void migrateGraph(final Graph fromGraph, final Graph toGraph) throws IOException {
+ migrateGraph(fromGraph, toGraph, defaultKryoReader, defaultKryoWriter);
+ }
+
+ /**
+ * Pipe the data from one graph to another graph. It is important that the reader and writer utilize the
+ * same format.
+ *
+ * @param fromGraph the graph to take data from
+ * @param toGraph the graph to take data to
+ * @param reader reads from the graph written by the writer
+ * @param writer writes the graph to be read by the reader
+ * @throws java.io.IOException thrown if there is an error in steam between the two graphs
+ */
+ public static void migrateGraph(final Graph fromGraph, final Graph toGraph,
+ final GraphReader reader, final GraphWriter writer) throws IOException {
+ final PipedInputStream inPipe = new PipedInputStream(1024);
+
+ final PipedOutputStream outPipe = new PipedOutputStream(inPipe) {
+ @Override
+ public void close() throws IOException {
+ while (inPipe.available() > 0) {
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException e) {
+ // do nothing
+ }
+ }
+ super.close();
+ }
+ };
+
+ new Thread(() -> {
+ try {
+ writer.writeGraph(outPipe, fromGraph);
+ outPipe.flush();
+ outPipe.close();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ } finally {
+ if (fromGraph.features().graph().supportsTransactions()) fromGraph.tx().rollback();
+ if (toGraph.features().graph().supportsTransactions()) toGraph.tx().rollback();
+ }
+ }).start();
+
+ reader.readGraph(inPipe, toGraph);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/GraphReader.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/GraphReader.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/GraphReader.java
new file mode 100644
index 0000000..73d218d
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/GraphReader.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.tinkerpop.gremlin.structure.io;
+
+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.util.detached.DetachedEdge;
+import com.tinkerpop.gremlin.structure.util.detached.DetachedVertex;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Iterator;
+import java.util.function.Function;
+
+/**
+ * Functions for reading a graph and its graph elements from a different format.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public interface GraphReader {
+
+ /**
+ * Reads an entire graph from an {@link InputStream}.
+ *
+ * @param inputStream a stream containing a single vertex as defined by the accompanying {@link GraphWriter}
+ */
+ public void readGraph(final InputStream inputStream, final Graph graphToWriteTo) throws IOException;
+
+ /**
+ * Reads a single vertex from an {@link InputStream}. This method will read vertex properties but not edges.
+ *
+ * @param inputStream a stream containing a single vertex as defined by the accompanying {@link GraphWriter}
+ * @param vertexMaker a function to create a vertex where the first argument is the vertex identifier, the
+ * second argument is vertex label and the last is the list of properties for it
+ */
+ public Vertex readVertex(final InputStream inputStream, final Function<DetachedVertex, Vertex> vertexMaker) throws IOException;
+
+ /**
+ * Reads a single vertex from an {@link InputStream}. This method will read vertex properties as well as edges
+ * given the direction supplied as an argument.
+ *
+ * @param inputStream a stream containing a single vertex as defined by the accompanying {@link GraphWriter}
+ * @param direction the direction of edges to read.
+ * @param vertexMaker a function to create a vertex where the first argument is the vertex identifier, the
+ * second argument is vertex label and the last is the list of properties for it
+ * @param edgeMaker a function that creates an edge from the stream where the first argument is the edge
+ * identifier, the second argument is the out vertex id, the third is the in vertex id,
+ * the fourth is the label, and the fifth is the list of properties as key/value pairs.
+ */
+ public Vertex readVertex(final InputStream inputStream, final Direction direction,
+ final Function<DetachedVertex, Vertex> vertexMaker,
+ final Function<DetachedEdge, Edge> edgeMaker) throws IOException;
+
+ /**
+ * Reads a set of vertices from an {@link InputStream} which were written by
+ * {@link GraphWriter#writeVertices(java.io.OutputStream, com.tinkerpop.gremlin.process.Traversal)}. This method
+ * will read vertex properties as well as edges given the direction supplied as an argument.
+ *
+ * @param inputStream a stream containing a single vertex as defined by the accompanying {@link GraphWriter}
+ * @param direction the direction of edges to read.
+ * @param vertexMaker a function to create a vertex where the first argument is the vertex identifier, the
+ * second argument is vertex label and the last is the list of properties for it
+ * @param edgeMaker a function that creates an edge from the stream where the first argument is the edge
+ * identifier, the second argument is the out vertex id, the third is the in vertex id,
+ * the fourth is the label, and the fifth is the list of properties as key/value pairs.
+ */
+ public Iterator<Vertex> readVertices(final InputStream inputStream, final Direction direction,
+ final Function<DetachedVertex, Vertex> vertexMaker,
+ final Function<DetachedEdge, Edge> edgeMaker) throws IOException;
+
+ /**
+ * Reads a single edge from an {@link InputStream}.
+ *
+ * @param inputStream a stream containing a single vertex as defined by the accompanying {@link GraphWriter}
+ * @param edgeMaker a function that creates an edge from the stream where the first argument is the edge
+ * identifier, the second argument is the out vertex id, the third is the in vertex id,
+ * the fourth is the label, and the fifth is the list of properties as key/value pairs.
+ */
+ public Edge readEdge(final InputStream inputStream, final Function<DetachedEdge, Edge> edgeMaker) throws IOException;
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/GraphWriter.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/GraphWriter.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/GraphWriter.java
new file mode 100644
index 0000000..e5ffa9c
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/GraphWriter.java
@@ -0,0 +1,89 @@
+/*
+ * 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;
+
+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 java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Functions for writing a graph and its elements to a different format.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public interface GraphWriter {
+ /**
+ * Write the entire graph to a stream.
+ */
+ public void writeGraph(final OutputStream outputStream, final Graph g) throws IOException;
+
+ /**
+ * Write a vertex to a stream with its associated edges. Only write edges as defined by the requested direction.
+ *
+ * @param outputStream The stream to write to.
+ * @param v The vertex to write.
+ * @param direction If direction is null then no edges are written.
+ */
+ public void writeVertex(final OutputStream outputStream, final Vertex v, final Direction direction) throws IOException;
+
+ /**
+ * Write a vertex to a stream without writing its edges.
+ *
+ * @param outputStream The stream to write to.
+ * @param v The vertex to write.
+ */
+ public void writeVertex(final OutputStream outputStream, final Vertex v) throws IOException;
+
+
+ /**
+ * Write a list of vertices from a {@link Traversal} to a stream with its associated edges. Only write edges as
+ * defined by the requested direction.
+ *
+ * @param outputStream The stream to write to.
+ * @param traversal A traversal that returns a list of vertices.
+ * @param direction If direction is null then no edges are written.
+ */
+ public default void writeVertices(final OutputStream outputStream, final Traversal<?, Vertex> traversal, final Direction direction) throws IOException {
+ while (traversal.hasNext()) {
+ writeVertex(outputStream, traversal.next(), direction);
+ }
+ }
+
+ /**
+ * Write a vertex to a stream without writing its edges.
+ *
+ * @param outputStream The stream to write to.
+ * @param traversal A traversal that returns a list of vertices.
+ */
+ public default void writeVertices(final OutputStream outputStream, final Traversal<?, Vertex> traversal) throws IOException {
+ while (traversal.hasNext()) {
+ writeVertex(outputStream, traversal.next());
+ }
+ }
+
+ /**
+ * Write an edge to a stream.
+ */
+ public void writeEdge(final OutputStream outputStream, final Edge e) throws IOException;
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/Mapper.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/Mapper.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/Mapper.java
new file mode 100644
index 0000000..32b5404
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/Mapper.java
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+/**
+ * Represents a low-level serialization class that can be used to map classes to serializers. These implementation
+ * create instances of serializers from other libraries (e.g. creating a {@code Kryo} instance).
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public interface Mapper<T> {
+ /**
+ * Create a new instance of the internal object mapper that an implementation represents.
+ */
+ public T createMapper();
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphml/GraphMLReader.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphml/GraphMLReader.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphml/GraphMLReader.java
new file mode 100644
index 0000000..226b2ff
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphml/GraphMLReader.java
@@ -0,0 +1,312 @@
+/*
+ * 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 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.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 javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.events.XMLEvent;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+/**
+ * GraphMLReader writes the data from a GraphML stream to a graph. Note that this format is lossy, in the sense that data
+ * types and features of Gremlin Structure not supported by GraphML are not serialized. This format is meant for
+ * external export of a graph to tools outside of Gremlin Structure graphs. Note that GraphML does not support
+ * the notion of multi-properties or properties on properties.
+ *
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ * @author Alex Averbuch (alex.averbuch@gmail.com)
+ * @author Joshua Shinavier (http://fortytwo.net)
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class GraphMLReader implements GraphReader {
+ private final XMLInputFactory inputFactory = XMLInputFactory.newInstance();
+
+ private final String vertexIdKey;
+ private final String edgeIdKey;
+ private final String edgeLabelKey;
+ private final String vertexLabelKey;
+ private final long batchSize;
+
+ private GraphMLReader(final String vertexIdKey, final String edgeIdKey,
+ final String edgeLabelKey, final String vertexLabelKey,
+ final long batchSize) {
+ this.vertexIdKey = vertexIdKey;
+ this.edgeIdKey = edgeIdKey;
+ this.edgeLabelKey = edgeLabelKey;
+ this.batchSize = batchSize;
+ this.vertexLabelKey = vertexLabelKey;
+ }
+
+ @Override
+ public Iterator<Vertex> readVertices(final InputStream inputStream, final Direction direction,
+ final Function<DetachedVertex, Vertex> vertexMaker,
+ final Function<DetachedEdge, Edge> edgeMaker) throws IOException {
+ throw new UnsupportedOperationException("GraphML does not allow for a partial structure");
+ }
+
+ @Override
+ public Edge readEdge(final InputStream inputStream, final Function<DetachedEdge, Edge> edgeMaker) throws IOException {
+ throw new UnsupportedOperationException("GraphML does not allow for a partial structure");
+ }
+
+ @Override
+ public Vertex readVertex(final InputStream inputStream, final Function<DetachedVertex, Vertex> vertexMaker) throws IOException {
+ throw new UnsupportedOperationException("GraphML does not allow for a partial structure");
+ }
+
+ @Override
+ public Vertex readVertex(final InputStream inputStream, final Direction direction, final Function<DetachedVertex, Vertex> vertexMaker,
+ final Function<DetachedEdge, Edge> edgeMaker) throws IOException {
+ throw new UnsupportedOperationException("GraphML does not allow for a partial structure");
+ }
+
+ @Override
+ public void readGraph(final InputStream graphInputStream, final Graph graphToWriteTo) throws IOException {
+ final BatchGraph graph;
+ try {
+ // will throw an exception if not constructed properly
+ graph = BatchGraph.build(graphToWriteTo)
+ .vertexIdKey(vertexIdKey)
+ .bufferSize(batchSize).create();
+ } catch (Exception ex) {
+ throw new IOException("Could not instantiate BatchGraph wrapper", ex);
+ }
+
+ try {
+ final XMLStreamReader reader = inputFactory.createXMLStreamReader(graphInputStream);
+ final Map<String, String> keyIdMap = new HashMap<>();
+ final Map<String, String> keyTypesMaps = new HashMap<>();
+
+ // Buffered Vertex Data
+ String vertexId = null;
+ String vertexLabel = null;
+ Map<String, Object> vertexProps = null;
+ boolean isInVertex = false;
+
+ // Buffered Edge Data
+ String edgeId = null;
+ String edgeLabel = null;
+ Vertex edgeInVertex = null;
+ Vertex edgeOutVertex = null;
+ Map<String, Object> edgeProps = null;
+ boolean isInEdge = false;
+
+ while (reader.hasNext()) {
+ final Integer eventType = reader.next();
+ if (eventType.equals(XMLEvent.START_ELEMENT)) {
+ final String elementName = reader.getName().getLocalPart();
+
+ switch (elementName) {
+ case GraphMLTokens.KEY:
+ final String id = reader.getAttributeValue(null, GraphMLTokens.ID);
+ final String attributeName = reader.getAttributeValue(null, GraphMLTokens.ATTR_NAME);
+ final String attributeType = reader.getAttributeValue(null, GraphMLTokens.ATTR_TYPE);
+ keyIdMap.put(id, attributeName);
+ keyTypesMaps.put(id, attributeType);
+ break;
+ case GraphMLTokens.NODE:
+ vertexId = reader.getAttributeValue(null, GraphMLTokens.ID);
+ isInVertex = true;
+ vertexProps = new HashMap<>();
+ break;
+ case GraphMLTokens.EDGE:
+ edgeId = reader.getAttributeValue(null, GraphMLTokens.ID);
+
+ final String vertexIdOut = reader.getAttributeValue(null, GraphMLTokens.SOURCE);
+ final String vertexIdIn = reader.getAttributeValue(null, GraphMLTokens.TARGET);
+
+ // graphml allows edges and vertices to be mixed in terms of how they are positioned
+ // in the xml therefore it is possible that an edge is created prior to its definition
+ // as a vertex.
+ Iterator<Vertex> iterator = graph.iterators().vertexIterator(vertexIdOut);
+ edgeOutVertex = iterator.hasNext() ? iterator.next() : graph.addVertex(T.id, vertexIdOut);
+ iterator = graph.iterators().vertexIterator(vertexIdIn);
+ edgeInVertex = iterator.hasNext() ? iterator.next() : graph.addVertex(T.id, vertexIdIn);
+
+ isInEdge = true;
+ edgeProps = new HashMap<>();
+
+ break;
+ case GraphMLTokens.DATA:
+ final String key = reader.getAttributeValue(null, GraphMLTokens.KEY);
+ final String dataAttributeName = keyIdMap.get(key);
+
+ if (dataAttributeName != null) {
+ final String value = reader.getElementText();
+
+ if (isInVertex) {
+ if (key.equals(vertexLabelKey))
+ vertexLabel = value;
+ else
+ vertexProps.put(dataAttributeName, typeCastValue(key, value, keyTypesMaps));
+ } else if (isInEdge) {
+ if (key.equals(edgeLabelKey))
+ edgeLabel = value;
+ else if (key.equals(edgeIdKey))
+ edgeId = value;
+ else
+ edgeProps.put(dataAttributeName, typeCastValue(key, value, keyTypesMaps));
+ }
+ }
+
+ break;
+ }
+ } else if (eventType.equals(XMLEvent.END_ELEMENT)) {
+ final String elementName = reader.getName().getLocalPart();
+
+ if (elementName.equals(GraphMLTokens.NODE)) {
+ final String currentVertexId = vertexId;
+ final String currentVertexLabel = Optional.ofNullable(vertexLabel).orElse(Vertex.DEFAULT_LABEL);
+ final Object[] propsAsArray = vertexProps.entrySet().stream().flatMap(e -> Stream.of(e.getKey(), e.getValue())).toArray();
+
+ // if incremental loading is on in batchgraph it handles graphml spec where it states that
+ // order of edges/vertices may be mixed such that an edge may be created before an vertex.
+ graph.addVertex(Stream.concat(Stream.of(T.id, currentVertexId, T.label, currentVertexLabel),
+ Stream.of(propsAsArray)).toArray());
+
+ vertexId = null;
+ vertexLabel = null;
+ vertexProps = null;
+ isInVertex = false;
+ } else if (elementName.equals(GraphMLTokens.EDGE)) {
+ final Object[] propsAsArray = edgeProps.entrySet().stream().flatMap(e -> Stream.of(e.getKey(), e.getValue())).toArray();
+ edgeOutVertex.addEdge(edgeLabel, edgeInVertex, Stream.concat(Stream.of(T.id, edgeId),
+ Stream.of(propsAsArray)).toArray());
+
+ edgeId = null;
+ edgeLabel = null;
+ edgeOutVertex = null;
+ edgeInVertex = null;
+ edgeProps = null;
+ isInEdge = false;
+ }
+
+ }
+ }
+
+ graph.tx().commit();
+ } catch (XMLStreamException xse) {
+ // rollback whatever portion failed
+ graph.tx().rollback();
+ throw new IOException(xse);
+ }
+ }
+
+ private static Object typeCastValue(final String key, final String value, final Map<String, String> keyTypes) {
+ final String type = keyTypes.get(key);
+ if (null == type || type.equals(GraphMLTokens.STRING))
+ return value;
+ else if (type.equals(GraphMLTokens.FLOAT))
+ return Float.valueOf(value);
+ else if (type.equals(GraphMLTokens.INT))
+ return Integer.valueOf(value);
+ else if (type.equals(GraphMLTokens.DOUBLE))
+ return Double.valueOf(value);
+ else if (type.equals(GraphMLTokens.BOOLEAN))
+ return Boolean.valueOf(value);
+ else if (type.equals(GraphMLTokens.LONG))
+ return Long.valueOf(value);
+ else
+ return value;
+ }
+
+ public static Builder build() {
+ return new Builder();
+ }
+
+ /**
+ * Allows configuration and construction of the GraphMLReader instance.
+ */
+ public static final class Builder {
+ private String vertexIdKey = T.id.getAccessor();
+ private String edgeIdKey = T.id.getAccessor();
+ private String edgeLabelKey = GraphMLTokens.LABEL_E;
+ private String vertexLabelKey = GraphMLTokens.LABEL_V;
+ private long batchSize = BatchGraph.DEFAULT_BUFFER_SIZE;
+
+ 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;
+ }
+
+ /**
+ * The key to use as the edge label.
+ */
+ public Builder edgeLabelKey(final String edgeLabelKey) {
+ this.edgeLabelKey = edgeLabelKey;
+ return this;
+ }
+
+ /**
+ * the key to use as the vertex label.
+ */
+ public Builder vertexLabelKey(final String vertexLabelKey) {
+ this.vertexLabelKey = vertexLabelKey;
+ return this;
+ }
+
+ /**
+ * Number of mutations to perform before a commit is executed.
+ */
+ public Builder batchSize(final long batchSize) {
+ this.batchSize = batchSize;
+ return this;
+ }
+
+ public GraphMLReader create() {
+ return new GraphMLReader(vertexIdKey, edgeIdKey, edgeLabelKey, vertexLabelKey, batchSize);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphml/GraphMLTokens.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphml/GraphMLTokens.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphml/GraphMLTokens.java
new file mode 100644
index 0000000..bb1298d
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphml/GraphMLTokens.java
@@ -0,0 +1,56 @@
+/*
+ * 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;
+
+/**
+ * A collection of tokens used for GraphML related data.
+ *
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+class GraphMLTokens {
+ public static final String XML_SCHEMA_NAMESPACE_TAG = "xsi";
+ public static final String DEFAULT_GRAPHML_SCHEMA_LOCATION = "http://graphml.graphdrawing.org/xmlns/1.1/graphml.xsd";
+ public static final String XML_SCHEMA_LOCATION_ATTRIBUTE = "schemaLocation";
+ public static final String GRAPHML = "graphml";
+ public static final String XMLNS = "xmlns";
+ public static final String GRAPHML_XMLNS = "http://graphml.graphdrawing.org/xmlns";
+ public static final String G = "G";
+ public static final String EDGEDEFAULT = "edgedefault";
+ public static final String DIRECTED = "directed";
+ public static final String KEY = "key";
+ public static final String FOR = "for";
+ public static final String ID = "id";
+ public static final String ATTR_NAME = "attr.name";
+ public static final String ATTR_TYPE = "attr.type";
+ public static final String GRAPH = "graph";
+ public static final String NODE = "node";
+ public static final String EDGE = "edge";
+ public static final String SOURCE = "source";
+ public static final String TARGET = "target";
+ public static final String DATA = "data";
+ public static final String LABEL_E = "labelE";
+ public static final String LABEL_V = "labelV";
+ public static final String STRING = "string";
+ public static final String FLOAT = "float";
+ public static final String DOUBLE = "double";
+ public static final String LONG = "long";
+ public static final String BOOLEAN = "boolean";
+ public static final String INT = "int";
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphml/GraphMLWriter.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphml/GraphMLWriter.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphml/GraphMLWriter.java
new file mode 100644
index 0000000..1cdbca4
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphml/GraphMLWriter.java
@@ -0,0 +1,445 @@
+/*
+ * 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 com.tinkerpop.gremlin.process.Traversal;
+import com.tinkerpop.gremlin.structure.*;
+import com.tinkerpop.gremlin.structure.io.GraphWriter;
+import com.tinkerpop.gremlin.structure.util.Comparators;
+import com.tinkerpop.gremlin.util.iterator.IteratorUtils;
+
+import javax.xml.XMLConstants;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.*;
+
+/**
+ * GraphMLWriter writes a Graph to a GraphML OutputStream. Note that this format is lossy, in the sense that data
+ * types and features of Gremlin Structure not supported by GraphML are not serialized. This format is meant for
+ * external export of a graph to tools outside of Gremlin Structure graphs. Note that GraphML does not support
+ * the notion of multi-properties or properties on properties and will throw an exception when writing a
+ * graph elements that have such things.
+ *
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ * @author Joshua Shinavier (http://fortytwo.net)
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class GraphMLWriter implements GraphWriter {
+ private final XMLOutputFactory inputFactory = XMLOutputFactory.newInstance();
+ private boolean normalize = false;
+
+ private final Optional<Map<String, String>> vertexKeyTypes;
+ private final Optional<Map<String, String>> edgeKeyTypes;
+ private final Optional<String> xmlSchemaLocation;
+ private final String edgeLabelKey;
+ private final String vertexLabelKey;
+
+ private GraphMLWriter(final boolean normalize, final Map<String, String> vertexKeyTypes,
+ final Map<String, String> edgeKeyTypes, final String xmlSchemaLocation,
+ final String edgeLabelKey, final String vertexLabelKey) {
+ this.normalize = normalize;
+ this.vertexKeyTypes = Optional.ofNullable(vertexKeyTypes);
+ this.edgeKeyTypes = Optional.ofNullable(edgeKeyTypes);
+ this.xmlSchemaLocation = Optional.ofNullable(xmlSchemaLocation);
+ this.edgeLabelKey = edgeLabelKey;
+ this.vertexLabelKey = vertexLabelKey;
+ }
+
+ @Override
+ public void writeVertex(final OutputStream outputStream, final Vertex v, Direction direction) throws IOException {
+ throw new UnsupportedOperationException("GraphML does not allow for a partial structure");
+ }
+
+ @Override
+ public void writeVertex(final OutputStream outputStream, final Vertex v) throws IOException {
+ throw new UnsupportedOperationException("GraphML does not allow for a partial structure");
+ }
+
+ @Override
+ public void writeEdge(final OutputStream outputStream, final Edge e) throws IOException {
+ throw new UnsupportedOperationException("GraphML does not allow for a partial structure");
+ }
+
+ @Override
+ public void writeVertices(final OutputStream outputStream, Traversal<?, Vertex> traversal, final Direction direction) throws IOException {
+ throw new UnsupportedOperationException("GraphML does not allow for a partial structure");
+ }
+
+ @Override
+ public void writeVertices(final OutputStream outputStream, final Traversal<?, Vertex> traversal) throws IOException {
+ throw new UnsupportedOperationException("GraphML does not allow for a partial structure");
+ }
+
+ /**
+ * Write the data in a Graph to a GraphML OutputStream.
+ *
+ * @param outputStream the GraphML OutputStream to write the Graph data to
+ * @throws java.io.IOException thrown if there is an error generating the GraphML data
+ */
+ @Override
+ public void writeGraph(final OutputStream outputStream, final Graph g) throws IOException {
+ final Map<String, String> identifiedVertexKeyTypes = this.vertexKeyTypes.orElseGet(() -> GraphMLWriter.determineVertexTypes(g));
+ final Map<String, String> identifiedEdgeKeyTypes = this.edgeKeyTypes.orElseGet(() -> GraphMLWriter.determineEdgeTypes(g));
+
+ if (identifiedEdgeKeyTypes.containsKey(this.edgeLabelKey))
+ throw new IllegalStateException(String.format("The edgeLabelKey value of[%s] conflicts with the name of an existing property key to be included in the GraphML", this.edgeLabelKey));
+ if (identifiedEdgeKeyTypes.containsKey(this.edgeLabelKey))
+ throw new IllegalStateException(String.format("The vertexLabelKey value of[%s] conflicts with the name of an existing property key to be included in the GraphML", this.vertexLabelKey));
+
+ identifiedEdgeKeyTypes.put(this.edgeLabelKey, GraphMLTokens.STRING);
+ identifiedVertexKeyTypes.put(this.vertexLabelKey, GraphMLTokens.STRING);
+
+ try {
+ final XMLStreamWriter writer;
+ writer = configureWriter(outputStream);
+
+ writer.writeStartDocument();
+ writer.writeStartElement(GraphMLTokens.GRAPHML);
+ writeXmlNsAndSchema(writer);
+
+ writeTypes(identifiedVertexKeyTypes, identifiedEdgeKeyTypes, writer);
+
+ writer.writeStartElement(GraphMLTokens.GRAPH);
+ writer.writeAttribute(GraphMLTokens.ID, GraphMLTokens.G);
+ writer.writeAttribute(GraphMLTokens.EDGEDEFAULT, GraphMLTokens.DIRECTED);
+
+ writeVertices(writer, g);
+ writeEdges(writer, g);
+
+ writer.writeEndElement(); // graph
+ writer.writeEndElement(); // graphml
+ writer.writeEndDocument();
+
+ writer.flush();
+ writer.close();
+ } catch (XMLStreamException xse) {
+ throw new IOException(xse);
+ }
+ }
+
+ private XMLStreamWriter configureWriter(final OutputStream outputStream) throws XMLStreamException {
+ final XMLStreamWriter utf8Writer = inputFactory.createXMLStreamWriter(outputStream, "UTF8");
+ if (normalize) {
+ final XMLStreamWriter writer = new GraphMLWriterHelper.IndentingXMLStreamWriter(utf8Writer);
+ ((GraphMLWriterHelper.IndentingXMLStreamWriter) writer).setIndentStep(" ");
+ return writer;
+ } else
+ return utf8Writer;
+ }
+
+ private void writeTypes(final Map<String, String> identifiedVertexKeyTypes,
+ final Map<String, String> identifiedEdgeKeyTypes,
+ final XMLStreamWriter writer) throws XMLStreamException {
+ // <key id="weight" for="edge" attr.name="weight" attr.type="float"/>
+ final Collection<String> vertexKeySet = getVertexKeysAndNormalizeIfRequired(identifiedVertexKeyTypes);
+ for (String key : vertexKeySet) {
+ writer.writeStartElement(GraphMLTokens.KEY);
+ writer.writeAttribute(GraphMLTokens.ID, key);
+ writer.writeAttribute(GraphMLTokens.FOR, GraphMLTokens.NODE);
+ writer.writeAttribute(GraphMLTokens.ATTR_NAME, key);
+ writer.writeAttribute(GraphMLTokens.ATTR_TYPE, identifiedVertexKeyTypes.get(key));
+ writer.writeEndElement();
+ }
+
+ final Collection<String> edgeKeySet = getEdgeKeysAndNormalizeIfRequired(identifiedEdgeKeyTypes);
+ for (String key : edgeKeySet) {
+ writer.writeStartElement(GraphMLTokens.KEY);
+ writer.writeAttribute(GraphMLTokens.ID, key);
+ writer.writeAttribute(GraphMLTokens.FOR, GraphMLTokens.EDGE);
+ writer.writeAttribute(GraphMLTokens.ATTR_NAME, key);
+ writer.writeAttribute(GraphMLTokens.ATTR_TYPE, identifiedEdgeKeyTypes.get(key));
+ writer.writeEndElement();
+ }
+ }
+
+ private void writeEdges(final XMLStreamWriter writer, final Graph graph) throws XMLStreamException {
+ if (normalize) {
+ final List<Edge> edges = IteratorUtils.list(graph.iterators().edgeIterator());
+ Collections.sort(edges, Comparators.ELEMENT_COMPARATOR);
+
+ for (Edge edge : edges) {
+ writer.writeStartElement(GraphMLTokens.EDGE);
+ writer.writeAttribute(GraphMLTokens.ID, edge.id().toString());
+ writer.writeAttribute(GraphMLTokens.SOURCE, edge.outV().next().id().toString());
+ writer.writeAttribute(GraphMLTokens.TARGET, edge.inV().next().id().toString());
+
+ writer.writeStartElement(GraphMLTokens.DATA);
+ writer.writeAttribute(GraphMLTokens.KEY, this.edgeLabelKey);
+ writer.writeCharacters(edge.label());
+ writer.writeEndElement();
+
+ final List<String> keys = new ArrayList<>(edge.keys());
+ Collections.sort(keys);
+
+ for (String key : keys) {
+ writer.writeStartElement(GraphMLTokens.DATA);
+ writer.writeAttribute(GraphMLTokens.KEY, key);
+ // technically there can't be a null here as gremlin structure forbids that occurrence even if Graph
+ // implementations support it, but out to empty string just in case.
+ writer.writeCharacters(edge.property(key).orElse("").toString());
+ writer.writeEndElement();
+ }
+ writer.writeEndElement();
+ }
+ } else {
+ final Iterator<Edge> iterator = graph.iterators().edgeIterator();
+ while (iterator.hasNext()) {
+ final Edge edge = iterator.next();
+ writer.writeStartElement(GraphMLTokens.EDGE);
+ writer.writeAttribute(GraphMLTokens.ID, edge.id().toString());
+ writer.writeAttribute(GraphMLTokens.SOURCE, edge.outV().next().id().toString());
+ writer.writeAttribute(GraphMLTokens.TARGET, edge.inV().next().id().toString());
+
+ writer.writeStartElement(GraphMLTokens.DATA);
+ writer.writeAttribute(GraphMLTokens.KEY, this.edgeLabelKey);
+ writer.writeCharacters(edge.label());
+ writer.writeEndElement();
+
+ for (String key : edge.keys()) {
+ writer.writeStartElement(GraphMLTokens.DATA);
+ writer.writeAttribute(GraphMLTokens.KEY, key);
+ // technically there can't be a null here as gremlin structure forbids that occurrence even if Graph
+ // implementations support it, but out to empty string just in case.
+ writer.writeCharacters(edge.property(key).orElse("").toString());
+ writer.writeEndElement();
+ }
+ writer.writeEndElement();
+ }
+ }
+ }
+
+ private void writeVertices(final XMLStreamWriter writer, final Graph graph) throws XMLStreamException {
+ final Iterable<Vertex> vertices = getVerticesAndNormalizeIfRequired(graph);
+ for (Vertex vertex : vertices) {
+ writer.writeStartElement(GraphMLTokens.NODE);
+ writer.writeAttribute(GraphMLTokens.ID, vertex.id().toString());
+ final Collection<String> keys = getElementKeysAndNormalizeIfRequired(vertex);
+
+ writer.writeStartElement(GraphMLTokens.DATA);
+ writer.writeAttribute(GraphMLTokens.KEY, this.vertexLabelKey);
+ writer.writeCharacters(vertex.label());
+ writer.writeEndElement();
+
+ for (String key : keys) {
+ writer.writeStartElement(GraphMLTokens.DATA);
+ writer.writeAttribute(GraphMLTokens.KEY, key);
+ // technically there can't be a null here as gremlin structure forbids that occurrence even if Graph
+ // implementations support it, but out to empty string just in case.
+ writer.writeCharacters(vertex.property(key).orElse("").toString());
+ writer.writeEndElement();
+ }
+ writer.writeEndElement();
+ }
+ }
+
+ private Collection<String> getElementKeysAndNormalizeIfRequired(final Element element) {
+ final Collection<String> keys;
+ if (normalize) {
+ keys = new ArrayList<>();
+ keys.addAll(element.keys());
+ Collections.sort((List<String>) keys);
+ } else
+ keys = element.keys();
+
+ return keys;
+ }
+
+ private Iterable<Vertex> getVerticesAndNormalizeIfRequired(final Graph graph) {
+ final Iterable<Vertex> vertices;
+ if (normalize) {
+ vertices = new ArrayList<>();
+ for (Vertex v : graph.V().toList()) {
+ ((Collection<Vertex>) vertices).add(v);
+ }
+ Collections.sort((List<Vertex>) vertices, Comparators.ELEMENT_COMPARATOR);
+ } else
+ vertices = IteratorUtils.list(graph.iterators().vertexIterator());
+
+ return vertices;
+ }
+
+ private Collection<String> getEdgeKeysAndNormalizeIfRequired(final Map<String, String> identifiedEdgeKeyTypes) {
+ final Collection<String> edgeKeySet;
+ if (normalize) {
+ edgeKeySet = new ArrayList<>();
+ edgeKeySet.addAll(identifiedEdgeKeyTypes.keySet());
+ Collections.sort((List<String>) edgeKeySet);
+ } else
+ edgeKeySet = identifiedEdgeKeyTypes.keySet();
+
+ return edgeKeySet;
+ }
+
+ private Collection<String> getVertexKeysAndNormalizeIfRequired(final Map<String, String> identifiedVertexKeyTypes) {
+ final Collection<String> keyset;
+ if (normalize) {
+ keyset = new ArrayList<>();
+ keyset.addAll(identifiedVertexKeyTypes.keySet());
+ Collections.sort((List<String>) keyset);
+ } else
+ keyset = identifiedVertexKeyTypes.keySet();
+
+ return keyset;
+ }
+
+ private void writeXmlNsAndSchema(final XMLStreamWriter writer) throws XMLStreamException {
+ writer.writeAttribute(GraphMLTokens.XMLNS, GraphMLTokens.GRAPHML_XMLNS);
+
+ //XML Schema instance namespace definition (xsi)
+ writer.writeAttribute(XMLConstants.XMLNS_ATTRIBUTE + ':' + GraphMLTokens.XML_SCHEMA_NAMESPACE_TAG,
+ XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI);
+ //XML Schema location
+ writer.writeAttribute(GraphMLTokens.XML_SCHEMA_NAMESPACE_TAG + ':' + GraphMLTokens.XML_SCHEMA_LOCATION_ATTRIBUTE,
+ GraphMLTokens.GRAPHML_XMLNS + ' ' + this.xmlSchemaLocation.orElse(GraphMLTokens.DEFAULT_GRAPHML_SCHEMA_LOCATION));
+ }
+
+ private static Map<String, String> determineVertexTypes(final Graph graph) {
+ final Map<String, String> vertexKeyTypes = new HashMap<>();
+ for (Vertex vertex : graph.V().toList()) {
+ for (String key : vertex.keys()) {
+ if (!vertexKeyTypes.containsKey(key)) {
+ vertexKeyTypes.put(key, GraphMLWriter.getStringType(vertex.property(key).value()));
+ }
+ }
+ }
+
+ return vertexKeyTypes;
+ }
+
+ private static Map<String, String> determineEdgeTypes(final Graph graph) {
+ final Map<String, String> edgeKeyTypes = new HashMap<>();
+ for (Edge edge : graph.E().toList()) {
+ for (String key : edge.keys()) {
+ if (!edgeKeyTypes.containsKey(key))
+ edgeKeyTypes.put(key, GraphMLWriter.getStringType(edge.property(key).value()));
+ }
+ }
+
+ return edgeKeyTypes;
+ }
+
+ private static String getStringType(final Object object) {
+ if (object instanceof String)
+ return GraphMLTokens.STRING;
+ else if (object instanceof Integer)
+ return GraphMLTokens.INT;
+ else if (object instanceof Long)
+ return GraphMLTokens.LONG;
+ else if (object instanceof Float)
+ return GraphMLTokens.FLOAT;
+ else if (object instanceof Double)
+ return GraphMLTokens.DOUBLE;
+ else if (object instanceof Boolean)
+ return GraphMLTokens.BOOLEAN;
+ else
+ return GraphMLTokens.STRING;
+ }
+
+ public static Builder build() {
+ return new Builder();
+ }
+
+ public static final class Builder {
+ private boolean normalize = false;
+ private Map<String, String> vertexKeyTypes = null;
+ private Map<String, String> edgeKeyTypes = null;
+
+ private String xmlSchemaLocation = null;
+ private String edgeLabelKey = GraphMLTokens.LABEL_E;
+ private String vertexLabelKey = GraphMLTokens.LABEL_V;
+
+ private Builder() {
+ }
+
+ /**
+ * Normalized output is deterministic with respect to the order of elements and properties in the resulting
+ * XML document, and is compatible with line diff-based tools such as Git. Note: normalized output is
+ * sideEffects-intensive and is not appropriate for very large graphs.
+ *
+ * @param normalize whether to normalize the output.
+ */
+ public Builder normalize(final boolean normalize) {
+ this.normalize = normalize;
+ return this;
+ }
+
+ /**
+ * Map of the data types of the vertex keys. It is best to specify this Map if possible as the only
+ * other way to attain it is to iterate all vertices to find all the property keys.
+ */
+ public Builder vertexKeyTypes(final Map<String, String> vertexKeyTypes) {
+ this.vertexKeyTypes = vertexKeyTypes;
+ return this;
+ }
+
+ /**
+ * Map of the data types of the edge keys. It is best to specify this Map if possible as the only
+ * other way to attain it is to iterate all vertices to find all the property keys.
+ */
+ public Builder edgeKeyTypes(final Map<String, String> edgeKeyTypes) {
+ this.edgeKeyTypes = edgeKeyTypes;
+ return this;
+ }
+
+ /**
+ * Location of the GraphML schema which is defaulted to
+ * {@link com.tinkerpop.gremlin.structure.io.graphml.GraphMLTokens#DEFAULT_GRAPHML_SCHEMA_LOCATION}.
+ */
+ public Builder xmlSchemaLocation(final String xmlSchemaLocation) {
+ this.xmlSchemaLocation = xmlSchemaLocation;
+ return this;
+ }
+
+ /**
+ * Set the name of the edge label in the GraphML. This value is defaulted to {@link GraphMLTokens#LABEL_E}.
+ * The value of {@link com.tinkerpop.gremlin.structure.Edge#label()} is written as a data element on the edge
+ * and the appropriate key element is added to define it in the GraphML. It is important that when reading
+ * this GraphML back in with the reader that this label key is set appropriately to properly read the edge
+ * labels.
+ *
+ * @param edgeLabelKey if the label of an edge will be handled by the data property.
+ */
+ public Builder edgeLabelKey(final String edgeLabelKey) {
+ this.edgeLabelKey = edgeLabelKey;
+ return this;
+ }
+
+ /**
+ * Set the name of the vertex label in the GraphML. This value is defaulted to {@link GraphMLTokens#LABEL_V}.
+ * The value of {@link com.tinkerpop.gremlin.structure.Vertex#label()} is written as a data element on the
+ * vertex and the appropriate key element is added to define it in the GraphML. It is important that when
+ * reading this GraphML back in with the reader that this label key is set appropriately to properly read the
+ * vertex labels.
+ *
+ * @param vertexLabelKey if the label of an vertex will be handled by the data property.
+ */
+ public Builder vertexLabelKey(final String vertexLabelKey) {
+ this.vertexLabelKey = vertexLabelKey;
+ return this;
+ }
+
+ public GraphMLWriter create() {
+ return new GraphMLWriter(normalize, vertexKeyTypes, edgeKeyTypes, xmlSchemaLocation, edgeLabelKey, vertexLabelKey);
+ }
+ }
+}
+