All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jgrapht.graph.DefaultListenableGraph Maven / Gradle / Ivy

The newest version!
/*
 * (C) Copyright 2003-2023, by Barak Naveh and Contributors.
 *
 * JGraphT : a free Java graph-theory library
 *
 * See the CONTRIBUTORS.md file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0, or the
 * GNU Lesser General Public License v2.1 or later
 * which is available at
 * http://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR LGPL-2.1-or-later
 */
package org.jgrapht.graph;

import org.jgrapht.*;
import org.jgrapht.event.*;
import org.jgrapht.util.*;

import java.util.*;

/**
 * A graph backed by the the graph specified at the constructor, which can be listened by
 * GraphListener s and by 
 * VertexSetListener 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.
 *
 * 

* This graph does not pass the hashCode and equals operations through to the backing graph, * but relies on Object's equals and hashCode methods. *

* * @param the graph vertex type * @param the graph edge type * * @author Barak Naveh * @see GraphListener * @see VertexSetListener */ public class DefaultListenableGraph extends GraphDelegator implements ListenableGraph, Cloneable { private static final long serialVersionUID = -1156773351121025002L; private List> graphListeners = new ArrayList<>(); private List> vertexSetListeners = new ArrayList<>(); private FlyweightEdgeEvent reuseableEdgeEvent; private FlyweightVertexEvent reuseableVertexEvent; private boolean reuseEvents; /** * Creates a new listenable graph. * * @param g the backing graph. */ public DefaultListenableGraph(Graph g) { this(g, false); } /** * Creates a new listenable graph. If the reuseEvents flag is set to * true 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 g, boolean reuseEvents) { super(g); this.reuseEvents = reuseEvents; reuseableEdgeEvent = new FlyweightEdgeEvent<>(this, -1, null); reuseableVertexEvent = new FlyweightVertexEvent<>(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 reuseEvents flag is set to true 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 reuseEvents flag is set. If the flag is set to * true 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 reuseEvents flag. */ public boolean isReuseEvents() { return reuseEvents; } @Override public E addEdge(V sourceVertex, V targetVertex) { E e = super.addEdge(sourceVertex, targetVertex); if (e != null) { fireEdgeAdded(e, sourceVertex, targetVertex, Graph.DEFAULT_EDGE_WEIGHT); } return e; } @Override public boolean addEdge(V sourceVertex, V targetVertex, E e) { boolean added = super.addEdge(sourceVertex, targetVertex, e); if (added) { fireEdgeAdded(e, sourceVertex, targetVertex, Graph.DEFAULT_EDGE_WEIGHT); } return added; } @Override public void addGraphListener(GraphListener l) { addToListenerList(graphListeners, l); } @Override public V addVertex() { V v = super.addVertex(); if (v != null) { fireVertexAdded(v); } return v; } @Override public boolean addVertex(V v) { boolean modified = super.addVertex(v); if (modified) { fireVertexAdded(v); } return modified; } @Override public void addVertexSetListener(VertexSetListener l) { addToListenerList(vertexSetListeners, l); } @Override public Object clone() { try { DefaultListenableGraph g = TypeUtil.uncheckedCast(super.clone()); g.graphListeners = new ArrayList<>(); g.vertexSetListeners = new ArrayList<>(); return g; } catch (CloneNotSupportedException e) { // should never get here since we're Cloneable e.printStackTrace(); throw new RuntimeException("internal error"); } } @Override public E removeEdge(V sourceVertex, V targetVertex) { E e = super.getEdge(sourceVertex, targetVertex); if (e != null) { double weight = super.getEdgeWeight(e); if (super.removeEdge(e)) { fireEdgeRemoved(e, sourceVertex, targetVertex, weight); } } return e; } @Override public boolean removeEdge(E e) { V sourceVertex = getEdgeSource(e); V targetVertex = getEdgeTarget(e); double weight = getEdgeWeight(e); boolean modified = super.removeEdge(e); if (modified) { fireEdgeRemoved(e, sourceVertex, targetVertex, weight); } return modified; } @Override public void removeGraphListener(GraphListener l) { graphListeners.remove(l); } @Override public boolean removeVertex(V v) { if (containsVertex(v)) { Set touchingEdgesList = edgesOf(v); // copy set to avoid ConcurrentModificationException removeAllEdges(new ArrayList<>(touchingEdgesList)); super.removeVertex(v); // remove the vertex itself fireVertexRemoved(v); return true; } else { return false; } } @Override public void setEdgeWeight(E e, double weight) { super.setEdgeWeight(e, weight); V sourceVertex = getEdgeSource(e); V targetVertex = getEdgeTarget(e); fireEdgeWeightUpdated(e, sourceVertex, targetVertex, weight); } @Override public void removeVertexSetListener(VertexSetListener 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 * @param weight edge weight */ protected void fireEdgeAdded(E edge, V source, V target, double weight) { GraphEdgeChangeEvent e = createGraphEdgeChangeEvent( GraphEdgeChangeEvent.EDGE_ADDED, edge, source, target, weight); for (GraphListener 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 * @param weight edge weight */ protected void fireEdgeRemoved(E edge, V source, V target, double weight) { GraphEdgeChangeEvent e = createGraphEdgeChangeEvent( GraphEdgeChangeEvent.EDGE_REMOVED, edge, source, target, weight); for (GraphListener l : graphListeners) { l.edgeRemoved(e); } } /** * Notify listeners that the weight of an edge has changed. * * @param edge the edge whose weight has changed. * @param source edge source * @param target edge target * @param weight the edge weight */ protected void fireEdgeWeightUpdated(E edge, V source, V target, double weight) { GraphEdgeChangeEvent e = createGraphEdgeChangeEvent( GraphEdgeChangeEvent.EDGE_WEIGHT_UPDATED, edge, source, target, weight); for (GraphListener l : graphListeners) { l.edgeWeightUpdated(e); } } /** * Notify listeners that the specified vertex was added. * * @param vertex the vertex that was added. */ protected void fireVertexAdded(V vertex) { GraphVertexChangeEvent e = createGraphVertexChangeEvent(GraphVertexChangeEvent.VERTEX_ADDED, vertex); for (VertexSetListener l : vertexSetListeners) { l.vertexAdded(e); } for (GraphListener 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 e = createGraphVertexChangeEvent(GraphVertexChangeEvent.VERTEX_REMOVED, vertex); for (VertexSetListener l : vertexSetListeners) { l.vertexRemoved(e); } for (GraphListener l : graphListeners) { l.vertexRemoved(e); } } private static void addToListenerList(List list, L l) { if (!list.contains(l)) { list.add(l); } } private GraphEdgeChangeEvent createGraphEdgeChangeEvent( int eventType, E edge, V source, V target, double weight) { if (reuseEvents) { reuseableEdgeEvent.setType(eventType); reuseableEdgeEvent.setEdge(edge); reuseableEdgeEvent.setEdgeSource(source); reuseableEdgeEvent.setEdgeTarget(target); reuseableEdgeEvent.setEdgeWeight(weight); return reuseableEdgeEvent; } else { return new GraphEdgeChangeEvent<>(this, eventType, edge, source, target, weight); } } private GraphVertexChangeEvent createGraphVertexChangeEvent(int eventType, V vertex) { if (reuseEvents) { reuseableVertexEvent.setType(eventType); reuseableVertexEvent.setVertex(vertex); return reuseableVertexEvent; } else { return new GraphVertexChangeEvent<>(this, eventType, vertex); } } /** * A reuseable edge event. * * @author Barak Naveh */ private static class FlyweightEdgeEvent extends GraphEdgeChangeEvent { private static final long serialVersionUID = 3907207152526636089L; /** * @see GraphEdgeChangeEvent */ public FlyweightEdgeEvent(Object eventSource, int type, EE e) { super(eventSource, type, e, null, null); } /** * 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; } protected void setEdgeWeight(double weight) { this.edgeWeight = weight; } /** * 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 */ private static class FlyweightVertexEvent extends GraphVertexChangeEvent { 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; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy