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 2018/07/19 17:41:53 UTC

[34/50] tinkerpop git commit: TINKERPOP-1996 Introduce read() and write() steps

TINKERPOP-1996 Introduce read() and write() steps


Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo
Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/ec1d05f7
Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/ec1d05f7
Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/ec1d05f7

Branch: refs/heads/TINKERPOP-1996
Commit: ec1d05f74fa875e4a07699dc89c3d1956aab586f
Parents: ce73ceb
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Fri Jun 29 15:04:17 2018 -0400
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Thu Jul 19 13:39:29 2018 -0400

----------------------------------------------------------------------
 .../traversal/dsl/graph/GraphTraversal.java     |   2 +
 .../dsl/graph/GraphTraversalSource.java         |  17 +++
 .../process/traversal/step/map/ReadStep.java    | 112 +++++++++++++++++++
 .../process/traversal/step/map/WriteStep.java   | 111 ++++++++++++++++++
 .../strategy/verification/IoUsageStrategy.java  |  60 ++++++++++
 .../verification/IoUsageStrategyTest.java       |  93 +++++++++++++++
 .../glv/GraphTraversalSource.template           |   4 +-
 gremlin-dotnet/glv/generate.groovy              |   5 +-
 .../Process/Traversal/GraphTraversalSource.cs   |  22 ++++
 .../lib/process/graph-traversal.js              |  20 ++++
 .../gremlin_python/process/graph_traversal.py   |  10 ++
 11 files changed, 452 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ec1d05f7/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java
index 63aa65d..210367f 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java
@@ -2817,6 +2817,8 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
         public static final String skip = "skip";
         public static final String tail = "tail";
         public static final String coin = "coin";
+        public static final String read = "read";
+        public static final String write = "write";
 
         public static final String timeLimit = "timeLimit";
         public static final String simplePath = "simplePath";

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ec1d05f7/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.java
index bc3ef9e..9b82108 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversalSource.java
@@ -31,6 +31,8 @@ import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddEdgeStartStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddVertexStartStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.map.GraphStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.ReadStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.WriteStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.InjectStep;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.RequirementsStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
@@ -40,6 +42,7 @@ import org.apache.tinkerpop.gremlin.structure.Transaction;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
 import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
 
+import java.util.Map;
 import java.util.Optional;
 import java.util.function.BinaryOperator;
 import java.util.function.Supplier;
@@ -390,6 +393,20 @@ public class GraphTraversalSource implements TraversalSource {
         return traversal.addStep(new GraphStep<>(traversal, Edge.class, true, edgesIds));
     }
 
+    public GraphTraversal<Map<String,Object>, Map<String,Object>> read(final String localFile) {
+        final GraphTraversalSource clone = this.clone();
+        clone.bytecode.addStep(GraphTraversal.Symbols.read, localFile);
+        final GraphTraversal.Admin<Map<String,Object>, Map<String,Object>> traversal = new DefaultGraphTraversal<>(clone);
+        return traversal.addStep(new ReadStep(traversal, localFile));
+    }
+
+    public GraphTraversal<Map<String,Object>, Map<String,Object>> write(final String localFile) {
+        final GraphTraversalSource clone = this.clone();
+        clone.bytecode.addStep(GraphTraversal.Symbols.write, localFile);
+        final GraphTraversal.Admin<Map<String,Object>, Map<String,Object>> traversal = new DefaultGraphTraversal<>(clone);
+        return traversal.addStep(new WriteStep(traversal, localFile));
+    }
+
     /**
      * Proxies calls through to the underlying {@link Graph#tx()}.
      */

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ec1d05f7/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ReadStep.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ReadStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ReadStep.java
new file mode 100644
index 0000000..afc3e53
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ReadStep.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.step.map;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+import org.apache.tinkerpop.gremlin.process.traversal.TraverserGenerator;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Configuring;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.AbstractStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters;
+import org.apache.tinkerpop.gremlin.process.traversal.util.FastNoSuchElementException;
+import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.io.gryo.GryoReader;
+import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
+import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * Reads data from a file into a {@link Graph}. This step is meant to be used as the first and last step in a
+ * traversal.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class ReadStep extends AbstractStep<Map<String,Object>, Map<String,Object>> implements Configuring {
+
+    private Parameters parameters = new Parameters();
+    private boolean first = true;
+    private String localFile;
+
+    public ReadStep(final Traversal.Admin traversal, final String localFile) {
+        super(traversal);
+
+        if (null == localFile || localFile.isEmpty())
+            throw new IllegalArgumentException("localFile cannot be null or empty");
+        if (!new File(localFile).exists()) throw new IllegalStateException(localFile + " does not exist");
+
+        this.localFile = localFile;
+    }
+
+    @Override
+    public Parameters getParameters() {
+        return this.parameters;
+    }
+
+    @Override
+    public void configure(final Object... keyValues) {
+        this.parameters.set(null, keyValues);
+    }
+
+    @Override
+    protected Traverser.Admin<Map<String,Object>> processNextStart() {
+        if (!this.first) throw FastNoSuchElementException.instance();
+
+        this.first = false;
+        final TraverserGenerator generator = this.getTraversal().getTraverserGenerator();
+        final File file = new File(localFile);
+
+        try (final InputStream stream = new FileInputStream(file)) {
+            final Graph graph = (Graph) this.traversal.getGraph().get();
+            GryoReader.build().create().readGraph(stream, graph);
+
+            final Map<String,Object> stats = new LinkedHashMap<>();
+            stats.put("vertices", IteratorUtils.count(graph.vertices()));
+            stats.put("edges", IteratorUtils.count(graph.edges()));
+
+            return generator.generate(stats, this, 1L);
+        } catch (IOException ioe) {
+            throw new IllegalStateException(String.format("Could not read file %s into graph", localFile), ioe);
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        final int hash = super.hashCode() ^ this.parameters.hashCode();
+        return (null != this.localFile) ? (hash ^ localFile.hashCode()) : hash;
+    }
+
+    @Override
+    public String toString() {
+        return StringFactory.stepString(this, localFile, this.parameters);
+    }
+
+    @Override
+    public ReadStep clone() {
+        final ReadStep clone = (ReadStep) super.clone();
+        clone.parameters = this.parameters.clone();
+        clone.localFile = this.localFile;
+        return clone;
+    }
+}

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ec1d05f7/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/WriteStep.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/WriteStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/WriteStep.java
new file mode 100644
index 0000000..e9346cf
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/WriteStep.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.step.map;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+import org.apache.tinkerpop.gremlin.process.traversal.TraverserGenerator;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Configuring;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.AbstractStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.Parameters;
+import org.apache.tinkerpop.gremlin.process.traversal.util.FastNoSuchElementException;
+import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.io.gryo.GryoWriter;
+import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
+import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * Writes data to a file from a {@link Graph}. This step is meant to be used as the first and last step in a
+ * traversal.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class WriteStep extends AbstractStep<Map<String,Object>, Map<String,Object>> implements Configuring {
+
+    private Parameters parameters = new Parameters();
+    private boolean first = true;
+    private String localFile;
+
+    public WriteStep(final Traversal.Admin traversal, final String localFile) {
+        super(traversal);
+
+        if (null == localFile || localFile.isEmpty())
+            throw new IllegalArgumentException("localFile cannot be null or empty");
+
+        this.localFile = localFile;
+    }
+
+    @Override
+    public Parameters getParameters() {
+        return this.parameters;
+    }
+
+    @Override
+    public void configure(final Object... keyValues) {
+        this.parameters.set(null, keyValues);
+    }
+
+    @Override
+    protected Traverser.Admin<Map<String,Object>> processNextStart() {
+        if (!this.first) throw FastNoSuchElementException.instance();
+
+        this.first = false;
+        final TraverserGenerator generator = this.getTraversal().getTraverserGenerator();
+
+        final File file = new File(localFile);
+        try (final OutputStream stream = new FileOutputStream(file)) {
+            final Graph graph = (Graph) this.traversal.getGraph().get();
+            GryoWriter.build().create().writeGraph(stream, graph);
+
+            final Map<String, Object> stats = new LinkedHashMap<>();
+            stats.put("vertices", IteratorUtils.count(graph.vertices()));
+            stats.put("edges", IteratorUtils.count(graph.edges()));
+
+            return generator.generate(stats, this, 1L);
+        } catch (IOException ioe) {
+            throw new IllegalStateException(String.format("Could not write file %s from graph", localFile), ioe);
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        final int hash = super.hashCode() ^ this.parameters.hashCode();
+        return (null != this.localFile) ? (hash ^ localFile.hashCode()) : hash;
+    }
+
+    @Override
+    public String toString() {
+        return StringFactory.stepString(this, localFile, this.parameters);
+    }
+
+    @Override
+    public WriteStep clone() {
+        final WriteStep clone = (WriteStep) super.clone();
+        clone.parameters = this.parameters.clone();
+        clone.localFile = this.localFile;
+        return clone;
+    }
+}

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ec1d05f7/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/IoUsageStrategy.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/IoUsageStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/IoUsageStrategy.java
new file mode 100644
index 0000000..95761ff
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/IoUsageStrategy.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.strategy.verification;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.ReadStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.WriteStep;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy;
+
+/**
+ * {@code IoUsageStrategy} prevents the {@link GraphTraversalSource#read(String)} and
+ * {@link GraphTraversalSource#write(String)} steps from being used outside of their intended scope, which is as the
+ * first and last step in a traversal. Therefore, it can only be used as {@code g.read('file.gryo')} and
+ * {@code g.write('file.gryo')}. As both of these steps take additional configuration, the use of the
+ * {@link GraphTraversal#with(String, Object)} is acceptable.
+ * <p/>
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ * @example <pre>
+ * g.read('file.kryo').V()            // throws VerificationException
+ * g.write('file.kryo').V()           // throws VerificationException
+ * </pre>
+ */
+public final class IoUsageStrategy extends AbstractTraversalStrategy<TraversalStrategy.VerificationStrategy> implements TraversalStrategy.VerificationStrategy {
+
+    private static final IoUsageStrategy INSTANCE = new IoUsageStrategy();
+
+    private IoUsageStrategy() {
+    }
+
+    @Override
+    public void apply(final Traversal.Admin<?, ?> traversal) {
+        if ((traversal.getStartStep() instanceof ReadStep || traversal.getStartStep() instanceof WriteStep) && traversal.getSteps().size() > 1) {
+            throw new VerificationException("The read() or write() steps must be the first and only step in the traversal - they cannot be used with other steps", traversal);
+        }
+    }
+
+    public static IoUsageStrategy instance() {
+        return INSTANCE;
+    }
+}

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ec1d05f7/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/IoUsageStrategyTest.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/IoUsageStrategyTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/IoUsageStrategyTest.java
new file mode 100644
index 0000000..907e2b7
--- /dev/null
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/IoUsageStrategyTest.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 org.apache.tinkerpop.gremlin.process.traversal.strategy.verification;
+
+import org.apache.tinkerpop.gremlin.TestHelper;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
+import org.apache.tinkerpop.gremlin.process.traversal.util.DefaultTraversalStrategies;
+import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+@RunWith(Parameterized.class)
+public class IoUsageStrategyTest {
+
+    private static final GraphTraversalSource g = EmptyGraph.instance().traversal();
+
+    private static File junkFile;
+
+    static {
+        try {
+            junkFile = TestHelper.generateTempFile(IoUsageStrategyTest.class, "fake", "kryo");
+        } catch (IOException ioe) {
+            throw new RuntimeException(ioe);
+        }
+    }
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Iterable<Object[]> data() {
+        return Arrays.asList(new Object[][]{
+                {"g.read('a.kryo')", g.read(junkFile.getAbsolutePath()), true},
+                {"g.write('a.kryo')", g.write(junkFile.getAbsolutePath()), true},
+                {"g.read('a.kryo').with(\"x\", \"y\")", g.read(junkFile.getAbsolutePath()).with("x", "y"), true},
+                {"g.write('a.kryo').with(\"x\", \"y\")", g.write(junkFile.getAbsolutePath()).with("x", "y"), true},
+                {"g.read('a.kryo').V()", g.read(junkFile.getAbsolutePath()).V(), false},
+                {"g.write('a.kryo').V()", g.write(junkFile.getAbsolutePath()).V(), false}
+        });
+    }
+
+    @Parameterized.Parameter(value = 0)
+    public String name;
+
+    @Parameterized.Parameter(value = 1)
+    public Traversal traversal;
+
+    @Parameterized.Parameter(value = 2)
+    public boolean allow;
+
+    @Test
+    public void shouldBeVerified() {
+        final TraversalStrategies strategies = new DefaultTraversalStrategies();
+        strategies.addStrategies(IoUsageStrategy.instance());
+        traversal.asAdmin().setStrategies(strategies);
+        if (allow) {
+            traversal.asAdmin().applyStrategies();
+        } else {
+            try {
+                traversal.asAdmin().applyStrategies();
+                fail("The strategy should not allow read()/write() to be used with other steps: " + this.traversal);
+            } catch (VerificationException ise) {
+                assertTrue(ise.getMessage().contains("read() or write() steps"));
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ec1d05f7/gremlin-dotnet/glv/GraphTraversalSource.template
----------------------------------------------------------------------
diff --git a/gremlin-dotnet/glv/GraphTraversalSource.template b/gremlin-dotnet/glv/GraphTraversalSource.template
index afa4297..23d66e5 100644
--- a/gremlin-dotnet/glv/GraphTraversalSource.template
+++ b/gremlin-dotnet/glv/GraphTraversalSource.template
@@ -127,9 +127,9 @@ namespace Gremlin.Net.Process.Traversal
         ///     Spawns a <see cref="GraphTraversal{SType, EType}" /> off this graph traversal source and adds the <%= method.methodName %> step to that
         ///     traversal.
         /// </summary>
-        public GraphTraversal<$method.typeNameString> <%= toCSharpMethodName.call(method.methodName) %><%= method.tParam %>(<%= method.parameters %>)
+        public GraphTraversal<$method.t1, $method.t2> <%= toCSharpMethodName.call(method.methodName) %><%= method.tParam %>(<%= method.parameters %>)
         {
-            var traversal = new GraphTraversal<$method.typeNameString>(TraversalStrategies, new Bytecode(Bytecode));
+            var traversal = new GraphTraversal<$method.t1, $method.t2>(TraversalStrategies, new Bytecode(Bytecode));
             <%  if (method.parameters.contains("params ")) {
           %>var args = new List<$method.argsListType>(<%= method.paramNames.init().size() %> + <%= method.paramNames.last() %>.Length) {<%= method.paramNames.init().join(", ") %>};
             args.AddRange(<%= method.paramNames.last() %>);

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ec1d05f7/gremlin-dotnet/glv/generate.groovy
----------------------------------------------------------------------
diff --git a/gremlin-dotnet/glv/generate.groovy b/gremlin-dotnet/glv/generate.groovy
index e542471..bd2d9d7 100644
--- a/gremlin-dotnet/glv/generate.groovy
+++ b/gremlin-dotnet/glv/generate.groovy
@@ -38,6 +38,7 @@ def toCSharpTypeMap = ["Long": "long",
                        "Object[]": "object[]",
                        "Class": "Type",
                        "Class[]": "Type[]",
+                       "java.util.Map<java.lang.String, java.lang.Object>": "IDictionary<string, object>",
                        "java.util.Map<java.lang.String, E2>": "IDictionary<string, E2>",
                        "java.util.Map<java.lang.String, B>": "IDictionary<string, E2>",
                        "java.util.Map<java.lang.Object, E2>": "IDictionary<object, E2>",
@@ -251,13 +252,13 @@ def binding = ["pmethods": P.class.getMethods().
                         unique { a,b -> a.name <=> b.name ?: getCSharpParamTypeString(a) <=> getCSharpParamTypeString(b) }.
                         collect { javaMethod ->
                             def typeNames = getJavaGenericTypeParameterTypeNames(javaMethod)
-                            def typeNameString = typeNames.join(", ")
+                            def t1 = toCSharpType(typeNames[0])
                             def t2 = toCSharpType(typeNames[1])
                             def tParam = getCSharpGenericTypeParam(t2)
                             def parameters = getCSharpParamString(javaMethod, true)
                             def paramNames = getParamNames(javaMethod.parameters)
                             def argsListType = getArgsListType(parameters)
-                            return ["methodName": javaMethod.name, "typeNameString": typeNameString, "tParam":tParam, "parameters":parameters, "paramNames":paramNames, "argsListType":argsListType]
+                            return ["methodName": javaMethod.name, "t1":t1, "t2":t2, "tParam":tParam, "parameters":parameters, "paramNames":paramNames, "argsListType":argsListType]
                         },
                "graphStepMethods": GraphTraversal.getMethods().
                         findAll { GraphTraversal.class.equals(it.returnType) }.

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ec1d05f7/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversalSource.cs
----------------------------------------------------------------------
diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversalSource.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversalSource.cs
index 2816c05..630ac28 100644
--- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversalSource.cs
+++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversalSource.cs
@@ -333,6 +333,28 @@ namespace Gremlin.Net.Process.Traversal
             return traversal;
         }
 
+        /// <summary>
+        ///     Spawns a <see cref="GraphTraversal{SType, EType}" /> off this graph traversal source and adds the read step to that
+        ///     traversal.
+        /// </summary>
+        public GraphTraversal<IDictionary<string, object>, IDictionary<string, object>> Read(string localFile)
+        {
+            var traversal = new GraphTraversal<IDictionary<string, object>, IDictionary<string, object>>(TraversalStrategies, new Bytecode(Bytecode));
+                traversal.Bytecode.AddStep("read", localFile);
+            return traversal;
+        }
+
+        /// <summary>
+        ///     Spawns a <see cref="GraphTraversal{SType, EType}" /> off this graph traversal source and adds the write step to that
+        ///     traversal.
+        /// </summary>
+        public GraphTraversal<IDictionary<string, object>, IDictionary<string, object>> Write(string localFile)
+        {
+            var traversal = new GraphTraversal<IDictionary<string, object>, IDictionary<string, object>>(TraversalStrategies, new Bytecode(Bytecode));
+                traversal.Bytecode.AddStep("write", localFile);
+            return traversal;
+        }
+
     }
     
 #pragma warning restore 1591

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ec1d05f7/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/graph-traversal.js
----------------------------------------------------------------------
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/graph-traversal.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/graph-traversal.js
index f143542..8fa51de 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/graph-traversal.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/graph-traversal.js
@@ -172,6 +172,26 @@ class GraphTraversalSource {
     return new GraphTraversal(this.graph, new TraversalStrategies(this.traversalStrategies), b);
   }
   
+  /**
+   * read GraphTraversalSource step method.
+   * @param {...Object} args
+   * @returns {GraphTraversal}
+   */
+  read(...args) {
+    const b = new Bytecode(this.bytecode).addStep('read', args);
+    return new GraphTraversal(this.graph, new TraversalStrategies(this.traversalStrategies), b);
+  }
+  
+  /**
+   * write GraphTraversalSource step method.
+   * @param {...Object} args
+   * @returns {GraphTraversal}
+   */
+  write(...args) {
+    const b = new Bytecode(this.bytecode).addStep('write', args);
+    return new GraphTraversal(this.graph, new TraversalStrategies(this.traversalStrategies), b);
+  }
+  
 }
 
 /**

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ec1d05f7/gremlin-python/src/main/jython/gremlin_python/process/graph_traversal.py
----------------------------------------------------------------------
diff --git a/gremlin-python/src/main/jython/gremlin_python/process/graph_traversal.py b/gremlin-python/src/main/jython/gremlin_python/process/graph_traversal.py
index bb81d87..e559613 100644
--- a/gremlin-python/src/main/jython/gremlin_python/process/graph_traversal.py
+++ b/gremlin-python/src/main/jython/gremlin_python/process/graph_traversal.py
@@ -106,6 +106,16 @@ class GraphTraversalSource(object):
         traversal.bytecode.add_step("inject", *args)
         return traversal
 
+    def read(self, *args):
+        traversal = self.get_graph_traversal()
+        traversal.bytecode.add_step("read", *args)
+        return traversal
+
+    def write(self, *args):
+        traversal = self.get_graph_traversal()
+        traversal.bytecode.add_step("write", *args)
+        return traversal
+
 
 class GraphTraversal(Traversal):
     def __init__(self, graph, traversal_strategies, bytecode):