/* ========================================== * 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. */ /* -------------------------- * ConnectivityInspector.java * -------------------------- * (C) Copyright 2003-2008, by Barak Naveh and Contributors. * * Original Author: Barak Naveh * Contributor(s): John V. Sichi * Christian Hammer * * $Id$ * * Changes * ------- * 06-Aug-2003 : Initial revision (BN); * 10-Aug-2003 : Adaptation to new event model (BN); * 07-Jun-2005 : Made generic (CH); * */ package org.jgrapht.alg; import java.util.*; import org.jgrapht.*; import org.jgrapht.event.*; import org.jgrapht.graph.*; import org.jgrapht.traverse.*; /** * Allows obtaining various connectivity aspects of a graph. The <i>inspected * graph</i> is specified at construction time and cannot be modified. * Currently, the inspector supports connected components for an undirected * graph and weakly connected components for a directed graph. To find strongly * connected components, use {@link StrongConnectivityInspector} instead. * * <p>The inspector methods work in a lazy fashion: no computation is performed * unless immediately necessary. Computation are done once and results and * cached within this class for future need.</p> * * <p>The inspector is also a {@link org.jgrapht.event.GraphListener}. If added * as a listener to the inspected graph, the inspector will amend internal * cached results instead of recomputing them. It is efficient when a few * modifications are applied to a large graph. If many modifications are * expected it will not be efficient due to added overhead on graph update * operations. If inspector is added as listener to a graph other than the one * it inspects, results are undefined.</p> * * @author Barak Naveh * @author John V. Sichi * @since Aug 6, 2003 */ public class ConnectivityInspector<V, E> implements GraphListener<V, E> { List<Set<V>> connectedSets; Map<V, Set<V>> vertexToConnectedSet; private Graph<V, E> graph; /** * Creates a connectivity inspector for the specified undirected graph. * * @param g the graph for which a connectivity inspector to be created. */ public ConnectivityInspector(UndirectedGraph<V, E> g) { init(); this.graph = g; } /** * Creates a connectivity inspector for the specified directed graph. * * @param g the graph for which a connectivity inspector to be created. */ public ConnectivityInspector(DirectedGraph<V, E> g) { init(); this.graph = new AsUndirectedGraph<V, E>(g); } /** * Test if the inspected graph is connected. An empty graph is <i>not</i> * considered connected. * * @return <code>true</code> if and only if inspected graph is connected. */ public boolean isGraphConnected() { return lazyFindConnectedSets().size() == 1; } /** * Returns a set of all vertices that are in the maximally connected * component together with the specified vertex. For more on maximally * connected component, see <a * href="http://www.nist.gov/dads/HTML/maximallyConnectedComponent.html"> * http://www.nist.gov/dads/HTML/maximallyConnectedComponent.html</a>. * * @param vertex the vertex for which the connected set to be returned. * * @return a set of all vertices that are in the maximally connected * component together with the specified vertex. */ public Set<V> connectedSetOf(V vertex) { Set<V> connectedSet = vertexToConnectedSet.get(vertex); if (connectedSet == null) { connectedSet = new HashSet<V>(); BreadthFirstIterator<V, E> i = new BreadthFirstIterator<V, E>(graph, vertex); while (i.hasNext()) { connectedSet.add(i.next()); } vertexToConnectedSet.put(vertex, connectedSet); } return connectedSet; } /** * Returns a list of <code>Set</code> s, where each set contains all * vertices that are in the same maximally connected component. All graph * vertices occur in exactly one set. For more on maximally connected * component, see <a * href="http://www.nist.gov/dads/HTML/maximallyConnectedComponent.html"> * http://www.nist.gov/dads/HTML/maximallyConnectedComponent.html</a>. * * @return Returns a list of <code>Set</code> s, where each set contains all * vertices that are in the same maximally connected component. */ public List<Set<V>> connectedSets() { return lazyFindConnectedSets(); } /** * @see GraphListener#edgeAdded(GraphEdgeChangeEvent) */ public void edgeAdded(GraphEdgeChangeEvent<V, E> e) { init(); // for now invalidate cached results, in the future need to // amend them. } /** * @see GraphListener#edgeRemoved(GraphEdgeChangeEvent) */ public void edgeRemoved(GraphEdgeChangeEvent<V, E> e) { init(); // for now invalidate cached results, in the future need to // amend them. } /** * Tests if there is a path from the specified source vertex to the * specified target vertices. For a directed graph, direction is ignored for * this interpretation of path. * * <p>Note: Future versions of this method might not ignore edge directions * for directed graphs.</p> * * @param sourceVertex one end of the path. * @param targetVertex another end of the path. * * @return <code>true</code> if and only if there is a path from the source * vertex to the target vertex. */ public boolean pathExists(V sourceVertex, V targetVertex) { /* * TODO: Ignoring edge direction for directed graph may be * confusing. For directed graphs, consider Dijkstra's algorithm. */ Set<V> sourceSet = connectedSetOf(sourceVertex); return sourceSet.contains(targetVertex); } /** * @see VertexSetListener#vertexAdded(GraphVertexChangeEvent) */ public void vertexAdded(GraphVertexChangeEvent<V> e) { init(); // for now invalidate cached results, in the future need to // amend them. } /** * @see VertexSetListener#vertexRemoved(GraphVertexChangeEvent) */ public void vertexRemoved(GraphVertexChangeEvent<V> e) { init(); // for now invalidate cached results, in the future need to // amend them. } private void init() { connectedSets = null; vertexToConnectedSet = new HashMap<V, Set<V>>(); } private List<Set<V>> lazyFindConnectedSets() { if (connectedSets == null) { connectedSets = new ArrayList<Set<V>>(); Set<V> vertexSet = graph.vertexSet(); if (vertexSet.size() > 0) { BreadthFirstIterator<V, E> i = new BreadthFirstIterator<V, E>(graph, null); i.addTraversalListener(new MyTraversalListener()); while (i.hasNext()) { i.next(); } } } return connectedSets; } /** * A traversal listener that groups all vertices according to to their * containing connected set. * * @author Barak Naveh * @since Aug 6, 2003 */ private class MyTraversalListener extends TraversalListenerAdapter<V, E> { private Set<V> currentConnectedSet; /** * @see TraversalListenerAdapter#connectedComponentFinished(ConnectedComponentTraversalEvent) */ public void connectedComponentFinished( ConnectedComponentTraversalEvent e) { connectedSets.add(currentConnectedSet); } /** * @see TraversalListenerAdapter#connectedComponentStarted(ConnectedComponentTraversalEvent) */ public void connectedComponentStarted( ConnectedComponentTraversalEvent e) { currentConnectedSet = new HashSet<V>(); } /** * @see TraversalListenerAdapter#vertexTraversed(VertexTraversalEvent) */ public void vertexTraversed(VertexTraversalEvent<V> e) { V v = e.getVertex(); currentConnectedSet.add(v); vertexToConnectedSet.put(v, currentConnectedSet); } } } // End ConnectivityInspector.java