You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by sp...@apache.org on 2015/03/23 21:53:32 UTC
[2/2] incubator-tinkerpop git commit: Add EventStrategy.
Add EventStrategy.
EventStrategy is similar to TinkerPop2's EventGraph. It tracks change in the Graph and generates events for each change.
Project: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/commit/e529a687
Tree: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/tree/e529a687
Diff: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/diff/e529a687
Branch: refs/heads/master
Commit: e529a6871c5c1c6d87fce4a08a525821ea32565f
Parents: f5c2496
Author: Stephen Mallette <sp...@apache.org>
Authored: Mon Mar 23 16:52:36 2015 -0400
Committer: Stephen Mallette <sp...@apache.org>
Committed: Mon Mar 23 16:52:36 2015 -0400
----------------------------------------------------------------------
.../process/traversal/step/Mutating.java | 11 +-
.../process/traversal/step/filter/DropStep.java | 78 ++-
.../traversal/step/map/AddEdgeByPathStep.java | 47 +-
.../process/traversal/step/map/AddEdgeStep.java | 47 +-
.../traversal/step/map/AddVertexStartStep.java | 40 +-
.../traversal/step/map/AddVertexStep.java | 40 +-
.../step/sideEffect/AddPropertyStep.java | 61 ++-
.../util/event/ConsoleGraphChangedListener.java | 89 ++++
.../step/util/event/EdgeAddedEvent.java | 42 ++
.../util/event/EdgePropertyChangedEvent.java | 38 ++
.../util/event/EdgePropertyRemovedEvent.java | 40 ++
.../step/util/event/EdgeRemovedEvent.java | 45 ++
.../util/event/ElementPropertyChangedEvent.java | 32 ++
.../step/util/event/ElementPropertyEvent.java | 51 ++
.../traversal/step/util/event/Event.java | 30 ++
.../step/util/event/EventCallback.java | 10 +
.../step/util/event/GraphChangedListener.java | 111 +++++
.../step/util/event/VertexAddedEvent.java | 42 ++
.../util/event/VertexPropertyChangedEvent.java | 38 ++
.../VertexPropertyPropertyChangedEvent.java | 38 ++
.../VertexPropertyPropertyRemovedEvent.java | 38 ++
.../util/event/VertexPropertyRemovedEvent.java | 42 ++
.../step/util/event/VertexRemovedEvent.java | 43 ++
.../strategy/decoration/EventStrategy.java | 151 ++++++
.../structure/util/empty/EmptyGraph.java | 15 +
.../strategy/decoration/EventStrategyTest.java | 93 ++++
.../gremlin/process/ProcessStandardSuite.java | 2 +
.../decoration/EventStrategyProcessTest.java | 484 +++++++++++++++++++
28 files changed, 1775 insertions(+), 23 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Mutating.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Mutating.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Mutating.java
index 13ce032..b278dd4 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Mutating.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Mutating.java
@@ -19,10 +19,19 @@
package org.apache.tinkerpop.gremlin.process.traversal.step;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.Event;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.EventCallback;
+
+import java.util.List;
+
/**
* A marker interface for steps that modify the graph.
*
* @author Stephen Mallette (http://stephen.genoprime.com)
*/
-public interface Mutating {
+public interface Mutating<C extends EventCallback<? extends Event>> {
+ public void addCallback(final C c);
+ public void removeCallback(final C c);
+ public void clearCallbacks();
+ public List<C> getCallbacks();
}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DropStep.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DropStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DropStep.java
index 8a3118f..1da4f90 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DropStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DropStep.java
@@ -21,13 +21,32 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.filter;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
import org.apache.tinkerpop.gremlin.process.traversal.step.Mutating;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.EdgePropertyRemovedEvent;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.EdgeRemovedEvent;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.ElementPropertyEvent;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.Event;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.EventCallback;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.VertexPropertyPropertyRemovedEvent;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.VertexPropertyRemovedEvent;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.VertexRemovedEvent;
+import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.Element;
import org.apache.tinkerpop.gremlin.structure.Property;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedFactory;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
/**
* @author Marko A. Rodriguez (http://markorodriguez.com)
+ * @author Stephen Mallette (http://stephen.genoprime.com)
*/
-public final class DropStep<S> extends FilterStep<S> implements Mutating {
+public final class DropStep<S> extends FilterStep<S> implements Mutating<EventCallback<Event>> {
+
+ private List<EventCallback<Event>> callbacks = null;
public DropStep(final Traversal.Admin traversal) {
super(traversal);
@@ -36,13 +55,60 @@ public final class DropStep<S> extends FilterStep<S> implements Mutating {
@Override
protected boolean filter(Traverser.Admin<S> traverser) {
final S s = traverser.get();
- if (s instanceof Element)
- ((Element) s).remove();
- else if (s instanceof Property)
- ((Property) s).remove();
- else
+ if (s instanceof Element) {
+ final Element toRemove = ((Element) s);
+ if (callbacks != null) {
+ final Event removeEvent;
+ if (s instanceof Vertex)
+ removeEvent = new VertexRemovedEvent(DetachedFactory.detach((Vertex) s, true));
+ else if (s instanceof Edge)
+ removeEvent = new EdgeRemovedEvent(DetachedFactory.detach((Edge) s, true));
+ else if (s instanceof VertexProperty)
+ removeEvent = new VertexPropertyRemovedEvent(DetachedFactory.detach((VertexProperty) s, true));
+ else
+ throw new IllegalStateException("The incoming object is not removable: " + s);
+
+ callbacks.forEach(c -> c.accept(removeEvent));
+ }
+
+ toRemove.remove();
+ } else if (s instanceof Property) {
+ final Property toRemove = ((Property) s);
+ if (callbacks != null) {
+ final ElementPropertyEvent removeEvent;
+ if (toRemove.element() instanceof Edge)
+ removeEvent = new EdgePropertyRemovedEvent((Edge) toRemove.element(), DetachedFactory.detach(toRemove));
+ else if (toRemove.element() instanceof VertexProperty)
+ removeEvent = new VertexPropertyPropertyRemovedEvent((VertexProperty) toRemove.element(), DetachedFactory.detach(toRemove));
+ else
+ throw new IllegalStateException("The incoming object is not removable: " + s);
+
+ callbacks.forEach(c -> c.accept(removeEvent));
+ }
+ toRemove.remove();
+ } else
throw new IllegalStateException("The incoming object is not removable: " + s);
return false;
+ }
+
+ @Override
+ public void addCallback(final EventCallback<Event> elementPropertyRemovedEventEventCallback) {
+ if (callbacks == null) callbacks = new ArrayList<>();
+ callbacks.add(elementPropertyRemovedEventEventCallback);
+ }
+
+ @Override
+ public void removeCallback(final EventCallback<Event> elementPropertyRemovedEventEventCallback) {
+ if (callbacks != null) callbacks.remove(elementPropertyRemovedEventEventCallback);
+ }
+ @Override
+ public void clearCallbacks() {
+ if (callbacks != null) callbacks.clear();
+ }
+
+ @Override
+ public List<EventCallback<Event>> getCallbacks() {
+ return (callbacks != null) ? Collections.unmodifiableList(callbacks) : Collections.emptyList();
}
}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeByPathStep.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeByPathStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeByPathStep.java
index 9671f34..1e8a86b 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeByPathStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeByPathStep.java
@@ -21,25 +21,34 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.map;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
import org.apache.tinkerpop.gremlin.process.traversal.step.Mutating;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.EdgeAddedEvent;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.EventCallback;
import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
import org.apache.tinkerpop.gremlin.structure.Direction;
import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedFactory;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.EnumSet;
+import java.util.List;
import java.util.Set;
/**
* @author Marko A. Rodriguez (http://markorodriguez.com)
+ * @author Stephen Mallette (http://stephen.genoprime.com)
*/
-public final class AddEdgeByPathStep extends MapStep<Vertex, Edge> implements Mutating {
+public final class AddEdgeByPathStep extends MapStep<Vertex, Edge> implements Mutating<EventCallback<EdgeAddedEvent>> {
private static final Set<TraverserRequirement> REQUIREMENTS = EnumSet.of(
TraverserRequirement.PATH,
TraverserRequirement.OBJECT
);
+ private List<EventCallback<EdgeAddedEvent>> callbacks = null;
+
// TODO: Weight key based on Traverser.getCount() ?
private final Direction direction;
@@ -79,17 +88,47 @@ public final class AddEdgeByPathStep extends MapStep<Vertex, Edge> implements Mu
}
@Override
- protected Edge map(Traverser.Admin<Vertex> traverser) {
+ protected Edge map(final Traverser.Admin<Vertex> traverser) {
final Vertex currentVertex = traverser.get();
final Vertex otherVertex = traverser.path().get(this.stepLabel);
+
+ Edge e;
if (this.direction.equals(Direction.IN))
- return otherVertex.addEdge(this.edgeLabel, currentVertex, this.keyValues);
+ e = otherVertex.addEdge(this.edgeLabel, currentVertex, this.keyValues);
else
- return currentVertex.addEdge(this.edgeLabel, otherVertex, this.keyValues);
+ e = currentVertex.addEdge(this.edgeLabel, otherVertex, this.keyValues);
+
+ if (callbacks != null) {
+ final EdgeAddedEvent vae = new EdgeAddedEvent(DetachedFactory.detach(e, true));
+ callbacks.forEach(c -> c.accept(vae));
+ }
+
+ return e;
}
@Override
public Set<TraverserRequirement> getRequirements() {
return REQUIREMENTS;
}
+
+ @Override
+ public void addCallback(final EventCallback<EdgeAddedEvent> edgeAddedEventEventCallback) {
+ if (callbacks == null) callbacks = new ArrayList<>();
+ callbacks.add(edgeAddedEventEventCallback);
+ }
+
+ @Override
+ public void removeCallback(final EventCallback<EdgeAddedEvent> edgeAddedEventEventCallback) {
+ if (callbacks != null) callbacks.remove(edgeAddedEventEventCallback);
+ }
+
+ @Override
+ public void clearCallbacks() {
+ if (callbacks != null) callbacks.clear();
+ }
+
+ @Override
+ public List<EventCallback<EdgeAddedEvent>> getCallbacks() {
+ return (callbacks != null) ? Collections.unmodifiableList(callbacks) : Collections.emptyList();
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java
index e96c65f..7ddcf0b 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java
@@ -21,12 +21,18 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.map;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
import org.apache.tinkerpop.gremlin.process.traversal.step.Mutating;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.EdgeAddedEvent;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.EventCallback;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.VertexAddedEvent;
import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
import org.apache.tinkerpop.gremlin.structure.Direction;
import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedFactory;
import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.List;
@@ -34,8 +40,9 @@ import java.util.Set;
/**
* @author Marko A. Rodriguez (http://markorodriguez.com)
+ * @author Stephen Mallette (http://stephen.genoprime.com)
*/
-public final class AddEdgeStep extends FlatMapStep<Vertex, Edge> implements Mutating {
+public final class AddEdgeStep extends FlatMapStep<Vertex, Edge> implements Mutating<EventCallback<EdgeAddedEvent>> {
private static final Set<TraverserRequirement> REQUIREMENTS = EnumSet.of(TraverserRequirement.OBJECT);
@@ -44,6 +51,8 @@ public final class AddEdgeStep extends FlatMapStep<Vertex, Edge> implements Muta
private final List<Vertex> vertices;
private final Direction direction;
+ private List<EventCallback<EdgeAddedEvent>> callbacks = null;
+
public AddEdgeStep(final Traversal.Admin traversal, final Direction direction, final String edgeLabel, final Vertex vertex, final Object... keyValues) {
this(traversal, direction, edgeLabel, IteratorUtils.of(vertex), keyValues);
}
@@ -74,13 +83,43 @@ public final class AddEdgeStep extends FlatMapStep<Vertex, Edge> implements Muta
@Override
protected Iterator<Edge> flatMap(final Traverser.Admin<Vertex> traverser) {
- return IteratorUtils.map(this.vertices.iterator(), this.direction.equals(Direction.OUT) ?
- vertex -> traverser.get().addEdge(this.edgeLabel, vertex, this.keyValues) :
- vertex -> vertex.addEdge(this.edgeLabel, traverser.get(), this.keyValues));
+ return IteratorUtils.map(this.vertices.iterator(), vertex -> {
+ final Edge e = this.direction.equals(Direction.OUT) ?
+ traverser.get().addEdge(this.edgeLabel, vertex, this.keyValues) :
+ vertex.addEdge(this.edgeLabel, traverser.get(), this.keyValues);
+
+ if (callbacks != null) {
+ final EdgeAddedEvent vae = new EdgeAddedEvent(DetachedFactory.detach(e, true));
+ callbacks.forEach(c -> c.accept(vae));
+ }
+
+ return e;
+ });
}
@Override
public Set<TraverserRequirement> getRequirements() {
return REQUIREMENTS;
}
+
+ @Override
+ public void addCallback(final EventCallback<EdgeAddedEvent> edgeAddedEventEventCallback) {
+ if (callbacks == null) callbacks = new ArrayList<>();
+ callbacks.add(edgeAddedEventEventCallback);
+ }
+
+ @Override
+ public void removeCallback(final EventCallback<EdgeAddedEvent> edgeAddedEventEventCallback) {
+ if (callbacks != null) callbacks.remove(edgeAddedEventEventCallback);
+ }
+
+ @Override
+ public void clearCallbacks() {
+ if (callbacks != null) callbacks.clear();
+ }
+
+ @Override
+ public List<EventCallback<EdgeAddedEvent>> getCallbacks() {
+ return (callbacks != null) ? Collections.unmodifiableList(callbacks) : Collections.emptyList();
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStep.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStep.java
index 2f90ea1..92dadb0 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStep.java
@@ -23,15 +23,24 @@ import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
import org.apache.tinkerpop.gremlin.process.traversal.step.Mutating;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.AbstractStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.EventCallback;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.VertexAddedEvent;
import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedFactory;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
/**
* @author Marko A. Rodriguez (http://markorodriguez.com)
+ * @author Stephen Mallette (http://stephen.genoprime.com)
*/
-public final class AddVertexStartStep extends AbstractStep<Vertex, Vertex> implements Mutating {
+public final class AddVertexStartStep extends AbstractStep<Vertex, Vertex> implements Mutating<EventCallback<VertexAddedEvent>> {
private final Object[] keyValues;
private boolean first = true;
+ private List<EventCallback<VertexAddedEvent>> callbacks = null;
public AddVertexStartStep(final Traversal.Admin traversal, final Object... keyValues) {
super(traversal);
@@ -46,8 +55,35 @@ public final class AddVertexStartStep extends AbstractStep<Vertex, Vertex> imple
protected Traverser<Vertex> processNextStart() {
if (this.first) {
this.first = false;
- return this.getTraversal().getTraverserGenerator().generate(this.getTraversal().getGraph().get().addVertex(this.keyValues), this, 1l);
+ final Vertex v = this.getTraversal().getGraph().get().addVertex(this.keyValues);
+ if (callbacks != null) {
+ final VertexAddedEvent vae = new VertexAddedEvent(DetachedFactory.detach(v, true));
+ callbacks.forEach(c -> c.accept(vae));
+ }
+
+ return this.getTraversal().getTraverserGenerator().generate(v, this, 1l);
} else
throw FastNoSuchElementException.instance();
}
+
+ @Override
+ public void addCallback(final EventCallback<VertexAddedEvent> vertexAddedEventEventCallback) {
+ if (callbacks == null) callbacks = new ArrayList<>();
+ callbacks.add(vertexAddedEventEventCallback);
+ }
+
+ @Override
+ public void removeCallback(final EventCallback<VertexAddedEvent> vertexAddedEventEventCallback) {
+ if (callbacks != null) callbacks.remove(vertexAddedEventEventCallback);
+ }
+
+ @Override
+ public void clearCallbacks() {
+ if (callbacks != null) callbacks.clear();
+ }
+
+ @Override
+ public List<EventCallback<VertexAddedEvent>> getCallbacks() {
+ return (callbacks != null) ? Collections.unmodifiableList(callbacks) : Collections.emptyList();
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java
index 0632f87..8e05471 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java
@@ -21,17 +21,27 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.map;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
import org.apache.tinkerpop.gremlin.process.traversal.step.Mutating;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.EventCallback;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.VertexAddedEvent;
import org.apache.tinkerpop.gremlin.structure.Graph;
import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedFactory;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
/**
* @author Marko A. Rodriguez (http://markorodriguez.com)
+ * @author Stephen Mallette (http://stephen.genoprime.com)
*/
-public final class AddVertexStep<S> extends MapStep<S, Vertex> implements Mutating {
+public final class AddVertexStep<S> extends MapStep<S, Vertex> implements Mutating<EventCallback<VertexAddedEvent>> {
private final Object[] keyValues;
private final transient Graph graph;
+ private List<EventCallback<VertexAddedEvent>> callbacks = null;
+
public AddVertexStep(final Traversal.Admin traversal, final Object... keyValues) {
super(traversal);
this.keyValues = keyValues;
@@ -44,6 +54,32 @@ public final class AddVertexStep<S> extends MapStep<S, Vertex> implements Mutati
@Override
protected Vertex map(final Traverser.Admin<S> traverser) {
- return this.graph.addVertex(this.keyValues);
+ final Vertex v = this.graph.addVertex(this.keyValues);
+ if (callbacks != null) {
+ final VertexAddedEvent vae = new VertexAddedEvent(DetachedFactory.detach(v, true));
+ callbacks.forEach(c -> c.accept(vae));
+ }
+ return v;
+ }
+
+ @Override
+ public void addCallback(final EventCallback<VertexAddedEvent> vertexAddedEventEventCallback) {
+ if (callbacks == null) callbacks = new ArrayList<>();
+ callbacks.add(vertexAddedEventEventCallback);
+ }
+
+ @Override
+ public void removeCallback(final EventCallback<VertexAddedEvent> vertexAddedEventEventCallback) {
+ if (callbacks != null) callbacks.remove(vertexAddedEventEventCallback);
+ }
+
+ @Override
+ public void clearCallbacks() {
+ if (callbacks != null) callbacks.clear();
+ }
+
+ @Override
+ public List<EventCallback<VertexAddedEvent>> getCallbacks() {
+ return (callbacks != null) ? Collections.unmodifiableList(callbacks) : Collections.emptyList();
}
}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java
index 7d25dc2..2d98738 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java
@@ -20,18 +20,30 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Mutating;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.EdgePropertyChangedEvent;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.ElementPropertyChangedEvent;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.EventCallback;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.VertexPropertyChangedEvent;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.VertexPropertyPropertyChangedEvent;
import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
+import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.Element;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.apache.tinkerpop.gremlin.structure.Property;
+import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedFactory;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.EnumSet;
+import java.util.List;
import java.util.Set;
/**
* @author Marko A. Rodriguez (http://markorodriguez.com)
*/
-public final class AddPropertyStep<S extends Element> extends SideEffectStep<S> {
+public final class AddPropertyStep<S extends Element> extends SideEffectStep<S> implements Mutating<EventCallback<ElementPropertyChangedEvent>> {
private static final Set<TraverserRequirement> REQUIREMENTS = EnumSet.of(TraverserRequirement.OBJECT);
@@ -40,6 +52,7 @@ public final class AddPropertyStep<S extends Element> extends SideEffectStep<S>
private final Object value;
private final Object[] vertexPropertyKeyValues;
private final boolean asVertex;
+ private List<EventCallback<ElementPropertyChangedEvent>> callbacks = null;
public AddPropertyStep(final Traversal.Admin traversal, final VertexProperty.Cardinality cardinality, final String key, final Object value, final Object... vertexPropertyKeyValues) {
super(traversal);
@@ -56,14 +69,54 @@ public final class AddPropertyStep<S extends Element> extends SideEffectStep<S>
@Override
protected void sideEffect(final Traverser.Admin<S> traverser) {
- if (this.asVertex)
- ((Vertex) traverser.get()).property(this.cardinality, this.key, this.value, this.vertexPropertyKeyValues);
+ if (callbacks != null) {
+ final Element currentElement = traverser.get();
+ final Property currentProperty = traverser.get().property(key);
+ final boolean newProperty = asVertex ? currentProperty == VertexProperty.empty() : currentProperty == Property.empty();
+
+ ElementPropertyChangedEvent evt;
+ if (currentElement instanceof Vertex)
+ evt = new VertexPropertyChangedEvent(DetachedFactory.detach((Vertex) currentElement, true), newProperty ? null : DetachedFactory.detach((VertexProperty) currentProperty, true), value, vertexPropertyKeyValues);
+ else if (currentElement instanceof Edge)
+ evt = new EdgePropertyChangedEvent(DetachedFactory.detach((Edge) currentElement, true), newProperty ? null : DetachedFactory.detach(currentProperty), value);
+ else if (currentElement instanceof VertexProperty)
+ evt = new VertexPropertyPropertyChangedEvent(DetachedFactory.detach((VertexProperty) currentElement, true), newProperty ? null : DetachedFactory.detach(currentProperty), value);
+ else
+ throw new IllegalStateException(String.format("The incoming object cannot be processed by change eventing in %s: %s", AddPropertyStep.class.getName(), currentElement));
+
+ callbacks.forEach(c -> c.accept(evt));
+ }
+
+ if (asVertex)
+ ((Vertex) traverser.get()).property(cardinality, key, value, vertexPropertyKeyValues);
else
- traverser.get().property(this.key, this.value);
+ traverser.get().property(key, value);
}
@Override
public Set<TraverserRequirement> getRequirements() {
return REQUIREMENTS;
}
+
+
+ @Override
+ public void addCallback(final EventCallback<ElementPropertyChangedEvent> elementPropertyChangedEventCallback) {
+ if (callbacks == null) callbacks = new ArrayList<>();
+ callbacks.add(elementPropertyChangedEventCallback);
+ }
+
+ @Override
+ public void removeCallback(final EventCallback<ElementPropertyChangedEvent> elementPropertyChangedEventCallback) {
+ if (callbacks != null) callbacks.remove(elementPropertyChangedEventCallback);
+ }
+
+ @Override
+ public void clearCallbacks() {
+ if (callbacks != null) callbacks.clear();
+ }
+
+ @Override
+ public List<EventCallback<ElementPropertyChangedEvent>> getCallbacks() {
+ return (callbacks != null) ? Collections.unmodifiableList(callbacks) : Collections.emptyList();
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/ConsoleGraphChangedListener.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/ConsoleGraphChangedListener.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/ConsoleGraphChangedListener.java
new file mode 100644
index 0000000..ab0888c
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/ConsoleGraphChangedListener.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.step.util.event;
+
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.Property;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+
+/**
+ * An example listener that writes a message to the console for each event that fires from the graph.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class ConsoleGraphChangedListener implements GraphChangedListener {
+
+ private final Graph graph;
+
+ public ConsoleGraphChangedListener(final Graph graph) {
+ this.graph = graph;
+ }
+
+ @Override
+ public void vertexAdded(final Vertex vertex) {
+ System.out.println("Vertex [" + vertex.toString() + "] added to graph [" + graph.toString() + "]");
+ }
+
+ @Override
+ public void vertexRemoved(final Vertex vertex) {
+ System.out.println("Vertex [" + vertex.toString() + "] removed from graph [" + graph.toString() + "]");
+ }
+
+ @Override
+ public void vertexPropertyRemoved(final VertexProperty vertexProperty) {
+ System.out.println("Vertex Property [" + vertexProperty.toString() + "] removed from graph [" + graph.toString() + "]");
+ }
+
+ @Override
+ public void edgeAdded(final Edge edge) {
+ System.out.println("Edge [" + edge.toString() + "] added to graph [" + graph.toString() + "]");
+ }
+
+ @Override
+ public void edgeRemoved(final Edge edge) {
+ System.out.println("Edge [" + edge.toString() + "] removed from graph [" + graph.toString() + "]");
+ }
+
+ @Override
+ public void edgePropertyRemoved(final Edge element, final Property removedValue) {
+ System.out.println("Edge [" + element.toString() + "] property with value of [" + removedValue + "] removed in graph [" + graph.toString() + "]");
+ }
+
+ @Override
+ public void edgePropertyChanged(final Edge element, final Property oldValue, final Object setValue) {
+ System.out.println("Edge [" + element.toString() + "] property change value from [" + oldValue + "] to [" + setValue + "] in graph [" + graph.toString() + "]");
+ }
+
+ @Override
+ public void vertexPropertyPropertyChanged(final VertexProperty element, final Property oldValue, final Object setValue) {
+ System.out.println("VertexProperty [" + element.toString() + "] property change value from [" + oldValue + "] to [" + setValue + "] in graph [" + graph.toString() + "]");
+ }
+
+ @Override
+ public void vertexPropertyPropertyRemoved(final VertexProperty element, final Property oldValue) {
+ System.out.println("VertexProperty [" + element.toString() + "] property with value of [" + oldValue + "] removed in graph [" + graph.toString() + "]");
+ }
+
+ @Override
+ public void vertexPropertyChanged(final Vertex element, final Property oldValue, final Object setValue, final Object... vertexPropertyKeyValues) {
+ System.out.println("Vertex [" + element.toString() + "] property [" + oldValue + "] change to [" + setValue + "] in graph [" + graph.toString() + "]");
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EdgeAddedEvent.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EdgeAddedEvent.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EdgeAddedEvent.java
new file mode 100644
index 0000000..aecc110
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EdgeAddedEvent.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.step.util.event;
+
+import org.apache.tinkerpop.gremlin.structure.Edge;
+
+import java.util.Iterator;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class EdgeAddedEvent implements Event {
+
+ private final Edge edge;
+
+ public EdgeAddedEvent(final Edge edge) {
+ this.edge = edge;
+ }
+
+ @Override
+ public void fireEvent(final Iterator<GraphChangedListener> eventListeners) {
+ while (eventListeners.hasNext()) {
+ eventListeners.next().edgeAdded(edge);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EdgePropertyChangedEvent.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EdgePropertyChangedEvent.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EdgePropertyChangedEvent.java
new file mode 100644
index 0000000..c5c36b6
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EdgePropertyChangedEvent.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.step.util.event;
+
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Element;
+import org.apache.tinkerpop.gremlin.structure.Property;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class EdgePropertyChangedEvent extends ElementPropertyChangedEvent {
+
+ public EdgePropertyChangedEvent(final Edge edge, final Property oldValue, final Object newValue) {
+ super(edge, oldValue, newValue);
+ }
+
+ @Override
+ void fire(final GraphChangedListener listener, final Element element, final Property oldValue, final Object newValue, final Object... vertexPropertyKeyValues) {
+ listener.edgePropertyChanged((Edge) element, oldValue, newValue);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EdgePropertyRemovedEvent.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EdgePropertyRemovedEvent.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EdgePropertyRemovedEvent.java
new file mode 100644
index 0000000..e8769fc
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EdgePropertyRemovedEvent.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.step.util.event;
+
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Element;
+import org.apache.tinkerpop.gremlin.structure.Property;
+
+/**
+ * Event fired when an edge property is removed.
+ *
+ * @author Stephen Mallette
+ */
+public class EdgePropertyRemovedEvent extends ElementPropertyEvent {
+
+ public EdgePropertyRemovedEvent(final Edge element, final Property removed) {
+ super(element, removed, null);
+ }
+
+ @Override
+ void fire(final GraphChangedListener listener, final Element element, final Property oldValue, final Object newValue, final Object... vertexPropertyKeyValues) {
+ listener.edgePropertyRemoved((Edge) element, oldValue);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EdgeRemovedEvent.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EdgeRemovedEvent.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EdgeRemovedEvent.java
new file mode 100644
index 0000000..6ba442a
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EdgeRemovedEvent.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.step.util.event;
+
+import org.apache.tinkerpop.gremlin.structure.Edge;
+
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Event fired when an edge is removed.
+ *
+ * @author Stephen Mallette
+ */
+public class EdgeRemovedEvent implements Event {
+
+ private final Edge edge;
+
+ public EdgeRemovedEvent(final Edge edge) {
+ this.edge = edge;
+ }
+
+ @Override
+ public void fireEvent(final Iterator<GraphChangedListener> eventListeners) {
+ while (eventListeners.hasNext()) {
+ eventListeners.next().edgeRemoved(edge);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/ElementPropertyChangedEvent.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/ElementPropertyChangedEvent.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/ElementPropertyChangedEvent.java
new file mode 100644
index 0000000..b56cbec
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/ElementPropertyChangedEvent.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.step.util.event;
+
+import org.apache.tinkerpop.gremlin.structure.Element;
+import org.apache.tinkerpop.gremlin.structure.Property;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public abstract class ElementPropertyChangedEvent extends ElementPropertyEvent {
+
+ public ElementPropertyChangedEvent(final Element element, final Property oldValue, final Object newValue, final Object... vertexPropertyKeyValues) {
+ super(element, oldValue, newValue, vertexPropertyKeyValues);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/ElementPropertyEvent.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/ElementPropertyEvent.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/ElementPropertyEvent.java
new file mode 100644
index 0000000..de3cbb9
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/ElementPropertyEvent.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.step.util.event;
+
+import org.apache.tinkerpop.gremlin.structure.Element;
+import org.apache.tinkerpop.gremlin.structure.Property;
+
+import java.util.Iterator;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public abstract class ElementPropertyEvent implements Event {
+
+ private final Element element;
+ private final Property oldValue;
+ private final Object newValue;
+ private final Object[] vertexPropertyKeyValues;
+
+ public ElementPropertyEvent(final Element element, final Property oldValue, final Object newValue, final Object... vertexPropertyKeyValues) {
+ this.element = element;
+ this.oldValue = oldValue;
+ this.newValue = newValue;
+ this.vertexPropertyKeyValues = vertexPropertyKeyValues;
+ }
+
+ abstract void fire(final GraphChangedListener listener, final Element element, final Property oldValue, final Object newValue, final Object... vertexPropertyKeyValues);
+
+ @Override
+ public void fireEvent(final Iterator<GraphChangedListener> eventListeners) {
+ while (eventListeners.hasNext()) {
+ fire(eventListeners.next(), element, oldValue, newValue, vertexPropertyKeyValues);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/Event.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/Event.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/Event.java
new file mode 100644
index 0000000..24c54c5
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/Event.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.step.util.event;
+
+import java.util.Iterator;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public interface Event {
+
+ public void fireEvent(final Iterator<GraphChangedListener> eventListeners);
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EventCallback.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EventCallback.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EventCallback.java
new file mode 100644
index 0000000..b0b9d6f
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EventCallback.java
@@ -0,0 +1,10 @@
+package org.apache.tinkerpop.gremlin.process.traversal.step.util.event;
+
+import java.util.function.Consumer;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+@FunctionalInterface
+public interface EventCallback<E extends Event> extends Consumer<E> {
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/GraphChangedListener.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/GraphChangedListener.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/GraphChangedListener.java
new file mode 100644
index 0000000..2c51990
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/GraphChangedListener.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.step.util.event;
+
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Element;
+import org.apache.tinkerpop.gremlin.structure.Property;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+
+/**
+ * Interface for a listener to {@link org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.EventStrategy}
+ * change events.
+ *
+ * Implementations of this interface should be added to the list of listeners on the addListener method on
+ * the {@link org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.EventStrategy}.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public interface GraphChangedListener {
+
+ /**
+ * Raised when a new {@link Vertex} is added.
+ *
+ * @param vertex the {@link Vertex} that was added
+ */
+ public void vertexAdded(final Vertex vertex);
+
+ /**
+ * Raised after a {@link Vertex} was removed from the graph.
+ *
+ * @param vertex the {@link Vertex} that was removed
+ */
+ public void vertexRemoved(final Vertex vertex);
+
+ /**
+ * Raised after the property of a {@link Vertex} changed.
+ *
+ * @param element the {@link Vertex} that changed
+ * @param setValue the new value of the property
+ */
+ public void vertexPropertyChanged(final Vertex element, final Property oldValue, final Object setValue, final Object... vertexPropertyKeyValues);
+
+ /**
+ * Raised after a {@link VertexProperty} was removed from the graph.
+ *
+ * @param vertexProperty the {@link VertexProperty} that was removed
+ */
+ public void vertexPropertyRemoved(final VertexProperty vertexProperty);
+
+ /**
+ * Raised after a new {@link Edge} is added.
+ *
+ * @param edge the {@link Edge} that was added
+ */
+ public void edgeAdded(final Edge edge);
+
+ /**
+ * Raised after an {@link Edge} was removed from the graph.
+ *
+ * @param edge the {@link Edge} that was removed.
+ */
+ public void edgeRemoved(final Edge edge);
+
+ /**
+ * Raised after the property of a {@link Edge} changed.
+ *
+ * @param element the {@link Edge} that changed
+ * @param setValue the new value of the property
+ */
+ public void edgePropertyChanged(final Edge element, final Property oldValue, final Object setValue);
+
+ /**
+ * Raised after an {@link Property} property was removed from an {@link Edge}.
+ *
+ * @param property the {@link Property} that was removed
+ */
+ public void edgePropertyRemoved(final Edge element, final Property property);
+
+ /**
+ * Raised after the property of a {@link VertexProperty} changed.
+ *
+ * @param element the {@link VertexProperty} that changed
+ * @param setValue the new value of the property
+ */
+ public void vertexPropertyPropertyChanged(final VertexProperty element, final Property oldValue, final Object setValue);
+
+ /**
+ * Raised after an {@link Property} property was removed from a {@link VertexProperty}.
+ *
+ * @param property the {@link Property} that removed
+ */
+ public void vertexPropertyPropertyRemoved(final VertexProperty element, final Property property);
+}
+
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexAddedEvent.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexAddedEvent.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexAddedEvent.java
new file mode 100644
index 0000000..4e5917b
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexAddedEvent.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.step.util.event;
+
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+
+import java.util.Iterator;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class VertexAddedEvent implements Event {
+
+ private final Vertex vertex;
+
+ public VertexAddedEvent(final Vertex vertex) {
+ this.vertex = vertex;
+ }
+
+ @Override
+ public void fireEvent(final Iterator<GraphChangedListener> eventListeners) {
+ while (eventListeners.hasNext()) {
+ eventListeners.next().vertexAdded(vertex);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexPropertyChangedEvent.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexPropertyChangedEvent.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexPropertyChangedEvent.java
new file mode 100644
index 0000000..0084fec
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexPropertyChangedEvent.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.step.util.event;
+
+import org.apache.tinkerpop.gremlin.structure.Element;
+import org.apache.tinkerpop.gremlin.structure.Property;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class VertexPropertyChangedEvent extends ElementPropertyChangedEvent {
+
+ public VertexPropertyChangedEvent(final Vertex element, final Property oldValue, final Object newValue, final Object... vertexPropertyKeyValues) {
+ super(element, oldValue, newValue, vertexPropertyKeyValues);
+ }
+
+ @Override
+ void fire(final GraphChangedListener listener, final Element element, final Property oldValue, final Object newValue, final Object... vertexPropertyKeyValues) {
+ listener.vertexPropertyChanged((Vertex) element, oldValue, newValue, vertexPropertyKeyValues);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexPropertyPropertyChangedEvent.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexPropertyPropertyChangedEvent.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexPropertyPropertyChangedEvent.java
new file mode 100644
index 0000000..6b9b712
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexPropertyPropertyChangedEvent.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.step.util.event;
+
+import org.apache.tinkerpop.gremlin.structure.Element;
+import org.apache.tinkerpop.gremlin.structure.Property;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class VertexPropertyPropertyChangedEvent extends ElementPropertyChangedEvent {
+
+ public VertexPropertyPropertyChangedEvent(final VertexProperty element, final Property oldValue, final Object newValue) {
+ super(element, oldValue, newValue);
+ }
+
+ @Override
+ void fire(final GraphChangedListener listener, final Element element, final Property oldValue, final Object newValue, final Object... vertexPropertyKeyValues) {
+ listener.vertexPropertyPropertyChanged((VertexProperty) element, oldValue, newValue);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexPropertyPropertyRemovedEvent.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexPropertyPropertyRemovedEvent.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexPropertyPropertyRemovedEvent.java
new file mode 100644
index 0000000..31f7ae3
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexPropertyPropertyRemovedEvent.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.step.util.event;
+
+import org.apache.tinkerpop.gremlin.structure.Element;
+import org.apache.tinkerpop.gremlin.structure.Property;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class VertexPropertyPropertyRemovedEvent extends ElementPropertyEvent {
+
+ public VertexPropertyPropertyRemovedEvent(final VertexProperty element, final Property removed) {
+ super(element, removed, null);
+ }
+
+ @Override
+ void fire(final GraphChangedListener listener, final Element element, final Property oldValue, final Object newValue, final Object... vertexPropertyKeyValues) {
+ listener.vertexPropertyPropertyRemoved((VertexProperty) element, oldValue);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexPropertyRemovedEvent.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexPropertyRemovedEvent.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexPropertyRemovedEvent.java
new file mode 100644
index 0000000..8ffdef4
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexPropertyRemovedEvent.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.step.util.event;
+
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+
+import java.util.Iterator;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class VertexPropertyRemovedEvent implements Event {
+
+ private final VertexProperty vertexProperty;
+
+ public VertexPropertyRemovedEvent(final VertexProperty vertexProperty) {
+ this.vertexProperty = vertexProperty;
+ }
+
+ @Override
+ public void fireEvent(final Iterator<GraphChangedListener> eventListeners) {
+ while (eventListeners.hasNext()) {
+ eventListeners.next().vertexPropertyRemoved(vertexProperty);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexRemovedEvent.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexRemovedEvent.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexRemovedEvent.java
new file mode 100644
index 0000000..2d691f8
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexRemovedEvent.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.step.util.event;
+
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class VertexRemovedEvent implements Event {
+
+ private final Vertex vertex;
+
+ public VertexRemovedEvent(final Vertex vertex) {
+ this.vertex = vertex;
+ }
+
+ @Override
+ public void fireEvent(final Iterator<GraphChangedListener> eventListeners) {
+ while (eventListeners.hasNext()) {
+ eventListeners.next().vertexRemoved(vertex);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/EventStrategy.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/EventStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/EventStrategy.java
new file mode 100644
index 0000000..acebadb
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/EventStrategy.java
@@ -0,0 +1,151 @@
+package org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Mutating;
+import org.apache.tinkerpop.gremlin.process.traversal.step.filter.DropStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddEdgeByPathStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddEdgeStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddVertexStartStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddVertexStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.AddPropertyStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.Event;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.EventCallback;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.GraphChangedListener;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
+import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.Transaction;
+
+import java.io.Serializable;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Deque;
+import java.util.List;
+
+/**
+ * A strategy that raises events when {@link org.apache.tinkerpop.gremlin.process.traversal.step.Mutating} steps are
+ * encountered and successfully executed.
+ * <br/>
+ * Note that this implementation requires a {@link Graph} on the {@link Traversal} instance. If that is not present
+ * an {@link java.lang.IllegalStateException} will be thrown.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class EventStrategy extends AbstractTraversalStrategy {
+ private final List<GraphChangedListener> listeners = new ArrayList<>();
+
+ private EventStrategy(final GraphChangedListener... listeners) {
+ this.listeners.addAll(Arrays.asList(listeners));
+ }
+
+ @Override
+ public void apply(final Traversal.Admin<?, ?> traversal) {
+ // EventStrategy requires access to both graph features and the current transaction object which are
+ // both part of Graph - if that isn't present, this strategy doesn't work.
+ if (!traversal.getGraph().isPresent())
+ throw new IllegalStateException(String.format("%s requires a graph instance is present on the traversal", EventStrategy.class.getName()));
+
+ final EventStrategyCallback callback = new EventStrategyCallback(new EventTrigger(traversal.getGraph().get()));
+ final List<Mutating> mutators = new ArrayList<>();
+ mutators.addAll(TraversalHelper.getStepsOfAssignableClass(AddVertexStep.class, traversal));
+ mutators.addAll(TraversalHelper.getStepsOfAssignableClass(AddVertexStartStep.class, traversal));
+ mutators.addAll(TraversalHelper.getStepsOfAssignableClass(AddEdgeStep.class, traversal));
+ mutators.addAll(TraversalHelper.getStepsOfAssignableClass(AddEdgeByPathStep.class, traversal));
+ mutators.addAll(TraversalHelper.getStepsOfAssignableClass(AddPropertyStep.class, traversal));
+ mutators.addAll(TraversalHelper.getStepsOfAssignableClass(DropStep.class, traversal));
+ mutators.forEach(s -> s.addCallback(callback));
+ }
+
+ public static Builder build() {
+ return new Builder();
+ }
+
+ public class EventStrategyCallback implements EventCallback<Event>, Serializable {
+ private final EventTrigger trigger;
+
+ public EventStrategyCallback(final EventTrigger trigger) {
+ this.trigger = trigger;
+ }
+
+ @Override
+ public void accept(final Event event) {
+ trigger.addEvent(event);
+ }
+ }
+
+ public static class Builder {
+ private final List<GraphChangedListener> listeners = new ArrayList<>();
+
+ Builder() {}
+
+ public Builder addListener(GraphChangedListener listener) {
+ this.listeners.add(listener);
+ return this;
+ }
+
+ public EventStrategy create() {
+ return new EventStrategy(this.listeners.toArray(new GraphChangedListener[this.listeners.size()]));
+ }
+ }
+
+ class EventTrigger {
+
+ /**
+ * A queue of events that are triggered by change to the graph. The queue builds up until the trigger fires them
+ * in the order they were received.
+ */
+ private final ThreadLocal<Deque<Event>> eventQueue = new ThreadLocal<Deque<Event>>() {
+ protected Deque<Event> initialValue() {
+ return new ArrayDeque<>();
+ }
+ };
+
+ /**
+ * When set to true, events in the event queue will only be fired when a transaction is committed.
+ */
+ private final boolean enqueueEvents;
+
+ public EventTrigger(final Graph graph) {
+ enqueueEvents = graph.features().graph().supportsTransactions();
+
+ if (enqueueEvents) {
+ // since this is a transactional graph events are enqueued so the events should be fired/reset only after
+ // transaction is committed/rolled back as tied to a graph transaction
+ graph.tx().addTransactionListener(status -> {
+ if (status == Transaction.Status.COMMIT)
+ fireEventQueue();
+ else if (status == Transaction.Status.ROLLBACK)
+ resetEventQueue();
+ else
+ throw new RuntimeException(String.format("The %s is not aware of this status: %s", EventTrigger.class.getName(), status));
+ });
+ }
+ }
+
+ /**
+ * Add an event to the event queue. If this is a non-transactional graph, then the queue fires and resets after
+ * each event is added.
+ */
+ public void addEvent(final Event evt) {
+ eventQueue.get().add(evt);
+
+ if (!this.enqueueEvents) {
+ fireEventQueue();
+ resetEventQueue();
+ }
+ }
+
+ private void resetEventQueue() {
+ eventQueue.set(new ArrayDeque<>());
+ }
+
+ private void fireEventQueue() {
+ final Deque<Event> deque = eventQueue.get();
+ for (Event event = deque.pollFirst(); event != null; event = deque.pollFirst()) {
+ event.fireEvent(listeners.iterator());
+ }
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/empty/EmptyGraph.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/empty/EmptyGraph.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/empty/EmptyGraph.java
index 98d43eb..272c709 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/empty/EmptyGraph.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/empty/EmptyGraph.java
@@ -45,6 +45,21 @@ public final class EmptyGraph implements Graph {
}
@Override
+ public Features features() {
+ return new Features() {
+ @Override
+ public GraphFeatures graph() {
+ return new GraphFeatures() {
+ @Override
+ public boolean supportsTransactions() {
+ return false;
+ }
+ };
+ }
+ };
+ }
+
+ @Override
public Vertex addVertex(final Object... keyValues) {
throw Exceptions.vertexAdditionsNotSupported();
}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/EventStrategyTest.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/EventStrategyTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/EventStrategyTest.java
new file mode 100644
index 0000000..ece5d2c
--- /dev/null
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/EventStrategyTest.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Mutating;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.ConsoleGraphChangedListener;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.GraphChangedListener;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+@RunWith(Parameterized.class)
+public class EventStrategyTest {
+ @Parameterized.Parameters(name = "{0}")
+ public static Iterable<Object[]> data() {
+ return Arrays.asList(new Object[][]{
+ {"addInE()", __.addInE("test", "x"), 1},
+ {"addInE(args)", __.addInE("test", "x", "this", "that"), 1},
+ {"addOutE()", __.addOutE("test", "x"), 1},
+ {"addOutE(args)", __.addOutE("test", "x", "this", "that"), 1},
+ {"addE(IN)", __.addE(Direction.IN, "test", "test"), 1},
+ {"addE(IN,args)", __.addE(Direction.IN, "test", "test", "this", "that"), 1},
+ {"addE(OUT)", __.addE(Direction.OUT, "test", "test"), 1},
+ {"addE(OUT,args)", __.addE(Direction.OUT, "test", "test", "this", "that"), 1},
+ {"addV()", __.addV(), 1},
+ {"addV(args)", __.addV("test", "this"), 1},
+ {"addV().property(k,v)", __.addV().property("test", "that"), 2},
+ {"properties().drop()", __.properties().drop(), 1},
+ {"properties(k).drop()", __.properties("test").drop(), 1},
+ {"out().drop()", __.out().drop(), 1},
+ {"out(args).drop()", __.out("test").drop(), 1},
+ {"outE().drop()", __.outE().drop(), 1},
+ {"outE().properties().drop()", __.outE().properties().drop(), 1},
+ {"outE(args).drop()", __.outE("test").drop(), 1}});
+ }
+
+ @Parameterized.Parameter(value = 0)
+ public String name;
+
+ @Parameterized.Parameter(value = 1)
+ public Traversal traversal;
+
+ @Parameterized.Parameter(value = 2)
+ public int expectedMutatingStepsFound;
+
+ @Test
+ public void shouldEventOnMutatingSteps() {
+ final GraphChangedListener listener1 = new ConsoleGraphChangedListener(EmptyGraph.instance());
+ final EventStrategy eventStrategy = EventStrategy.build()
+ .addListener(listener1).create();
+
+ eventStrategy.apply(traversal.asAdmin());
+
+ final AtomicInteger mutatingStepsFound = new AtomicInteger(0);
+ traversal.asAdmin().getSteps().stream()
+ .filter(s -> s instanceof Mutating)
+ .forEach(s -> {
+ final Mutating mutating = (Mutating) s;
+ assertEquals(1, mutating.getCallbacks().size());
+ mutatingStepsFound.incrementAndGet();
+ });
+
+ assertEquals(expectedMutatingStepsFound, mutatingStepsFound.get());
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessStandardSuite.java
----------------------------------------------------------------------
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessStandardSuite.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessStandardSuite.java
index 348ddf6..7d6ee8a 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessStandardSuite.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessStandardSuite.java
@@ -74,6 +74,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.StoreTest;
import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.SubgraphTest;
import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.TreeTest;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.PathTest;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.EventStrategyProcessTest;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.PartitionStrategyProcessTest;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.ReadOnlyStrategyProcessTest;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SubgraphStrategyProcessTest;
@@ -191,6 +192,7 @@ public class ProcessStandardSuite extends AbstractGremlinSuite {
// PageRankVertexProgramTest.class
// decorations
+ EventStrategyProcessTest.class,
ReadOnlyStrategyProcessTest.class,
PartitionStrategyProcessTest.class,
SubgraphStrategyProcessTest.class