You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by ok...@apache.org on 2015/03/19 19:26:40 UTC

[22/40] incubator-tinkerpop git commit: the traversal steps provided by TinkerPop are the foundation for all dsl. GraphTraversal is just a dsl of traversal. Refactored the process API to reflect this concept. Fixed #592.

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/4c97e964/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/AbstractTraverser.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/AbstractTraverser.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/AbstractTraverser.java
new file mode 100644
index 0000000..6804dbd
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/AbstractTraverser.java
@@ -0,0 +1,187 @@
+/*
+ * 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.traverser.util;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Path;
+import org.apache.tinkerpop.gremlin.process.traversal.Step;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalSideEffects;
+import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+import org.apache.tinkerpop.gremlin.process.traversal.util.EmptyTraversalSideEffects;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.EmptyPath;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedElement;
+import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedFactory;
+import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedProperty;
+
+/**
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ */
+public abstract class AbstractTraverser<T> implements Traverser<T>, Traverser.Admin<T> {
+
+    protected T t;
+
+    protected AbstractTraverser() {
+
+    }
+
+    public AbstractTraverser(final T t) {
+        this.t = t;
+    }
+
+    /////////////
+
+    @Override
+    public void merge(final Admin<?> other) {
+        throw new UnsupportedOperationException("This traverser does not support merging: " + this.getClass().getCanonicalName());
+    }
+
+    @Override
+    public <R> Admin<R> split(final R r, final Step<T, R> step) {
+        try {
+            final AbstractTraverser<R> clone = (AbstractTraverser<R>) super.clone();
+            clone.t = r;
+            return clone;
+        } catch (final CloneNotSupportedException e) {
+            throw new IllegalStateException(e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public Admin<T> split() {
+        try {
+            return (AbstractTraverser<T>) super.clone();
+        } catch (final CloneNotSupportedException e) {
+            throw new IllegalStateException(e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public void set(final T t) {
+        this.t = t;
+    }
+
+    @Override
+    public void incrLoops(final String stepLabel) {
+
+    }
+
+    @Override
+    public void resetLoops() {
+
+    }
+
+    @Override
+    public String getStepId() {
+        throw new UnsupportedOperationException("This traverser does not support futures: " + this.getClass().getCanonicalName());
+    }
+
+    @Override
+    public void setStepId(final String stepId) {
+
+    }
+
+    @Override
+    public void setBulk(final long count) {
+
+    }
+
+    @Override
+    public Admin<T> detach() {
+        this.t = DetachedFactory.detach(this.t, false);
+        return this;
+    }
+
+    @Override
+    public Admin<T> attach(final Vertex hostVertex) {
+        if (this.t instanceof DetachedElement)
+            this.t = (T) ((DetachedElement) this.t).attach(hostVertex);
+        else if (this.t instanceof DetachedProperty)
+            this.t = (T) ((DetachedProperty) this.t).attach(hostVertex);
+        // you do not want to attach a path because it will reference graph objects not at the current vertex
+        return this;
+    }
+
+    @Override
+    public void setSideEffects(final TraversalSideEffects sideEffects) {
+
+    }
+
+    @Override
+    public TraversalSideEffects getSideEffects() {
+        return EmptyTraversalSideEffects.instance();
+        //throw new UnsupportedOperationException("This traverser does not support sideEffects: " + this.getClass().getCanonicalName());
+    }
+
+    @Override
+    public T get() {
+        return this.t;
+    }
+
+    @Override
+    public <S> S sack() {
+        throw new UnsupportedOperationException("This traverser does not support sacks: " + this.getClass().getCanonicalName());
+    }
+
+    @Override
+    public <S> void sack(final S object) {
+
+    }
+
+    @Override
+    public Path path() {
+        return EmptyPath.instance();
+    }
+
+    @Override
+    public int loops() {
+        throw new UnsupportedOperationException("This traverser does not support loops: " + this.getClass().getCanonicalName());
+    }
+
+    @Override
+    public long bulk() {
+        return 1l;
+    }
+
+    @Override
+    @SuppressWarnings("CloneDoesntDeclareCloneNotSupportedException")
+    public AbstractTraverser<T> clone() {
+        try {
+            return (AbstractTraverser<T>) super.clone();
+        } catch (final CloneNotSupportedException e) {
+            throw new IllegalStateException(e.getMessage(), e);
+        }
+    }
+
+    ///////////
+
+    @Override
+    public int hashCode() {
+        return this.t.hashCode();
+    }
+
+    @Override
+    public boolean equals(final Object object) {
+        return object instanceof AbstractTraverser && ((AbstractTraverser) object).get().equals(this.t);
+    }
+
+    @Override
+    public String toString() {
+        return this.t.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/4c97e964/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/DefaultTraverserGeneratorFactory.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/DefaultTraverserGeneratorFactory.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/DefaultTraverserGeneratorFactory.java
new file mode 100644
index 0000000..0f79d99
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/DefaultTraverserGeneratorFactory.java
@@ -0,0 +1,64 @@
+/*
+ * 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.traverser.util;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.TraverserGenerator;
+import org.apache.tinkerpop.gremlin.process.traversal.traverser.B_O_PA_S_SE_SL_TraverserGenerator;
+import org.apache.tinkerpop.gremlin.process.traversal.traverser.B_O_P_PA_S_SE_SL_TraverserGenerator;
+import org.apache.tinkerpop.gremlin.process.traversal.traverser.B_O_TraverserGenerator;
+import org.apache.tinkerpop.gremlin.process.traversal.traverser.O_TraverserGenerator;
+import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserGeneratorFactory;
+import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
+
+import java.util.Set;
+
+/**
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ */
+public class DefaultTraverserGeneratorFactory implements TraverserGeneratorFactory {
+
+    private static DefaultTraverserGeneratorFactory INSTANCE = new DefaultTraverserGeneratorFactory();
+
+    public static DefaultTraverserGeneratorFactory instance() {
+        return INSTANCE;
+    }
+
+    private DefaultTraverserGeneratorFactory() {
+    }
+
+    @Override
+    public TraverserGenerator getTraverserGenerator(final Traversal.Admin<?,?> traversal) {
+        final Set<TraverserRequirement> requirements = traversal.getTraverserRequirements();
+
+        if (O_TraverserGenerator.instance().getProvidedRequirements().containsAll(requirements))
+            return O_TraverserGenerator.instance();
+
+        if (B_O_TraverserGenerator.instance().getProvidedRequirements().containsAll(requirements))
+            return B_O_TraverserGenerator.instance();
+
+        if (B_O_PA_S_SE_SL_TraverserGenerator.instance().getProvidedRequirements().containsAll(requirements))
+            return B_O_PA_S_SE_SL_TraverserGenerator.instance();
+
+        if (B_O_P_PA_S_SE_SL_TraverserGenerator.instance().getProvidedRequirements().containsAll(requirements))
+            return B_O_P_PA_S_SE_SL_TraverserGenerator.instance();
+
+        throw new IllegalStateException("The provided traverser generator factory does not support the requirements of the traversal: " + this.getClass().getCanonicalName() + requirements);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/4c97e964/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/EmptyTraverser.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/EmptyTraverser.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/EmptyTraverser.java
new file mode 100644
index 0000000..498f6b0
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/EmptyTraverser.java
@@ -0,0 +1,153 @@
+/*
+ * 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.traverser.util;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Path;
+import org.apache.tinkerpop.gremlin.process.traversal.Step;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalSideEffects;
+import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.EmptyPath;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+
+/**
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ */
+public final class EmptyTraverser<T> implements Traverser<T>, Traverser.Admin<T> {
+
+    private static final EmptyTraverser INSTANCE = new EmptyTraverser();
+
+    public static <R> EmptyTraverser<R> instance() {
+        return INSTANCE;
+    }
+
+    private EmptyTraverser() {
+
+    }
+
+    @Override
+    public void set(final T t) {
+
+    }
+
+    @Override
+    public void incrLoops(final String stepLabel) {
+
+    }
+
+    @Override
+    public void resetLoops() {
+
+    }
+
+    @Override
+    public String getStepId() {
+        return HALT;
+    }
+
+    @Override
+    public void setStepId(final String stepId) {
+
+    }
+
+    @Override
+    public void setBulk(long count) {
+
+    }
+
+    @Override
+    public <R> Admin<R> split(final R r, final Step<T, R> step) {
+        return INSTANCE;
+    }
+
+    @Override
+    public Admin<T> split() {
+        return this;
+    }
+
+    @Override
+    public Admin<T> detach() {
+        return this;
+    }
+
+    @Override
+    public Admin<T> attach(final Vertex hostVertex) {
+        return this;
+    }
+
+    @Override
+    public void setSideEffects(final TraversalSideEffects sideEffects) {
+
+    }
+
+    @Override
+    public T get() {
+        return null;
+    }
+
+    @Override
+    public <S> S sack() {
+        return null;
+    }
+
+    @Override
+    public <S> void sack(final S object) {
+
+    }
+
+    @Override
+    public void merge(final Traverser.Admin<?> other) {
+
+    }
+
+    @Override
+    public Path path() {
+        return EmptyPath.instance();
+    }
+
+    @Override
+    public int loops() {
+        return 0;
+    }
+
+    @Override
+    public long bulk() {
+        return 0l;
+    }
+
+    @Override
+    public TraversalSideEffects getSideEffects() {
+        return null;
+    }
+
+    @Override
+    public int hashCode() {
+        return 380473707;
+    }
+
+    @Override
+    public boolean equals(final Object object) {
+        return object instanceof EmptyTraverser;
+    }
+
+    @Override
+    @SuppressWarnings("CloneDoesntCallSuperClone")
+    public EmptyTraverser<T> clone() {
+        return this;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/4c97e964/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/TraverserSet.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/TraverserSet.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/TraverserSet.java
new file mode 100644
index 0000000..0a0cc5a
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/util/TraverserSet.java
@@ -0,0 +1,143 @@
+/*
+ * 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.traverser.util;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+
+import java.io.Serializable;
+import java.util.AbstractSet;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+import java.util.Spliterator;
+
+/**
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ */
+public class TraverserSet<S> extends AbstractSet<Traverser.Admin<S>> implements Set<Traverser.Admin<S>>, Queue<Traverser.Admin<S>>, Serializable {
+
+    private final Map<Traverser.Admin<S>, Traverser.Admin<S>> map = new LinkedHashMap<>();
+
+    public TraverserSet() {
+
+    }
+
+    public TraverserSet(final Traverser.Admin<S> traverser) {
+        this.map.put(traverser, traverser);
+    }
+
+    @Override
+    public Iterator<Traverser.Admin<S>> iterator() {
+        return this.map.values().iterator();
+    }
+
+    public Traverser.Admin<S> get(final Traverser.Admin<S> traverser) {
+        return this.map.get(traverser);
+    }
+
+    @Override
+    public int size() {
+        return this.map.size();
+    }
+
+    public long bulkSize() {
+        return this.map.values().stream().map(Traverser::bulk).reduce(0l, (a, b) -> a + b);
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return this.map.isEmpty();
+    }
+
+    @Override
+    public boolean contains(final Object traverser) {
+        return this.map.containsKey(traverser);
+    }
+
+    @Override
+    public boolean add(final Traverser.Admin<S> traverser) {
+        final Traverser.Admin<S> existing = this.map.get(traverser);
+        if (null == existing) {
+            this.map.put(traverser, traverser);
+            return true;
+        } else {
+            existing.merge(traverser);
+            return false;
+        }
+    }
+
+    @Override
+    public boolean offer(final Traverser.Admin<S> traverser) {
+        return this.add(traverser);
+    }
+
+    @Override
+    public Traverser.Admin<S> remove() {  // pop, exception if empty
+        return this.map.remove(this.map.values().iterator().next());
+    }
+
+    @Override
+    public Traverser.Admin<S> poll() {  // pop, null if empty
+        return this.map.isEmpty() ? null : this.remove();
+    }
+
+    @Override
+    public Traverser.Admin<S> element() { // peek, exception if empty
+        return this.iterator().next();
+    }
+
+    @Override
+    public Traverser.Admin<S> peek() { // peek, null if empty
+        return this.map.isEmpty() ? null : this.iterator().next();
+    }
+
+    @Override
+    public boolean remove(final Object traverser) {
+        return this.map.remove(traverser) != null;
+    }
+
+    @Override
+    public void clear() {
+        this.map.clear();
+    }
+
+    @Override
+    public Spliterator<Traverser.Admin<S>> spliterator() {
+        return this.map.values().spliterator();
+    }
+
+    @Override
+    public String toString() {
+        return this.map.keySet().toString();
+    }
+
+    public void sort(final Comparator<Traverser<S>> comparator) {
+        final List<Traverser.Admin<S>> list = new ArrayList<>(this.map.values());
+        Collections.sort(list, comparator);
+        this.map.clear();
+        list.forEach(traverser -> this.map.put(traverser, traverser));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/4c97e964/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/DefaultTraversal.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/DefaultTraversal.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/DefaultTraversal.java
new file mode 100644
index 0000000..71e286d
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/DefaultTraversal.java
@@ -0,0 +1,256 @@
+/*
+ * 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.util;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Step;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalEngine;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalSideEffects;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
+import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+import org.apache.tinkerpop.gremlin.process.traversal.engine.StandardTraversalEngine;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.EmptyStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
+import org.apache.tinkerpop.gremlin.structure.Graph;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ */
+public class DefaultTraversal<S, E> implements Traversal.Admin<S, E> {
+
+    private E lastEnd = null;
+    private long lastEndCount = 0l;
+    private Step<?, E> finalEndStep = EmptyStep.instance();
+    private final StepPosition stepPosition = new StepPosition();
+
+    protected final transient Graph graph;
+
+    protected List<Step> steps = new ArrayList<>();
+    protected TraversalSideEffects sideEffects = new DefaultTraversalSideEffects();
+    protected TraversalStrategies strategies;
+    protected TraversalEngine traversalEngine;
+
+    protected boolean locked = false;
+
+    protected TraversalParent traversalParent = (TraversalParent) EmptyStep.instance();
+
+    public DefaultTraversal(final Graph graph) {
+        this.graph = graph;
+        this.setStrategies(TraversalStrategies.GlobalCache.getStrategies(this.graph.getClass()));
+        this.traversalEngine = StandardTraversalEngine.instance(); // TODO: remove and then clean up v.outE
+    }
+
+    @Override
+    public Traversal.Admin<S, E> asAdmin() {
+        return this;
+    }
+
+    @Override
+    public void applyStrategies() throws IllegalStateException {
+        if (this.locked) throw Traversal.Exceptions.traversalIsLocked();
+        TraversalHelper.reIdSteps(this.stepPosition, this);
+        this.strategies.applyStrategies(this);
+        for (final Step<?, ?> step : this.getSteps()) {
+            if (step instanceof TraversalParent) {
+                for (final Traversal.Admin<?, ?> globalChild : ((TraversalParent) step).getGlobalChildren()) {
+                    globalChild.setStrategies(this.strategies);
+                    globalChild.setEngine(this.traversalEngine);
+                    globalChild.applyStrategies();
+                }
+                for (final Traversal.Admin<?, ?> localChild : ((TraversalParent) step).getLocalChildren()) {
+                    localChild.setStrategies(this.strategies);
+                    localChild.setEngine(StandardTraversalEngine.instance());
+                    localChild.applyStrategies();
+                }
+            }
+        }
+        this.traversalEngine.processTraversal(this);
+        this.finalEndStep = this.getEndStep();
+        this.locked = true;
+    }
+
+    @Override
+    public TraversalEngine getEngine() {
+        return this.traversalEngine;
+    }
+
+    @Override
+    public void setEngine(final TraversalEngine engine) {
+        this.traversalEngine = engine;
+    }
+
+    @Override
+    public List<Step> getSteps() {
+        return Collections.unmodifiableList(this.steps);
+    }
+
+    @Override
+    public boolean hasNext() {
+        if (!this.locked) this.applyStrategies();
+        return this.lastEndCount > 0l || this.finalEndStep.hasNext();
+    }
+
+    @Override
+    public E next() {
+        if (!this.locked) this.applyStrategies();
+        if (this.lastEndCount > 0l) {
+            this.lastEndCount--;
+            return this.lastEnd;
+        } else {
+            final Traverser<E> next = this.finalEndStep.next();
+            final long nextBulk = next.bulk();
+            if (nextBulk == 1) {
+                return next.get();
+            } else {
+                this.lastEndCount = nextBulk - 1;
+                this.lastEnd = next.get();
+                return this.lastEnd;
+            }
+        }
+    }
+
+    @Override
+    public void reset() {
+        this.steps.forEach(Step::reset);
+        this.lastEndCount = 0l;
+    }
+
+    @Override
+    public void addStart(final Traverser<S> start) {
+        if (!this.locked) this.applyStrategies();
+        if (!this.steps.isEmpty()) this.steps.get(0).addStart(start);
+    }
+
+    @Override
+    public void addStarts(final Iterator<Traverser<S>> starts) {
+        if (!this.locked) this.applyStrategies();
+        if (!this.steps.isEmpty()) this.steps.get(0).addStarts(starts);
+    }
+
+    @Override
+    public String toString() {
+        return TraversalHelper.makeTraversalString(this);
+    }
+
+    @Override
+    public Step<S, ?> getStartStep() {
+        return this.steps.isEmpty() ? EmptyStep.instance() : this.steps.get(0);
+    }
+
+    @Override
+    public Step<?, E> getEndStep() {
+        return this.steps.isEmpty() ? EmptyStep.instance() : this.steps.get(this.steps.size() - 1);
+    }
+
+    @Override
+    public DefaultTraversal<S, E> clone() {
+        try {
+            final DefaultTraversal<S, E> clone = (DefaultTraversal<S, E>) super.clone();
+            clone.steps = new ArrayList<>();
+            clone.sideEffects = this.sideEffects.clone();
+            clone.strategies = this.strategies.clone(); // TODO: does this need to be cloned?
+            clone.lastEnd = null;
+            clone.lastEndCount = 0l;
+            for (final Step<?, ?> step : this.steps) {
+                final Step<?, ?> clonedStep = step.clone();
+                clonedStep.setTraversal(clone);
+                final Step previousStep = clone.steps.isEmpty() ? EmptyStep.instance() : clone.steps.get(clone.steps.size() - 1);
+                clonedStep.setPreviousStep(previousStep);
+                previousStep.setNextStep(clonedStep);
+                clone.steps.add(clonedStep);
+            }
+            clone.finalEndStep = clone.getEndStep();
+            return clone;
+        } catch (final CloneNotSupportedException e) {
+            throw new IllegalStateException(e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public boolean isLocked() {
+        return this.locked;
+    }
+
+    @Override
+    public void setSideEffects(final TraversalSideEffects sideEffects) {
+        this.sideEffects = sideEffects;
+    }
+
+    @Override
+    public TraversalSideEffects getSideEffects() {
+        return this.sideEffects;
+    }
+
+    @Override
+    public void setStrategies(final TraversalStrategies strategies) {
+        this.strategies = strategies.clone();
+    }
+
+    @Override
+    public TraversalStrategies getStrategies() {
+        return this.strategies;
+    }
+
+    @Override
+    public <S2, E2> Traversal.Admin<S2, E2> addStep(final int index, final Step<?, ?> step) throws IllegalStateException {
+        if (this.locked) throw Exceptions.traversalIsLocked();
+        step.setId(this.stepPosition.nextXId());
+        this.steps.add(index, step);
+        final Step previousStep = this.steps.size() > 0 && index != 0 ? steps.get(index - 1) : null;
+        final Step nextStep = this.steps.size() > index + 1 ? steps.get(index + 1) : null;
+        step.setPreviousStep(null != previousStep ? previousStep : EmptyStep.instance());
+        step.setNextStep(null != nextStep ? nextStep : EmptyStep.instance());
+        if (null != previousStep) previousStep.setNextStep(step);
+        if (null != nextStep) nextStep.setPreviousStep(step);
+        return (Traversal.Admin<S2, E2>) this;
+    }
+
+    @Override
+    public <S2, E2> Traversal.Admin<S2, E2> removeStep(final int index) throws IllegalStateException {
+        if (this.locked) throw Exceptions.traversalIsLocked();
+        final Step previousStep = this.steps.size() > 0 && index != 0 ? steps.get(index - 1) : null;
+        final Step nextStep = this.steps.size() > index + 1 ? steps.get(index + 1) : null;
+        this.steps.remove(index);
+        if (null != previousStep) previousStep.setNextStep(null == nextStep ? EmptyStep.instance() : nextStep);
+        if (null != nextStep) nextStep.setPreviousStep(null == previousStep ? EmptyStep.instance() : previousStep);
+        return (Traversal.Admin<S2, E2>) this;
+    }
+
+    @Override
+    public void setParent(final TraversalParent step) {
+        this.traversalParent = step;
+    }
+
+    @Override
+    public TraversalParent getParent() {
+        return this.traversalParent;
+    }
+
+    @Override
+    public Optional<Graph> getGraph() {
+        return Optional.ofNullable(this.graph);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/4c97e964/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/DefaultTraversalSideEffects.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/DefaultTraversalSideEffects.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/DefaultTraversalSideEffects.java
new file mode 100644
index 0000000..b93185e
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/DefaultTraversalSideEffects.java
@@ -0,0 +1,202 @@
+/*
+ * 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.util;
+
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalSideEffects;
+import org.apache.tinkerpop.gremlin.structure.Property;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Supplier;
+import java.util.function.UnaryOperator;
+
+/**
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ */
+public class DefaultTraversalSideEffects implements TraversalSideEffects {
+
+    protected Map<String, Object> objectMap = new HashMap<>();
+    protected Map<String, Supplier> supplierMap = new HashMap<>();
+    protected UnaryOperator sackSplitOperator = null;
+    protected Supplier sackInitialValue = null;
+
+    public DefaultTraversalSideEffects() {
+
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void registerSupplier(final String key, final Supplier supplier) {
+        this.supplierMap.put(key, supplier);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public <V> Optional<Supplier<V>> getRegisteredSupplier(final String key) {
+        return Optional.ofNullable(this.supplierMap.get(key));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void registerSupplierIfAbsent(final String key, final Supplier supplier) {
+        if (!this.supplierMap.containsKey(key))
+            this.supplierMap.put(key, supplier);
+    }
+
+    @Override
+    public <S> void setSack(final Supplier<S> initialValue, final Optional<UnaryOperator<S>> splitOperator) {
+        this.sackInitialValue = initialValue;
+        this.sackSplitOperator = splitOperator.orElse(null);
+    }
+
+    @Override
+    public <S> Optional<Supplier<S>> getSackInitialValue() {
+        return Optional.ofNullable(this.sackInitialValue);
+    }
+
+    @Override
+    public <S> Optional<UnaryOperator<S>> getSackSplitOperator() {
+        return Optional.ofNullable(this.sackSplitOperator);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean exists(final String key) {
+        return this.objectMap.containsKey(key) || this.supplierMap.containsKey(key);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void set(final String key, final Object value) {
+        SideEffectHelper.validateSideEffect(key, value);
+        this.objectMap.put(key, value);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public <V> V get(final String key) throws IllegalArgumentException {
+        final V value = (V) this.objectMap.get(key);
+        if (null != value)
+            return value;
+        else {
+            if (this.supplierMap.containsKey(key)) {
+                final V v = (V) this.supplierMap.get(key).get();
+                this.objectMap.put(key, v);
+                return v;
+            } else {
+                throw TraversalSideEffects.Exceptions.sideEffectDoesNotExist(key);
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public <V> V getOrCreate(final String key, final Supplier<V> orCreate) {
+        if (this.objectMap.containsKey(key))
+            return (V) this.objectMap.get(key);
+        else if (this.supplierMap.containsKey(key)) {
+            final V value = (V) this.supplierMap.get(key).get();
+            this.objectMap.put(key, value);
+            return value;
+        } else {
+            final V value = orCreate.get();
+            this.objectMap.put(key, value);
+            return value;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void remove(final String key) {
+        this.objectMap.remove(key);
+        this.supplierMap.remove(key);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Set<String> keys() {
+        final Set<String> keys = new HashSet<>();
+        keys.addAll(this.objectMap.keySet());
+        keys.addAll(this.supplierMap.keySet());
+        return Collections.unmodifiableSet(keys);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setLocalVertex(final Vertex vertex) {
+        final Property<Map<String, Object>> property = vertex.property(SIDE_EFFECTS);
+        if (property.isPresent()) {
+            this.objectMap = property.value();
+        } else {
+            this.objectMap = new HashMap<>();
+            vertex.property(SIDE_EFFECTS, this.objectMap);
+        }
+    }
+
+    @Override
+    public void mergeInto(final TraversalSideEffects sideEffects) {
+        this.objectMap.forEach(sideEffects::set);
+        this.supplierMap.forEach(sideEffects::registerSupplierIfAbsent);
+        // TODO: add sack information?
+    }
+
+    @Override
+    public String toString() {
+        return StringFactory.traversalSideEffectsString(this);
+    }
+
+    @Override
+    @SuppressWarnings("CloneDoesntDeclareCloneNotSupportedException")
+    public DefaultTraversalSideEffects clone() {
+        try {
+            final DefaultTraversalSideEffects sideEffects = (DefaultTraversalSideEffects) super.clone();
+            sideEffects.objectMap = new HashMap<>(this.objectMap);
+            sideEffects.supplierMap = new HashMap<>(this.supplierMap);
+            return sideEffects;
+        } catch (final CloneNotSupportedException e) {
+            throw new IllegalStateException(e.getMessage(), e);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/4c97e964/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/DefaultTraversalStrategies.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/DefaultTraversalStrategies.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/DefaultTraversalStrategies.java
new file mode 100644
index 0000000..eca4d5d
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/DefaultTraversalStrategies.java
@@ -0,0 +1,105 @@
+/*
+ * 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.util;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserGeneratorFactory;
+import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.DefaultTraverserGeneratorFactory;
+import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ */
+public class DefaultTraversalStrategies implements TraversalStrategies {
+
+    protected List<TraversalStrategy> traversalStrategies = new ArrayList<>();
+    protected TraverserGeneratorFactory traverserGeneratorFactory = DefaultTraverserGeneratorFactory.instance();
+
+    @Override
+    public TraversalStrategies addStrategies(final TraversalStrategy... strategies) {
+        boolean added = false;
+        for (final TraversalStrategy strategy : strategies) {
+            if (!this.traversalStrategies.contains(strategy)) {
+                this.traversalStrategies.add(strategy);
+                added = true;
+            }
+        }
+        if (added) TraversalStrategies.sortStrategies(this.traversalStrategies);
+        return this;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public TraversalStrategies removeStrategies(final Class<? extends TraversalStrategy>... strategyClasses) {
+        boolean removed = false;
+        for (final Class<? extends TraversalStrategy> strategyClass : strategyClasses) {
+            final Optional<TraversalStrategy> strategy = this.traversalStrategies.stream().filter(s -> s.getClass().equals(strategyClass)).findAny();
+            if (strategy.isPresent()) {
+                this.traversalStrategies.remove(strategy.get());
+                removed = true;
+            }
+        }
+        if (removed) TraversalStrategies.sortStrategies(this.traversalStrategies);
+        return this;
+    }
+
+    @Override
+    public List<TraversalStrategy> toList() {
+        return Collections.unmodifiableList(this.traversalStrategies);
+    }
+
+    @Override
+    public void applyStrategies(final Traversal.Admin<?, ?> traversal) {
+        this.traversalStrategies.forEach(traversalStrategy -> traversalStrategy.apply(traversal));
+    }
+
+    @Override
+    public TraverserGeneratorFactory getTraverserGeneratorFactory() {
+        return this.traverserGeneratorFactory;
+    }
+
+    @Override
+    public void setTraverserGeneratorFactory(final TraverserGeneratorFactory traverserGeneratorFactory) {
+        this.traverserGeneratorFactory = traverserGeneratorFactory;
+    }
+
+    @Override
+    public DefaultTraversalStrategies clone() {
+        try {
+            final DefaultTraversalStrategies clone = (DefaultTraversalStrategies) super.clone();
+            clone.traversalStrategies = new ArrayList<>();
+            clone.traversalStrategies.addAll(this.traversalStrategies);
+            return clone;
+        } catch (final CloneNotSupportedException e) {
+            throw new IllegalStateException(e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return StringFactory.traversalStrategiesString(this);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/4c97e964/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/DependantMutableMetrics.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/DependantMutableMetrics.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/DependantMutableMetrics.java
new file mode 100644
index 0000000..fd580fe
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/DependantMutableMetrics.java
@@ -0,0 +1,59 @@
+/*
+ * 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.util;
+
+/**
+ * This Metrics class handles a metrics chain in which durations are "double counted" by upstream metrics. Durations are
+ * corrected on-the-fly by subtracting upstream durations on every call to stop().
+ *
+ * @author Bob Briody (http://bobbriody.com)
+ */
+public class DependantMutableMetrics extends MutableMetrics {
+    private long prevDur = 0L;
+    private DependantMutableMetrics upStreamMetrics;
+
+    private DependantMutableMetrics() {
+        // necessary for gryo serialization
+        super();
+    }
+
+    public DependantMutableMetrics(final String id, final String name, final DependantMutableMetrics upStreamMetrics) {
+        super(id, name);
+        this.upStreamMetrics = upStreamMetrics;
+    }
+
+    public void start() {
+        super.start();
+    }
+
+    public void stop() {
+        super.stop();
+        // root step will not have an upstream metrics
+        if (upStreamMetrics != null) {
+            // subtract time that is "double counted" by upstream metrics
+            super.durationNs -= upStreamMetrics.getAndResetIncrementalDur();
+        }
+    }
+
+    public long getAndResetIncrementalDur() {
+        long incrementalDur = super.durationNs - prevDur;
+        prevDur = super.durationNs;
+        return incrementalDur;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/4c97e964/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/EmptyTraversal.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/EmptyTraversal.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/EmptyTraversal.java
index 5d2a6ef..72dbcc3 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/EmptyTraversal.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/EmptyTraversal.java
@@ -18,18 +18,18 @@
  */
 package org.apache.tinkerpop.gremlin.process.traversal.util;
 
-import org.apache.tinkerpop.gremlin.process.FastNoSuchElementException;
-import org.apache.tinkerpop.gremlin.process.Step;
-import org.apache.tinkerpop.gremlin.process.Traversal;
-import org.apache.tinkerpop.gremlin.process.TraversalEngine;
-import org.apache.tinkerpop.gremlin.process.TraversalSideEffects;
-import org.apache.tinkerpop.gremlin.process.TraversalStrategies;
-import org.apache.tinkerpop.gremlin.process.Traverser;
-import org.apache.tinkerpop.gremlin.process.TraverserGenerator;
+import org.apache.tinkerpop.gremlin.process.traversal.FastNoSuchElementException;
+import org.apache.tinkerpop.gremlin.process.traversal.Step;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalEngine;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalSideEffects;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
+import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+import org.apache.tinkerpop.gremlin.process.traversal.TraverserGenerator;
 import org.apache.tinkerpop.gremlin.process.traversal.engine.StandardTraversalEngine;
-import org.apache.tinkerpop.gremlin.process.traversal.step.EmptyStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.EmptyStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
-import org.apache.tinkerpop.gremlin.process.traverser.TraverserRequirement;
+import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
 import org.apache.tinkerpop.gremlin.structure.Graph;
 
 import java.util.Collections;

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/4c97e964/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/EmptyTraversalSideEffects.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/EmptyTraversalSideEffects.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/EmptyTraversalSideEffects.java
index d41f12b..e82ac1f 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/EmptyTraversalSideEffects.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/EmptyTraversalSideEffects.java
@@ -18,7 +18,7 @@
  */
 package org.apache.tinkerpop.gremlin.process.traversal.util;
 
-import org.apache.tinkerpop.gremlin.process.TraversalSideEffects;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalSideEffects;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
 
 import java.util.Collections;

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/4c97e964/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/EmptyTraversalStrategies.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/EmptyTraversalStrategies.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/EmptyTraversalStrategies.java
index 48a4405..c0a9175 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/EmptyTraversalStrategies.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/EmptyTraversalStrategies.java
@@ -18,12 +18,11 @@
  */
 package org.apache.tinkerpop.gremlin.process.traversal.util;
 
-import org.apache.tinkerpop.gremlin.process.Traversal;
-import org.apache.tinkerpop.gremlin.process.TraversalEngine;
-import org.apache.tinkerpop.gremlin.process.TraversalStrategies;
-import org.apache.tinkerpop.gremlin.process.TraversalStrategy;
-import org.apache.tinkerpop.gremlin.process.traverser.TraverserGeneratorFactory;
-import org.apache.tinkerpop.gremlin.process.traverser.util.DefaultTraverserGeneratorFactory;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserGeneratorFactory;
+import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.DefaultTraverserGeneratorFactory;
 
 import java.util.Collections;
 import java.util.List;

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/4c97e964/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/ImmutableMetrics.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/ImmutableMetrics.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/ImmutableMetrics.java
new file mode 100644
index 0000000..471fc5f
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/ImmutableMetrics.java
@@ -0,0 +1,103 @@
+/*
+ * 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.util;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * @author Bob Briody (http://bobbriody.com)
+ */
+public class ImmutableMetrics implements Metrics, Serializable {
+
+    static final TimeUnit SOURCE_UNIT = TimeUnit.NANOSECONDS;
+
+    protected String id;
+    protected String name;
+    protected Map<String, AtomicLong> counts = new HashMap<>();
+    protected long durationNs = 0l;
+    protected final Map<String, Object> annotations = new HashMap<>();
+    protected final Map<String, ImmutableMetrics> nested = new LinkedHashMap<>();
+
+    protected ImmutableMetrics() {
+    }
+
+    @Override
+    public long getDuration(TimeUnit unit) {
+        return unit.convert(this.durationNs, SOURCE_UNIT);
+    }
+
+    @Override
+    public long getCount(String key) {
+        return counts.get(key).get();
+    }
+
+    @Override
+    public Map<String, Long> getCounts() {
+        Map<String, Long> ret = new HashMap<>();
+        for (Map.Entry<String, AtomicLong> count : counts.entrySet()) {
+            ret.put(count.getKey(), count.getValue().get());
+        }
+        return ret;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    @Override
+    public Collection<ImmutableMetrics> getNested() {
+        return nested.values();
+    }
+
+    @Override
+    public ImmutableMetrics getNested(String metricsId) {
+        return nested.get(metricsId);
+    }
+
+    @Override
+    public Map<String, Object> getAnnotations() {
+        return annotations;
+    }
+
+    @Override
+    public Object getAnnotation(final String key) {
+        return annotations.get(key);
+    }
+
+    @Override
+    public String toString() {
+        return "ImmutableMetrics{" +
+                "id='" + id + '\'' +
+                ", name='" + name + '\'' +
+                ", counts=" + counts +
+                ", durationNs=" + durationNs +
+                '}';
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/4c97e964/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/Metrics.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/Metrics.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/Metrics.java
new file mode 100644
index 0000000..6f2c963
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/Metrics.java
@@ -0,0 +1,104 @@
+/*
+ * 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.util;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Holds metrics data; typically for .profile()-step analysis. Metrics may be nested. Nesting enables the ability to
+ * capture explicit metrics for multiple distinct operations. Annotations are used to store miscellaneous notes that
+ * might be useful to a developer when examining results, such as index coverage for Steps in a Traversal.
+ *
+ * @author Bob Briody (http://bobbriody.com)
+ */
+public interface Metrics {
+
+
+
+
+    /**
+     * Get the duration of execution time taken.
+     *
+     * @param units
+     * @return
+     */
+    public long getDuration(TimeUnit units);
+
+    /**
+     * Get the count for the corresponding countKey.
+     *
+     * @param countKey key for counter to get.
+     * @return
+     */
+    public long getCount(String countKey);
+
+    /**
+     * Get the map of all counters. This method copies the internal map.
+     *
+     * @return a Map where the key is the counter ID and the value is the counter value.
+     */
+    public Map<String, Long> getCounts();
+
+    /**
+     * Name of this Metrics.
+     *
+     * @return name of this Metrics.
+     */
+    public String getName();
+
+    /**
+     * Id of this Metrics.
+     *
+     * @return id of this Metrics.
+     */
+    public String getId();
+
+
+    /**
+     * Get the nested Metrics objects. Metrics will be ordered in the order they were inserted.
+     *
+     * @return the nested Metrics objects.
+     */
+    public Collection<? extends Metrics> getNested();
+
+    /**
+     * Get a nested Metrics object by Id.
+     *
+     * @param metricsId
+     * @return a nested Metrics object.
+     */
+    Metrics getNested(String metricsId);
+
+    /**
+     * Obtain the annotations for this Metrics. Values may be of type String or Number.
+     *
+     * @return the annotations for this Metrics. Modifications to the returned object are persisted in the original.
+     */
+    public Map<String, Object> getAnnotations();
+
+    /**
+     * Obtain the annotation with the specified key. Values may be of type String or Number.
+     *
+     * @param key key of the annotation to obtain.
+     * @return
+     */
+    public Object getAnnotation(String key);
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/4c97e964/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/MutableMetrics.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/MutableMetrics.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/MutableMetrics.java
new file mode 100644
index 0000000..ec5b19c
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/MutableMetrics.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 org.apache.tinkerpop.gremlin.process.traversal.util;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * @author Bob Briody (http://bobbriody.com)
+ */
+public class MutableMetrics extends ImmutableMetrics implements Cloneable {
+
+    // Note: if you add new members then you probably need to add them to the copy constructor;
+
+    private long tempTime = -1l;
+
+    protected MutableMetrics() {
+        // necessary for gryo serialization
+    }
+
+    public MutableMetrics(final String id, final String name) {
+        this.id = id;
+        this.name = name;
+    }
+
+
+    public void addNested(MutableMetrics metrics) {
+        this.nested.put(metrics.getId(), metrics);
+    }
+
+    public void start() {
+        if (-1 != this.tempTime) {
+            throw new IllegalStateException("Internal Error: Concurrent Metrics start. Stop timer before starting timer.");
+        }
+        this.tempTime = System.nanoTime();
+    }
+
+    public void stop() {
+        if (-1 == this.tempTime)
+            throw new IllegalStateException("Internal Error: Metrics has not been started. Start timer before stopping timer");
+        this.durationNs = this.durationNs + (System.nanoTime() - this.tempTime);
+        this.tempTime = -1;
+    }
+
+    public void incrementCount(String key, final long incr) {
+        AtomicLong count = this.counts.get(key);
+        if (count == null) {
+            count = new AtomicLong();
+            this.counts.put(key, count);
+        }
+        count.addAndGet(incr);
+    }
+
+    public void aggregate(MutableMetrics other) {
+        this.durationNs += other.durationNs;
+        for (Map.Entry<String, AtomicLong> otherCount : other.counts.entrySet()) {
+            AtomicLong thisCount = this.counts.get(otherCount.getKey());
+            if (thisCount == null) {
+                thisCount = new AtomicLong(otherCount.getValue().get());
+                this.counts.put(otherCount.getKey(), thisCount);
+            } else {
+                thisCount.addAndGet(otherCount.getValue().get());
+            }
+        }
+
+        // Merge annotations. If multiple values for a given key are found then append it to a comma-separated list.
+        for (Map.Entry<String, Object> p : other.annotations.entrySet()) {
+            if (this.annotations.containsKey(p.getKey())) {
+                // Strings are concatenated
+                Object existingVal = this.annotations.get(p.getKey());
+                if (existingVal instanceof String) {
+                    final List<String> existingValues = Arrays.asList(existingVal.toString().split(","));
+                    if (!existingValues.contains(p.getValue())) {
+                        // New value. Append to comma-separated list.
+                        this.annotations.put(p.getKey(), existingVal.toString() + ',' + p.getValue());
+                    }
+                } else {
+                    // Numbers are summed
+                    Number existingNum = (Number) existingVal;
+                    Number otherNum = (Number) p.getValue();
+                    Number newVal;
+                    if (existingNum instanceof Double || existingNum instanceof Float) {
+                        newVal =
+                                existingNum.doubleValue() + otherNum.doubleValue();
+                    } else {
+                        newVal = existingNum.longValue() + otherNum.longValue();
+                    }
+                    this.annotations.put(p.getKey(), newVal);
+                }
+            } else {
+                this.annotations.put(p.getKey(), p.getValue());
+            }
+        }
+        this.annotations.putAll(other.annotations);
+
+        // Merge nested Metrics
+        other.nested.values().forEach(nested -> {
+            MutableMetrics thisNested = (MutableMetrics) this.nested.get(nested.getId());
+            if (thisNested == null) {
+                thisNested = new MutableMetrics(nested.getId(), nested.getName());
+                this.nested.put(thisNested.getId(), thisNested);
+            }
+            thisNested.aggregate((MutableMetrics) nested);
+        });
+    }
+
+    /**
+     * Set an annotation value. Support exists for Strings and Numbers only. During a merge, Strings are concatenated
+     * into a "," (comma) separated list of distinct values (duplicates are ignored), and Numbers are summed.
+     *
+     * @param key
+     * @param value
+     */
+    public void setAnnotation(String key, Object value) {
+        if (!(value instanceof String) && !(value instanceof Number)) {
+            throw new IllegalArgumentException("Metrics annotations only support String and Number values.");
+        }
+        annotations.put(key, value);
+    }
+
+    @Override
+    public MutableMetrics getNested(String metricsId) {
+        return (MutableMetrics) nested.get(metricsId);
+    }
+
+    public ImmutableMetrics getImmutableClone() {
+        final ImmutableMetrics clone = new ImmutableMetrics();
+        copyMembers(clone);
+        this.nested.values().forEach(nested -> clone.nested.put(nested.id, ((MutableMetrics) nested).getImmutableClone()));
+        return clone;
+    }
+
+    private void copyMembers(final ImmutableMetrics clone) {
+        clone.id = this.id;
+        clone.name = this.name;
+        clone.durationNs = this.durationNs;
+        for (Map.Entry<String, AtomicLong> c : this.counts.entrySet()) {
+            clone.counts.put(c.getKey(), new AtomicLong(c.getValue().get()));
+        }
+        for (Map.Entry<String, Object> a : this.annotations.entrySet()) {
+            clone.annotations.put(a.getKey(), a.getValue());
+        }
+    }
+
+    @Override
+    public MutableMetrics clone() {
+        final MutableMetrics clone = new MutableMetrics();
+        copyMembers(clone);
+        this.nested.values().forEach(nested -> clone.nested.put(nested.id, ((MutableMetrics) nested).clone()));
+        return clone;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/4c97e964/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/SideEffectHelper.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/SideEffectHelper.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/SideEffectHelper.java
index f35a8f6..36b88af 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/SideEffectHelper.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/SideEffectHelper.java
@@ -18,7 +18,7 @@
  */
 package org.apache.tinkerpop.gremlin.process.traversal.util;
 
-import org.apache.tinkerpop.gremlin.process.TraversalSideEffects;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalSideEffects;
 
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/4c97e964/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/StandardTraversalMetrics.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/StandardTraversalMetrics.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/StandardTraversalMetrics.java
new file mode 100644
index 0000000..8ea8d6d
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/StandardTraversalMetrics.java
@@ -0,0 +1,204 @@
+/*
+ * 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.util;
+
+import org.apache.commons.lang.StringUtils;
+
+import java.io.Serializable;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author Bob Briody (http://bobbriody.com)
+ */
+public final class StandardTraversalMetrics implements TraversalMetrics, Serializable {
+    // toString() specific headers
+    private static final String[] HEADERS = {"Step", "Count", "Traversers", "Time (ms)", "% Dur"};
+
+    private boolean dirty = true;
+    private final Map<String, MutableMetrics> metrics = new HashMap<>();
+    private final Map<String, MutableMetrics> allMetrics = new HashMap<>();
+    private final TreeMap<Integer, String> indexToLabelMap = new TreeMap<>();
+
+    /*
+    The following are computed values upon the completion of profiling in order to report the results back to the user
+     */
+    private long totalStepDuration;
+    private Map<String, ImmutableMetrics> computedMetrics;
+
+    public StandardTraversalMetrics() {
+    }
+
+    public void start(final String metricsId) {
+        dirty = true;
+        if (allMetrics.get(metricsId) == null) {
+            System.out.println();
+        }
+        allMetrics.get(metricsId).start();
+    }
+
+    public void stop(final String metricsId) {
+        dirty = true;
+        allMetrics.get(metricsId).stop();
+    }
+
+    public void finish(final String metricsId, final long bulk) {
+        dirty = true;
+        final MutableMetrics metrics = allMetrics.get(metricsId);
+        metrics.stop();
+        metrics.incrementCount(TRAVERSER_COUNT_ID, 1);
+        metrics.incrementCount(ELEMENT_COUNT_ID, bulk);
+    }
+
+    @Override
+    public long getDuration(final TimeUnit unit) {
+        computeTotals();
+        return unit.convert(totalStepDuration, MutableMetrics.SOURCE_UNIT);
+    }
+
+    @Override
+    public Metrics getMetrics(final int index) {
+        computeTotals();
+        // adjust index to account for the injected profile steps
+        return (Metrics) computedMetrics.get(indexToLabelMap.get(index));
+    }
+
+    @Override
+    public Metrics getMetrics(final String id) {
+        computeTotals();
+        return computedMetrics.get(id);
+    }
+
+    @Override
+    public Collection<ImmutableMetrics> getMetrics() {
+        computeTotals();
+        return computedMetrics.values();
+    }
+
+    @Override
+    public String toString() {
+        computeTotals();
+
+        // Build a pretty table of metrics data.
+
+        // Append headers
+        final StringBuilder sb = new StringBuilder("Traversal Metrics\n")
+                .append(String.format("%-50s %21s %11s %15s %8s", HEADERS));
+
+        sb.append("\n=============================================================================================================");
+
+        appendMetrics(computedMetrics.values(), sb, 0);
+
+        // Append total duration
+        sb.append(String.format("%n%50s %21s %11s %15.3f %8s",
+                ">TOTAL", "-", "-", getDuration(TimeUnit.MICROSECONDS) / 1000.0, "-"));
+
+        return sb.toString();
+    }
+
+    private void appendMetrics(final Collection<? extends Metrics> metrics, final StringBuilder sb, final int indent) {
+        // Append each StepMetric's row. indexToLabelMap values are ordered by index.
+        for (Metrics m : metrics) {
+            String rowName = m.getName();
+            for (int ii = 0; ii < indent; ii++) {
+                rowName = "  " + rowName;
+            }
+            rowName = StringUtils.abbreviate(rowName, 50);
+            final long itemCount = m.getCount(ELEMENT_COUNT_ID);
+            final long traverserCount = m.getCount(TRAVERSER_COUNT_ID);
+
+            Double percentDur = (Double) m.getAnnotation(PERCENT_DURATION_KEY);
+            if (percentDur != null) {
+                sb.append(String.format("%n%-50s %21d %11d %15.3f %8.2f",
+                        rowName, itemCount, traverserCount, m.getDuration(TimeUnit.MICROSECONDS) / 1000.0, percentDur));
+            } else {
+                sb.append(String.format("%n%-50s %21d %11d %15.3f",
+                        rowName, itemCount, traverserCount, m.getDuration(TimeUnit.MICROSECONDS) / 1000.0));
+            }
+            appendMetrics(m.getNested(), sb, indent + 1);
+        }
+    }
+
+    private void computeTotals() {
+        if (!dirty) {
+            // already good to go
+            return;
+        }
+
+        // Create temp list of ordered metrics
+        List<MutableMetrics> tempMetrics = new ArrayList<>(metrics.size());
+        for (String label : indexToLabelMap.values()) {
+            // The indexToLabelMap is sorted by index (key)
+            tempMetrics.add(metrics.get(label).clone());
+        }
+
+        // Calculate total duration
+        this.totalStepDuration = 0;
+        tempMetrics.forEach(m -> this.totalStepDuration += m.getDuration(MutableMetrics.SOURCE_UNIT));
+
+        // Assign %'s
+        tempMetrics.forEach(m -> {
+            double dur = m.getDuration(TimeUnit.NANOSECONDS) * 100.d / this.totalStepDuration;
+            m.setAnnotation(PERCENT_DURATION_KEY, dur);
+        });
+
+        // Store immutable instances of the calculated metrics
+        computedMetrics = new LinkedHashMap<>(metrics.size());
+        tempMetrics.forEach(it -> computedMetrics.put(it.getId(), it.getImmutableClone()));
+
+        dirty = false;
+    }
+
+    public static StandardTraversalMetrics merge(final Iterator<StandardTraversalMetrics> toMerge) {
+        final StandardTraversalMetrics newTraversalMetrics = new StandardTraversalMetrics();
+
+        // iterate the incoming TraversalMetrics
+        toMerge.forEachRemaining(inTraversalMetrics -> {
+            // aggregate the internal Metrics
+            inTraversalMetrics.metrics.forEach((metricsId, toAggregate) -> {
+
+                MutableMetrics aggregateMetrics = newTraversalMetrics.metrics.get(metricsId);
+                if (null == aggregateMetrics) {
+                    // need to create a Metrics to aggregate into
+                    aggregateMetrics = new MutableMetrics(toAggregate.getId(), toAggregate.getName());
+
+                    newTraversalMetrics.metrics.put(metricsId, aggregateMetrics);
+                    // Set the index of the Metrics
+                    for (Map.Entry<Integer, String> entry : inTraversalMetrics.indexToLabelMap.entrySet()) {
+                        if (metricsId.equals(entry.getValue())) {
+                            newTraversalMetrics.indexToLabelMap.put(entry.getKey(), metricsId);
+                            break;
+                        }
+                    }
+                }
+                aggregateMetrics.aggregate(toAggregate);
+            });
+        });
+        return newTraversalMetrics;
+    }
+
+    public void addMetrics(final MutableMetrics newMetrics, final String id, final int index, final boolean isTopLevel, final String profileStepId) {
+        if (isTopLevel) {
+            // The index is necessary to ensure that step order is preserved after a merge.
+            indexToLabelMap.put(index, id);
+            metrics.put(id, newMetrics);
+        }
+        allMetrics.put(profileStepId, newMetrics);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/4c97e964/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/StepPosition.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/StepPosition.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/StepPosition.java
new file mode 100644
index 0000000..4438543
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/StepPosition.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.util;
+
+import java.io.Serializable;
+
+/**
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ */
+public final class StepPosition implements Serializable {
+
+    public int x; // step in traversal length
+    public int y; // depth in traversal nested tree
+    public int z; // breadth in traversal siblings
+    public String parentId; // the traversal holder id
+
+    private static final String DOT = ".";
+    private static final String LEFT_PARENTHESES = "(";
+    private static final String RIGHT_PARENTHESES = ")";
+    private static final String EMPTY_STRING = "";
+
+    private StepPosition(final int x, final int y, final int z, final String parentId) {
+        this.x = x;
+        this.y = y;
+        this.z = z;
+        this.parentId = parentId;
+    }
+
+    public StepPosition() {
+        this(0, 0, 0, EMPTY_STRING);
+    }
+
+    public String nextXId() {
+        return this.x++ + DOT + this.y + DOT + this.z + LEFT_PARENTHESES + this.parentId + RIGHT_PARENTHESES;
+    }
+
+    @Override
+    public String toString() {
+        return this.x + DOT + this.y + DOT + this.z + LEFT_PARENTHESES + this.parentId + RIGHT_PARENTHESES;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/4c97e964/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalHelper.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalHelper.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalHelper.java
index 4e71c7e..0ffe53f 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalHelper.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalHelper.java
@@ -18,17 +18,14 @@
  */
 package org.apache.tinkerpop.gremlin.process.traversal.util;
 
-import org.apache.tinkerpop.gremlin.process.Step;
-import org.apache.tinkerpop.gremlin.process.Traversal;
-import org.apache.tinkerpop.gremlin.process.graph.traversal.step.map.EdgeVertexStep;
-import org.apache.tinkerpop.gremlin.process.graph.traversal.step.map.PropertiesStep;
-import org.apache.tinkerpop.gremlin.process.graph.traversal.step.map.VertexStep;
-import org.apache.tinkerpop.gremlin.process.graph.traversal.step.sideEffect.GraphStep;
-import org.apache.tinkerpop.gremlin.process.traversal.StepPosition;
-import org.apache.tinkerpop.gremlin.process.traversal.step.EmptyStep;
+import org.apache.tinkerpop.gremlin.process.traversal.Step;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.EdgeVertexStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.PropertiesStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.VertexStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.EmptyStep;
 import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
-import org.apache.tinkerpop.gremlin.process.util.BulkSet;
-import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.BulkSet;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
 
 import java.util.ArrayList;

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/4c97e964/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalMatrix.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalMatrix.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalMatrix.java
new file mode 100644
index 0000000..36a105a
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalMatrix.java
@@ -0,0 +1,66 @@
+/*
+ * 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.util;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Step;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A TraversalMatrix provides random, non-linear access to the steps of a traversal by their step id.
+ * This is useful in situations where traversers becomes detached from their traversal (and step) and later need to be re-attached.
+ * A classic use case is {@link org.apache.tinkerpop.gremlin.process.computer.traversal.TraversalVertexProgram} on {@link org.apache.tinkerpop.gremlin.process.computer.GraphComputer}.
+ *
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ */
+public final class TraversalMatrix<S, E> {
+
+    private final Map<String, Step<?, ?>> matrix = new HashMap<>();
+    private final Traversal.Admin<S, E> traversal;
+
+    public TraversalMatrix(final Traversal.Admin<S, E> traversal) {
+        this.harvestSteps(this.traversal = traversal);
+    }
+
+    public <A, B, C extends Step<A, B>> C getStepById(final String stepId) {
+        return (C) this.matrix.get(stepId);
+    }
+
+    public Traversal.Admin<S, E> getTraversal() {
+        return this.traversal;
+    }
+
+    private final void harvestSteps(final Traversal.Admin<?, ?> traversal) {
+        for (final Step<?, ?> step : traversal.getSteps()) {
+            this.matrix.put(step.getId(), step);
+            if (step instanceof TraversalParent) {
+                for (final Traversal.Admin<?, ?> globalChild : ((TraversalParent) step).getGlobalChildren()) {
+                    this.harvestSteps(globalChild);
+                }
+                /*for (final Traversal.Admin<?, ?> localChild : ((TraversalParent) step).getLocalChildren()) {
+                    this.harvestSteps(localChild);
+                }*/
+            }
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/4c97e964/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalMetrics.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalMetrics.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalMetrics.java
new file mode 100644
index 0000000..b2c70cf
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalMetrics.java
@@ -0,0 +1,77 @@
+/*
+ * 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.util;
+
+import org.apache.tinkerpop.gremlin.structure.Graph;
+
+import java.util.Collection;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Contains the Metrics gathered for a Traversal as the result of the .profile()-step.
+ *
+ * @author Bob Briody (http://bobbriody.com)
+ */
+public interface TraversalMetrics {
+    /**
+     * The side-effect key used to store and retrieve the TraversalMetrics for a given Traversal.
+     */
+    public static final String METRICS_KEY = Graph.Hidden.hide("metrics");
+
+    /**
+     * The MetricsId used to obtain the element count via Metrics.getCount(String countKey)
+     */
+    public static final String ELEMENT_COUNT_ID = "elementCount";
+
+    /**
+     * The MetricsId used to obtain the traverser count via Metrics.getCount(String countKey)
+     */
+    public static final String TRAVERSER_COUNT_ID = "traverserCount";
+
+    /**
+     * The annotation key used to obtain the percent duration via Metrics.getAnnotation(String key)
+     */
+    public static final String PERCENT_DURATION_KEY = "percentDur";
+
+    /**
+     * Get the total duration taken by the Traversal.
+     *
+     * @param unit
+     * @return total duration taken by the Traversal.
+     */
+    public long getDuration(TimeUnit unit);
+
+    /**
+     * Get an individual Metrics object by the index of the profiled Step.
+     *
+     * @param stepIndex
+     * @return an individual Metrics object.
+     */
+    public Metrics getMetrics(final int stepIndex);
+
+    /**
+     * Get an individual Metrics object by the id of the profiled Step.
+     *
+     * @param id
+     * @return an individual Metrics object.
+     */
+    public Metrics getMetrics(final String id);
+
+    public Collection<? extends Metrics> getMetrics();
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/4c97e964/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalRing.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalRing.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalRing.java
index 43abc0e..2d16fab 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalRing.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalRing.java
@@ -18,7 +18,7 @@
  */
 package org.apache.tinkerpop.gremlin.process.traversal.util;
 
-import org.apache.tinkerpop.gremlin.process.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.lambda.IdentityTraversal;
 
 import java.io.Serializable;

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/4c97e964/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalUtil.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalUtil.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalUtil.java
index 7c3d74e..98b3b9f 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalUtil.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/TraversalUtil.java
@@ -18,8 +18,8 @@
  */
 package org.apache.tinkerpop.gremlin.process.traversal.util;
 
-import org.apache.tinkerpop.gremlin.process.Traversal;
-import org.apache.tinkerpop.gremlin.process.Traverser;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 
 import java.util.NoSuchElementException;