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;