package com.tinkerpop.blueprints.util.wrappers.event;
import com.tinkerpop.blueprints.Edge;
import com.tinkerpop.blueprints.Features;
import com.tinkerpop.blueprints.Graph;
import com.tinkerpop.blueprints.GraphQuery;
import com.tinkerpop.blueprints.Vertex;
import com.tinkerpop.blueprints.util.ElementHelper;
import com.tinkerpop.blueprints.util.StringFactory;
import com.tinkerpop.blueprints.util.wrappers.WrappedGraphQuery;
import com.tinkerpop.blueprints.util.wrappers.WrapperGraph;
import com.tinkerpop.blueprints.util.wrappers.event.listener.EdgeAddedEvent;
import com.tinkerpop.blueprints.util.wrappers.event.listener.EdgeRemovedEvent;
import com.tinkerpop.blueprints.util.wrappers.event.listener.GraphChangedListener;
import com.tinkerpop.blueprints.util.wrappers.event.listener.VertexAddedEvent;
import com.tinkerpop.blueprints.util.wrappers.event.listener.VertexRemovedEvent;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* An EventGraph is a wrapper to existing Graph implementations and provides for graph events to be raised
* to one or more listeners on changes to the Graph. Notifications to the listeners occur for the
* following events: new vertex/edge, vertex/edge property changed, vertex/edge property removed,
* vertex/edge removed.
*
* The limiting factor to events being raised is related to out-of-process functions changing graph elements.
*
* To gather events from EventGraph, simply provide an implementation of the {@link GraphChangedListener} to
* the EventGraph by utilizing the addListener method. EventGraph allows the addition of multiple GraphChangedListener
* implementations. Each listener will be notified in the order that it was added.
*
* @author Stephen Mallette
*/
public class EventGraph<T extends Graph> implements Graph, WrapperGraph<T> {
protected EventTrigger trigger;
protected final T baseGraph;
protected final List<GraphChangedListener> graphChangedListeners = new ArrayList<GraphChangedListener>();
private final Features features;
public EventGraph(final T baseGraph) {
this.baseGraph = baseGraph;
this.features = this.baseGraph.getFeatures().copyFeatures();
this.features.isWrapper = true;
this.trigger = new EventTrigger(this, false);
}
public void removeAllListeners() {
this.graphChangedListeners.clear();
}
public void addListener(final GraphChangedListener listener) {
this.graphChangedListeners.add(listener);
}
public Iterator<GraphChangedListener> getListenerIterator() {
return this.graphChangedListeners.iterator();
}
public EventTrigger getTrigger() {
return this.trigger;
}
public void removeListener(final GraphChangedListener listener) {
this.graphChangedListeners.remove(listener);
}
protected void onVertexAdded(Vertex vertex) {
this.trigger.addEvent(new VertexAddedEvent(vertex));
}
protected void onVertexRemoved(final Vertex vertex, Map<String, Object> props) {
this.trigger.addEvent(new VertexRemovedEvent(vertex, props));
}
protected void onEdgeAdded(Edge edge) {
this.trigger.addEvent(new EdgeAddedEvent(edge));
}
protected void onEdgeRemoved(final Edge edge, Map<String, Object> props) {
this.trigger.addEvent(new EdgeRemovedEvent(edge, props));
}
/**
* Raises a vertexAdded event.
*/
public Vertex addVertex(final Object id) {
final Vertex vertex = this.baseGraph.addVertex(id);
if (vertex == null) {
return null;
} else {
this.onVertexAdded(vertex);
return new EventVertex(vertex, this);
}
}
public Vertex getVertex(final Object id) {
final Vertex vertex = this.baseGraph.getVertex(id);
if (vertex == null) {
return null;
} else {
return new EventVertex(vertex, this);
}
}
/**
* Raises a vertexRemoved event.
*/
public void removeVertex(final Vertex vertex) {
Vertex vertexToRemove = vertex;
if (vertex instanceof EventVertex) {
vertexToRemove = ((EventVertex) vertex).getBaseVertex();
}
Map<String, Object> props = ElementHelper.getProperties(vertex);
this.baseGraph.removeVertex(vertexToRemove);
this.onVertexRemoved(vertex, props);
}
public Iterable<Vertex> getVertices() {
return new EventVertexIterable(this.baseGraph.getVertices(), this);
}
public Iterable<Vertex> getVertices(final String key, final Object value) {
return new EventVertexIterable(this.baseGraph.getVertices(key, value), this);
}
/**
* Raises an edgeAdded event.
*/
public Edge addEdge(final Object id, final Vertex outVertex, final Vertex inVertex, final String label) {
Vertex outVertexToSet = outVertex;
if (outVertex instanceof EventVertex) {
outVertexToSet = ((EventVertex) outVertex).getBaseVertex();
}
Vertex inVertexToSet = inVertex;
if (inVertex instanceof EventVertex) {
inVertexToSet = ((EventVertex) inVertex).getBaseVertex();
}
final Edge edge = this.baseGraph.addEdge(id, outVertexToSet, inVertexToSet, label);
if (edge == null) {
return null;
} else {
this.onEdgeAdded(edge);
return new EventEdge(edge, this);
}
}
public Edge getEdge(final Object id) {
final Edge edge = this.baseGraph.getEdge(id);
if (edge == null) {
return null;
} else {
return new EventEdge(edge, this);
}
}
/**
* Raises an edgeRemoved event.
*/
public void removeEdge(final Edge edge) {
Edge edgeToRemove = edge;
if (edge instanceof EventEdge) {
edgeToRemove = ((EventEdge) edge).getBaseEdge();
}
Map<String, Object> props = ElementHelper.getProperties(edge);
this.baseGraph.removeEdge(edgeToRemove);
this.onEdgeRemoved(edge, props);
}
public Iterable<Edge> getEdges() {
return new EventEdgeIterable(this.baseGraph.getEdges(), this);
}
public Iterable<Edge> getEdges(final String key, final Object value) {
return new EventEdgeIterable(this.baseGraph.getEdges(key, value), this);
}
public GraphQuery query() {
final EventGraph eventGraph = this;
return new WrappedGraphQuery(this.baseGraph.query()) {
@Override
public Iterable<Edge> edges() {
return new EventEdgeIterable(this.query.edges(), eventGraph);
}
@Override
public Iterable<Vertex> vertices() {
return new EventVertexIterable(this.query.vertices(), eventGraph);
}
};
}
public void shutdown() {
try {
this.baseGraph.shutdown();
// TODO: hmmmmmm??
this.trigger.fireEventQueue();
this.trigger.resetEventQueue();
} catch (Exception re) {
}
}
public String toString() {
return StringFactory.graphString(this, this.baseGraph.toString());
}
@Override
public T getBaseGraph() {
return this.baseGraph;
}
public Features getFeatures() {
return this.features;
}
}