/* ==========================================
* JGraphT : a free Java graph-theory library
* ==========================================
*
* Project Info: http://jgrapht.sourceforge.net/
* Project Creator: Barak Naveh (http://sourceforge.net/users/barak_naveh)
*
* (C) Copyright 2003-2008, by Barak Naveh and Contributors.
*
* This program and the accompanying materials are dual-licensed under
* either
*
* (a) the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation, or (at your option) any
* later version.
*
* or (per the licensee's choosing)
*
* (b) the terms of the Eclipse Public License v1.0 as published by
* the Eclipse Foundation.
*/
/* ---------------------------
* DefaultListenableGraph.java
* ---------------------------
* (C) Copyright 2003-2008, by Barak Naveh and Contributors.
*
* Original Author: Barak Naveh
* Contributor(s): Christian Hammer
*
* $Id$
*
* Changes
* -------
* 24-Jul-2003 : Initial revision (BN);
* 04-Aug-2003 : Strong refs to listeners instead of weak refs (BN);
* 10-Aug-2003 : Adaptation to new event model (BN);
* 07-Mar-2004 : Fixed unnecessary clone bug #819075 (BN);
* 11-Mar-2004 : Made generic (CH);
* 07-May-2006 : Changed from List<Edge> to Set<Edge> (JVS);
* 28-May-2006 : Moved connectivity info from edge to graph (JVS);
*
*/
package org.jgrapht.graph;
import java.util.*;
import org.jgrapht.*;
import org.jgrapht.event.*;
import org.jgrapht.util.*;
/**
* A graph backed by the the graph specified at the constructor, which can be
* listened by <code>GraphListener</code> s and by <code>
* VertexSetListener</code> s. Operations on this graph "pass through" to the to
* the backing graph. Any modification made to this graph or the backing graph
* is reflected by the other.
*
* <p>This graph does <i>not</i> pass the hashCode and equals operations through
* to the backing graph, but relies on <tt>Object</tt>'s <tt>equals</tt> and
* <tt>hashCode</tt> methods.</p>
*
* @author Barak Naveh
* @see GraphListener
* @see VertexSetListener
* @since Jul 20, 2003
*/
public class DefaultListenableGraph<V, E>
extends GraphDelegator<V, E>
implements ListenableGraph<V, E>,
Cloneable
{
private static final long serialVersionUID = 3977575900898471984L;
private List<GraphListener<V, E>> graphListeners =
new ArrayList<GraphListener<V, E>>();
private List<VertexSetListener<V>> vertexSetListeners =
new ArrayList<VertexSetListener<V>>();
private FlyweightEdgeEvent<V, E> reuseableEdgeEvent;
private FlyweightVertexEvent<V> reuseableVertexEvent;
private boolean reuseEvents;
/**
* Creates a new listenable graph.
*
* @param g the backing graph.
*/
public DefaultListenableGraph(Graph<V, E> g)
{
this(g, false);
}
/**
* Creates a new listenable graph. If the <code>reuseEvents</code> flag is
* set to <code>true</code> this class will reuse previously fired events
* and will not create a new object for each event. This option increases
* performance but should be used with care, especially in multithreaded
* environment.
*
* @param g the backing graph.
* @param reuseEvents whether to reuse previously fired event objects
* instead of creating a new event object for each event.
*
* @throws IllegalArgumentException if the backing graph is already a
* listenable graph.
*/
public DefaultListenableGraph(Graph<V, E> g, boolean reuseEvents)
{
super(g);
this.reuseEvents = reuseEvents;
reuseableEdgeEvent = new FlyweightEdgeEvent<V, E>(this, -1, null);
reuseableVertexEvent = new FlyweightVertexEvent<V>(this, -1, null);
// the following restriction could be probably relaxed in the future.
if (g instanceof ListenableGraph<?, ?>) {
throw new IllegalArgumentException(
"base graph cannot be listenable");
}
}
/**
* If the <code>reuseEvents</code> flag is set to <code>true</code> this
* class will reuse previously fired events and will not create a new object
* for each event. This option increases performance but should be used with
* care, especially in multithreaded environment.
*
* @param reuseEvents whether to reuse previously fired event objects
* instead of creating a new event object for each event.
*/
public void setReuseEvents(boolean reuseEvents)
{
this.reuseEvents = reuseEvents;
}
/**
* Tests whether the <code>reuseEvents</code> flag is set. If the flag is
* set to <code>true</code> this class will reuse previously fired events
* and will not create a new object for each event. This option increases
* performance but should be used with care, especially in multithreaded
* environment.
*
* @return the value of the <code>reuseEvents</code> flag.
*/
public boolean isReuseEvents()
{
return reuseEvents;
}
/**
* @see Graph#addEdge(Object, Object)
*/
public E addEdge(V sourceVertex, V targetVertex)
{
E e = super.addEdge(sourceVertex, targetVertex);
if (e != null) {
fireEdgeAdded(e, sourceVertex, targetVertex);
}
return e;
}
/**
* @see Graph#addEdge(Object, Object, Object)
*/
public boolean addEdge(V sourceVertex, V targetVertex, E e)
{
boolean added = super.addEdge(sourceVertex, targetVertex, e);
if (added) {
fireEdgeAdded(e, sourceVertex, targetVertex);
}
return added;
}
/**
* @see ListenableGraph#addGraphListener(GraphListener)
*/
public void addGraphListener(GraphListener<V, E> l)
{
addToListenerList(graphListeners, l);
}
/**
* @see Graph#addVertex(Object)
*/
public boolean addVertex(V v)
{
boolean modified = super.addVertex(v);
if (modified) {
fireVertexAdded(v);
}
return modified;
}
/**
* @see ListenableGraph#addVertexSetListener(VertexSetListener)
*/
public void addVertexSetListener(VertexSetListener<V> l)
{
addToListenerList(vertexSetListeners, l);
}
/**
* @see java.lang.Object#clone()
*/
public Object clone()
{
try {
TypeUtil<DefaultListenableGraph<V, E>> typeDecl = null;
DefaultListenableGraph<V, E> g =
TypeUtil.uncheckedCast(super.clone(), typeDecl);
g.graphListeners = new ArrayList<GraphListener<V, E>>();
g.vertexSetListeners = new ArrayList<VertexSetListener<V>>();
return g;
} catch (CloneNotSupportedException e) {
// should never get here since we're Cloneable
e.printStackTrace();
throw new RuntimeException("internal error");
}
}
/**
* @see Graph#removeEdge(Object, Object)
*/
public E removeEdge(V sourceVertex, V targetVertex)
{
E e = super.removeEdge(sourceVertex, targetVertex);
if (e != null) {
fireEdgeRemoved(e, sourceVertex, targetVertex);
}
return e;
}
/**
* @see Graph#removeEdge(Object)
*/
public boolean removeEdge(E e)
{
V sourceVertex = getEdgeSource(e);
V targetVertex = getEdgeTarget(e);
boolean modified = super.removeEdge(e);
if (modified) {
fireEdgeRemoved(e, sourceVertex, targetVertex);
}
return modified;
}
/**
* @see ListenableGraph#removeGraphListener(GraphListener)
*/
public void removeGraphListener(GraphListener<V, E> l)
{
graphListeners.remove(l);
}
/**
* @see Graph#removeVertex(Object)
*/
public boolean removeVertex(V v)
{
if (containsVertex(v)) {
Set<E> touchingEdgesList = edgesOf(v);
// copy set to avoid ConcurrentModificationException
removeAllEdges(new ArrayList<E>(touchingEdgesList));
super.removeVertex(v); // remove the vertex itself
fireVertexRemoved(v);
return true;
} else {
return false;
}
}
/**
* @see ListenableGraph#removeVertexSetListener(VertexSetListener)
*/
public void removeVertexSetListener(VertexSetListener<V> l)
{
vertexSetListeners.remove(l);
}
/**
* Notify listeners that the specified edge was added.
*
* @param edge the edge that was added.
* @param source edge source
* @param target edge target
*/
protected void fireEdgeAdded(E edge, V source, V target)
{
GraphEdgeChangeEvent<V, E> e =
createGraphEdgeChangeEvent(
GraphEdgeChangeEvent.EDGE_ADDED,
edge,
source,
target);
for (GraphListener<V, E> l : graphListeners) {
l.edgeAdded(e);
}
}
/**
* Notify listeners that the specified edge was removed.
*
* @param edge the edge that was removed.
* @param source edge source
* @param target edge target
*/
protected void fireEdgeRemoved(E edge, V source, V target)
{
GraphEdgeChangeEvent<V, E> e =
createGraphEdgeChangeEvent(
GraphEdgeChangeEvent.EDGE_REMOVED,
edge,
source,
target);
for (GraphListener<V, E> l : graphListeners) {
l.edgeRemoved(e);
}
}
/**
* Notify listeners that the specified vertex was added.
*
* @param vertex the vertex that was added.
*/
protected void fireVertexAdded(V vertex)
{
GraphVertexChangeEvent<V> e =
createGraphVertexChangeEvent(
GraphVertexChangeEvent.VERTEX_ADDED,
vertex);
for (VertexSetListener<V> l : vertexSetListeners) {
l.vertexAdded(e);
}
for (GraphListener<V, E> l : graphListeners) {
l.vertexAdded(e);
}
}
/**
* Notify listeners that the specified vertex was removed.
*
* @param vertex the vertex that was removed.
*/
protected void fireVertexRemoved(V vertex)
{
GraphVertexChangeEvent<V> e =
createGraphVertexChangeEvent(
GraphVertexChangeEvent.VERTEX_REMOVED,
vertex);
for (VertexSetListener<V> l : vertexSetListeners) {
l.vertexRemoved(e);
}
for (GraphListener<V, E> l : graphListeners) {
l.vertexRemoved(e);
}
}
private static <L extends EventListener> void addToListenerList(
List<L> list,
L l)
{
if (!list.contains(l)) {
list.add(l);
}
}
private GraphEdgeChangeEvent<V, E> createGraphEdgeChangeEvent(
int eventType,
E edge,
V source,
V target)
{
if (reuseEvents) {
reuseableEdgeEvent.setType(eventType);
reuseableEdgeEvent.setEdge(edge);
reuseableEdgeEvent.setEdgeSource(source);
reuseableEdgeEvent.setEdgeTarget(target);
return reuseableEdgeEvent;
} else {
return new GraphEdgeChangeEvent<V, E>(
this,
eventType,
edge,
source,
target);
}
}
private GraphVertexChangeEvent<V> createGraphVertexChangeEvent(
int eventType,
V vertex)
{
if (reuseEvents) {
reuseableVertexEvent.setType(eventType);
reuseableVertexEvent.setVertex(vertex);
return reuseableVertexEvent;
} else {
return new GraphVertexChangeEvent<V>(this, eventType, vertex);
}
}
/**
* A reuseable edge event.
*
* @author Barak Naveh
* @since Aug 10, 2003
*/
private static class FlyweightEdgeEvent<VV, EE>
extends GraphEdgeChangeEvent<VV, EE>
{
private static final long serialVersionUID = 3907207152526636089L;
/**
* @see GraphEdgeChangeEvent#GraphEdgeChangeEvent(Object, int, Edge)
*/
public FlyweightEdgeEvent(Object eventSource, int type, EE e)
{
super(eventSource, type, e);
}
/**
* Sets the edge of this event.
*
* @param e the edge to be set.
*/
protected void setEdge(EE e)
{
this.edge = e;
}
protected void setEdgeSource(VV v)
{
this.edgeSource = v;
}
protected void setEdgeTarget(VV v)
{
this.edgeTarget = v;
}
/**
* Set the event type of this event.
*
* @param type the type to be set.
*/
protected void setType(int type)
{
this.type = type;
}
}
/**
* A reuseable vertex event.
*
* @author Barak Naveh
* @since Aug 10, 2003
*/
private static class FlyweightVertexEvent<VV>
extends GraphVertexChangeEvent<VV>
{
private static final long serialVersionUID = 3257848787857585716L;
/**
* @see GraphVertexChangeEvent#GraphVertexChangeEvent(Object, int,
* Object)
*/
public FlyweightVertexEvent(Object eventSource, int type, VV vertex)
{
super(eventSource, type, vertex);
}
/**
* Set the event type of this event.
*
* @param type type to be set.
*/
protected void setType(int type)
{
this.type = type;
}
/**
* Sets the vertex of this event.
*
* @param vertex the vertex to be set.
*/
protected void setVertex(VV vertex)
{
this.vertex = vertex;
}
}
}
// End DefaultListenableGraph.java