/* * EuroCarbDB, a framework for carbohydrate bioinformatics * * Copyright (c) 2006-2009, Eurocarb project, or third-party contributors as * indicated by the @author tags or express copyright attribution * statements applied by the authors. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * A copy of this license accompanies this distribution in the file LICENSE.txt. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * Last commit: $Rev: 1932 $ by $Author: glycoslave $ on $Date:: 2010-08-05 #$ */ package org.eurocarbdb.util.graph; // stdlib imports import java.util.Map; import java.util.Set; import java.util.List; import java.util.Iterator; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.ArrayList; import java.util.LinkedList; import java.util.Collections; import java.io.Serializable; // 3rd party imports import org.apache.log4j.Logger; import static org.eurocarbdb.util.StringUtils.join; /* class Graph *//************************************************** *<p> * This class implements mathematical graphs. Conventional graph theory * defines a graph as a set of <em>vertices</em> (or nodes), connected * to each other by <em>edges</em>. For more specific information about * vertices and edges, see the {@link Vertex} and {@link Edge} class * documentation. *</p> *<p> * Typical usage involves creating a Graph object, to which Vertex * and Edge objects are then added through the various <tt>add</tt> * methods. Vertex and Edge objects are created through the * {@link #createVertex createVertex} and {@link #createEdge createEdge} * methods. *</p> *<p> * Alternatively, vertices and edges may be rapidly added to the graph * through the {@link Graph#addPath addPath} method, which creates * Vertex and Edge objects implicitly. *</p> *<p> * This is a <em>generic</em> class - it can be parameterised for both * Edge and Vertex types; accordingly, the 'E' and 'V' in the class * declaration refer to the parameterised Edge and Vertex types * respectively. While it is possible to access edge and vertex * objects directly, this is rarely necessary, since most methods * also provide direct access to vertex and edge values. *</p> *<p> * The associated {@link Graphs} class provides several additional * methods for working with {@link Graph}s. *</p> * *<h3>Example usage</h3> *<pre> // create an empty graph</em> Graph<Float,String> g = new Graph<Float,String>(); // add a vertex g.addVertex("vertex1"); System.out.println("last vertex added was " + g.lastVertex() ); // add an edge and vertex attached to vertex1 g.addPath( g.lastVertex(), 0.75f, "vertex2" ); System.out.println("last vertex added was " + g.lastVertex() ); // add a couple more vertices and edges g.addPath( g.lastVertex(), 0.25f, "vertex3" ); g.addPath( g.getVertex("vertex2"), 0.1f, "vertex4" ); // list all vertices in the graph Set<String> vlist = g.getAllVertexValues(); System.out.println("vertices are: " + vlist ); // list all edges List<Float> elist = g.getAllEdgeValues(); System.out.println("edges are: " + elist ); // list all vertices that have 0 or 1 vertex attached to them System.out.println("leaves are: " + g.getLeaves() ); // elaborate graph a little more... g.addPath( g.getVertex("vertex2"), 0.01f, "vertex5" ); g.addPath( g.lastVertex(), 0.02f, "vertex6" ); g.addPath( g.getVertex("vertex5"), 0.98f, "vertex7" ); System.out.println("leaves are: " + g.getLeaves() ); // this edge introduces a cycle into the graph g.addEdge( g.getVertex("vertex1"), g.getVertex("vertex7"), 0.001f ); System.out.println("leaves are: " + g.getLeaves() ); // traverse graph vertices in depth-first order Graph<Float,String>.GraphIterator.DepthFirst dfs = g.traverseDepthFirst(); while ( dfs.hasNext() ) { String s = dfs.next(); System.out.println( s ); } *</pre> * * @param E The edge type. * @param V The vertex type. * @see Graphs * @see Vertex * @see Edge * @author mjh */ public class Graph<E,V> implements Iterable<Vertex<E,V>>, Set<Vertex<E,V>>, Serializable, Cloneable { //~~~~~~~~~~~~~~~~~~~~~ STATIC FIELDS ~~~~~~~~~~~~~~~~~~~~~~~// /** logging handle */ static Logger log = Logger.getLogger( Graph.class ); private static final long serialVersionUID = -1; /** The default number of child vertices to accomodate per vertex. */ static final int Default_Number_Of_Children = 4; /** The default number of vertices to accomodate in a tree. */ static final int Default_Tree_Size = 8; //~~~~~~~~~~~~~~~~~~~~~~~~~ FIELDS ~~~~~~~~~~~~~~~~~~~~~~~~~~// ///** //* //*/ //public boolean directional = true; /** Private set of Vertices. By definition, the first vertex in * the list is the root vertex. */ protected Set<Vertex<E,V>> vertices; /** Private list of Edges. */ protected Set<Edge<E,V>> edges; /** Map of value to vertex, to trade some memory for enhanced lookup speed. */ private Map<V,Vertex<E,V>> values; /** A semi-arbitrary root vertex. This will be the vertex that is * used to start any graph traversal in lieu of a user-provided * vertex. This also serves as the root node in the case that * the current graph is a tree. */ private Vertex<E,V> rootVertex = null; /** The most recently-added vertex. */ private Vertex<E,V> lastVertex; //~~~~~~~~~~~~~~~~~~~~~~ CONSTRUCTORS ~~~~~~~~~~~~~~~~~~~~~~~// /* Constructor *//********************************************* * * Constructs an empty Graph with an initial vertex capacity * of size {@link #Default_Tree_Size}. */ public Graph() { _init( Default_Tree_Size ); } /* Constructor *//******************************************* * * Constructs an empty Graph with an initial vertex capacity * of the given initial size. */ public Graph( int initial_size ) { _init( initial_size ); } /* Constructor *//******************************************* * * Constructs an empty Graph with an initial vertex capacity * of the given initial vertex size, and an initial edges * size per vertex of the given edges size. */ public Graph( int initial_edges_size, int initial_vertex_size ) { // TODO! _init( initial_vertex_size ); } /* Constructor *//********************************************* * * Constructs a Graph with the given vertices. Package use only. */ Graph( Set<Vertex<E,V>> vertices, Set<Edge<E,V>> edges ) { assert vertices != null; assert edges != null; this.vertices = vertices; this.edges = edges; this.values = new HashMap<V,Vertex<E,V>>( vertices.size() ); for ( Vertex<E,V> v : vertices ) { values.put( v.getValue(), v ); } assert values.size() == vertices.size(); } public static <E,V> Graph<E,V> create() { return new Graph<E,V>(); } public static <E,V> Graph<E,V> create( int vertices ) { return new Graph<E,V>( vertices ); } public static <E,V> Graph<E,V> create( int vertices, int edges ) { return new Graph<E,V>( vertices, edges ); } //~~~~~~~~~~~~~~~~~~~~~~~~ METHODS ~~~~~~~~~~~~~~~~~~~~~~~~~~// /* add *//***************************************************** * * Semantically equivalent to addVertex(Vertex). */ public boolean add( Vertex<E,V> v ) { return addVertex( v ); } /* addAll *//************************************************** * * Semantically equivalent to addVertex(Vertex) called for every * vertex in the passed collection. */ public boolean addAll( Collection<? extends Vertex<E,V>> vertices ) { boolean true_if_added = false; for ( Vertex<E,V> v : vertices ) true_if_added &= addVertex( v ); return true_if_added; } /* addAllValues *//******************************************** * * Semantically equivalent to {@link #addVertex(V)} called for every * vertex in the passed collection. */ public boolean addAllValues( Collection<V> vertex_values ) { boolean true_if_added = false; for ( V v : vertex_values ) true_if_added &= addVertex( v ); return true_if_added; } /* addEdge *//************************************************* *<p> * Adds an edge (of implicit value null) between the given vertices. *</p> *<p> * The order of the given vertex implies directionality - the edge * created by this method will use the first given vertex as the * "parent", the second as the "child". In effect this means that * the edge will be considered to have originated from the parent * vertex. *<p> * * @param parent The parent/originating/source vertex. * @param child The child/sink vertex. * @return True unless an edge between the given vertices * already exists in the graph. * @throws IllegalArgumentException if either of the passed * vertices are not in the current graph. */ public boolean addEdge( Vertex<E,V> parent, Vertex<E,V> child ) { return addEdge( parent, child, null ); } /* addEdge *//************************************************* * * Convenience method for adding edges between vertices with the * given vertex values. Subject to all of the caveats described * in {@link #addEdge(Vertex,Vertex)}. * * @param parent The parent/originating/source vertex value. * @param child The child/sink vertex value. * @throws IllegalArgumentException if either of the passed * values do not correspond to vertices in the current graph. */ public boolean addEdge( V parent, V child ) { return addEdge( parent, child, null ); } /* addEdge *//************************************************* *<p> * Adds an edge with the given value between the given vertices. *</p> *<p> * The order of the given vertex implies directionality - the edge * created by this method will use the first given vertex as the * "parent", the second as the "child". In effect this means that * the edge will be considered to have originated from the parent * vertex. *</p> * * @param parent The parent/originating/source vertex. * @param child The child/sink vertex. * @param edge_value Value of this edge. * @return True unless an edge with the given value * between the given vertices already exists * in the graph. */ public boolean addEdge( Vertex<E,V> parent, Vertex<E,V> child, E edge_value ) { return addEdge( createEdge( parent, child, edge_value ) ); } /* addEdge *//************************************************* * * Convenience method for adding edges between vertices with the * given vertex values. Subject to all of the caveats described * in {@link #addEdge(Vertex,Vertex)}. * * @param parent * The parent/originating/source vertex (value). * @param child * The child/sink vertex (value). * @throws IllegalArgumentException * If either of the passed values do not correspond to vertices in the current graph. */ public boolean addEdge( V parent, V child, E edge_value ) { Vertex<E,V> parentv = getVertex( parent ); Vertex<E,V> childv = getVertex( child ); if ( parentv == null ) throw new IllegalArgumentException("Parent vertex value argument '" + parent + "' does not correspond to a vertex in the current graph; try adding it first"); if ( childv == null ) throw new IllegalArgumentException("Child vertex value argument '" + child + "' does not correspond to a vertex in the current graph; try adding it first"); return addEdge( parentv, childv, edge_value ); } /* addEdge *//************************************************* *<p> * Adds a pre-defined edge to the current graph. *</p> *<p> * The given edge cannot be null, though it may contain vertices * that do not yet exist in the graph. *</p> * @return True unless the given edge already exists * in the graph. */ protected boolean addEdge( Edge<E,V> edge ) { if ( edge == null ) throw new IllegalArgumentException( "edge argument cannot be null"); if ( log.isDebugEnabled() ) { if ( edge.parent != edge.child ) { // regular edge between diff vertices log.debug( "adding edge of " + (edge.value == null ? "no value" : ("value=" + edge.value) ) + " between parent=" + edge.parent + " and child=" + edge.child ); } else { // self-referential edge from/to the same vertex log.debug( "adding self-referential (circular) edge of value " + edge.value + " to " + edge.parent ); } } // Skip over the question of whether the vertices in the given // edge are actually in the graph by just adding them anyway. // Object member 'vertices' is a Set anyway and won't accept duplicates. if ( vertices.add( edge.parent ) ) log.warn("parent vertex '" + edge.parent + "' of edge was not added to graph, adding it..."); if ( vertices.add( edge.child ) ) log.warn("child vertex '" + edge.child + "' of edge was not added to graph, adding it..."); // add edge references to vertices. edge.parent.addEdge( edge ); // only need to add edge once if parent & child are the same vertex. if ( edge.parent != edge.child ) edge.child.addEdge( edge ); return edges.add( edge ); } /* addGraph *//************************************************ * * Adds an existing graph to this graph. Note that this involves * a shallow copy of references only, so future changes made to * the graph being added will be observed in the present graph. * * @param graph * The graph to be added * @return * True if all vertices and edges were added successfully. * * @see java.util.Collection#addAll(java.util.Collection) */ public boolean addGraph( Graph<E,V> graph ) { boolean true_if_all_added = vertices.addAll( graph.vertices ); true_if_all_added &= edges.addAll( graph.edges ); return true_if_all_added; } /* addPath *//************************************************* *<p> * Convenience method for quickly adding edges/vertices. This method * implicity creates a new edge and vertex with the given values, * and associates them with the given vertex. *</p> *<p> * This method can be useful in combination with the <code>lastVertex()</code> * method to rapidly add edges & vertices to a graph. *</p> * * @see #lastVertex() * @param vertex A pre-existing vertex in the current graph. * @param edge_value The value of the edge between <code>vertex</code> * and the new vertex created with <code>vertex_value</code>. * @param vertex_value The desired value of the vertex created. * @return True unless the given edge already exists * in the graph. */ public boolean addPath( Vertex<E,V> vertex, E edge_value, V vertex_value ) { assert( vertex != null ); if ( ! this.contains( vertex ) ) throw new IllegalArgumentException("vertex '" + vertex + "' not present in graph"); Vertex<E,V> v = createVertex( vertex_value ); addVertex( v ); return addEdge( vertex, v, edge_value ); } /* addVertex(V) *//******************************************** * * Adds a new vertex into the current graph. * * @return * True unless a vertex with the given value already exists in the graph. */ public boolean addVertex( V vertex_value ) { return addVertex( createVertex( vertex_value ) ); } /* addVertex(Vertex) *//*************************************** * * Adds a predeclared vertex into the current graph. * * @return * True unless the given vertex already exists in the graph. */ protected boolean addVertex( Vertex<E,V> vertex ) { log.debug( "adding vertex of value " + vertex.getValue() ); if ( rootVertex == null ) rootVertex = vertex; lastVertex = vertex; boolean added = vertices.add( vertex ); values.put( vertex.getValue(), vertex ); vertex.index = vertices.size(); if ( values.size() != vertices.size() ) throw new IllegalArgumentException("Vertex value '" + vertex.getValue() + "' already exists in graph -- vertex values must be unique"); return added; } /* clear *//*************************************************** * * Empties this graph of all vertices and edges. */ public void clear() { _init( -1 ); } /* clone *//*************************************************** * * Returns a shallow copy of this graph -- this means that * modifications made to the original graph will appear in both * the original and copied graph. */ @Override @SuppressWarnings("unchecked") public Object clone() { Graph<E,V> copy = null; try { copy = (Graph<E,V>) super.clone(); copy.edges = (Set<Edge<E,V>>) ((HashSet) this.edges).clone(); copy.vertices = (Set<Vertex<E,V>>) ((HashSet) this.vertices).clone(); copy.values = (Map<V,Vertex<E,V>>) ((HashMap) this.values).clone(); } catch ( CloneNotSupportedException ex ) { log.warn( ex ); } return copy; } /* contains *//************************************************ * * Returns whether this Graph contains the given {@link Vertex}, * {@link Edge}, or a vertex that has a value equal to the given * value argument. */ public boolean contains( Object obj ) { if ( obj instanceof Vertex ) return vertices.contains( obj ); else if ( obj instanceof Edge ) return edges.contains( obj ); else try { // maybe it's a vertex value? // this try-catch block is only necessary because Java's // generics are erased at compile time, so we can't test // the type of obj against V or E in a runtime instanceof // expression. lame!!! V value = (V) obj; return (this.getVertex( value ) != null); } catch ( ClassCastException dont_care ) { return false; } } /** Not supported. */ public boolean containsAll( Collection<?> c ) throws UnsupportedOperationException { throw new UnsupportedOperationException("Feature not supported"); } /* countEdges *//********************************************** * * Returns the number of edges in the current graph. Note that * the number of edges is unrelated to the number of vertices. */ public int countEdges() { return this.edges.size(); } /* countVertices *//******************************************* * * Returns the number of vertices in the current graph. Note that * the number of vertices is unrelated to the number of edges. */ public int countVertices() { return this.vertices.size(); } /* createEdge *//********************************************** * * Factory style constructor for Edge instances. Note that this * method only creates edges; it does not add them to the current * graph. * * Subclasses should feel free to reimplement this method if they * wish to create more specialised Edge classes. * * @param parent The parent/originating/source vertex. * @param child The child/sink vertex. * @param edge_value Value of the edge. * @return A new edge instance. * @throws IllegalArgumentException if either parent or child are null, * or if either parent or child are not in the current Graph. */ protected Edge<E,V> createEdge( Vertex<E,V> parent, Vertex<E,V> child, E edge_value ) { if ( child == null ) throw new IllegalArgumentException( "child vertex of edge cannot be null"); if ( parent == null ) throw new IllegalArgumentException( "parent vertex of edge cannot be null"); if ( ! this.contains( parent ) ) throw new IllegalArgumentException("Parent vertex '" + parent + "' not present in graph"); if ( ! this.contains( child ) ) throw new IllegalArgumentException("Child vertex '" + child + "' not present in graph"); return new Edge<E,V>( parent, child, edge_value ); } // possible enhancement for later? // /** // * Creates a subgraph of the current Graph -- changes to // * vertices and edges in the sub-graph will also be reflected // * in this Graph. // */ // public Graph<E,V> createSubgraph() // { // // } /* createVertex *//******************************************** *<p> * Factory style constructor for Vertex instances. Note that this * method only creates vertices; it does not add them to the current * graph. *</p> *<p> * Subclasses should feel free to reimplement this method if they * wish to create more specialised Vertex classes. *</p> * @param value The value of the created vertex. * @return A new vertex instance. */ protected Vertex<E,V> createVertex( V value ) { return new Vertex<E,V>( value ); } /* getAllEdges *//********************************************* * * Returns the set of Edges in this graph. Note that the returned * set is a <em>copy</em> of the set of edges in the graph, and so * can be freely modified. */ public Set<Edge<E,V>> getAllEdges() { //return new HashSet<Edge<E,V>>( edges ); return Collections.unmodifiableSet( edges ); } /* getAllEdgeValues *//**************************************** * * Returns all edge values in this graph as a List. */ public List<E> getAllEdgeValues() { List<E> values = new ArrayList<E>( edges.size() ); for ( Edge<E,V> e : edges ) values.add( e.getValue() ); return values; } /* getAllVertexValues *//****************************************** * * Returns a (unmodifiable) {@link Set} of all {@link Vertex} * values in the current graph. */ public Set<V> getAllVertexValues() { return Collections.unmodifiableSet( values.keySet() ); } // TODO only if needed... // // /* getAllPaths *//********************************************* // * // * Returns a list of a list of vertex paths through the tree, one // * path per leaf vertex. Each path list comprises all vertices from // * the leaf vertex to the root, inclusive. // */ // public List<List<Vertex<E,V>>> getAllPaths() // { // List<List<Vertex<E,V>>> paths = new ArrayList<List<Vertex<E,V>>>(); // List<Vertex<E,V>> leaves = this.getLeaves(); // // for ( Vertex<E,V> v : leaves ) // { // List<Vertex<E,V>> path = this.getAllParentsOf( v ); // path.add( 0, v ); // paths.add( path ); // } // // return paths; // } // /* getAllVertices *//****************************************** * * Returns the set of vertices in the current graph. */ public Set<Vertex<E,V>> getAllVertices() { return Collections.unmodifiableSet( vertices ); } /* getEdges *//************************************************ * * Returns the list of edges that exist in this graph between the * given 2 vertices, returning an empty list if no edges exist * between these vertices. */ public Set<Edge<E,V>> getEdges( Vertex<E,V> v1, Vertex<E,V> v2 ) { Set<Edge<E,V>> e1 = v1.getAttachedEdges(); if ( e1.size() == 0 ) return Collections.emptySet(); Set<Edge<E,V>> e2 = v2.getAttachedEdges(); if ( e2.size() == 0 ) return Collections.emptySet(); Set<Edge<E,V>> intersection = new HashSet<Edge<E,V>>(); intersection.addAll( e1 ); intersection.retainAll( e2 ); if ( intersection.size() == 0 ) return Collections.emptySet(); //Collections.EMPTY_LIST // return new ArrayList<Edge<E,V>>( intersection ); return intersection; } /* getEdges *//************************************************ * * Returns the list of edge values that exist in this graph between * the given 2 vertices, returning an empty list if no edges exist * between these vertices. Note that this is a {@link List} whereas * the other {@link getEdges} method returns a {@link Set}. This is * because this method cannot assume that Edge <em>values</em> * are always unique. */ public List<E> getEdges( V v1, V v2 ) { Set<Edge<E,V>> edges = getEdges( getVertex(v1), getVertex(v2) ); if ( edges.size() == 0 ) return Collections.emptyList(); List<E> edge_values = new ArrayList<E>( edges.size() ); for ( Edge<E,V> e : edges ) edge_values.add( e.getValue() ); return edge_values; } /** * Returns the {@link Set} of values that correspond to (leaf) vertices; * ie: vertices that have exactly one incoming {@link Edge} and no * outgoing {@link Edge}s (ie: no children). */ public Set<V> getLeafValues() { Set<V> leaves = new HashSet<V>(); for ( Vertex<E,V> v : getLeafVertices() ) leaves.add( v.getValue() ); return leaves; } /** * Leaf vertices are vertices that have exactly one incoming {@link Edge} * and no outgoing {@link Edge}s. */ public Set<Vertex<E,V>> getLeafVertices() { Set<Vertex<E,V>> leaves = new HashSet<Vertex<E,V>>(); for ( Vertex<E,V> v : vertices ) if ( v.getIncomingEdges().size() == 1 && v.getOutgoingEdges().size() == 0 ) leaves.add( v ); if ( vertices.size() == 1 ) leaves.addAll( vertices ); return leaves; } /* getRootVertex *//******************************************* * * Returns the value of the nominal 'root' vertex of this graph; * see {@link #getRootVertex()}. */ public V getRootValue() { if ( this.isEmpty() ) return null; return this.rootVertex.getValue(); } /* getRootVertex *//******************************************* * * Returns the nominal 'root' vertex of this graph. Graphs do not * ordinarily have a "root" vertex, however for the purposes of this * class, the root vertex of a graph is used in various places as the * default "starting vertex" for various types of iteration and graph * representation. It also serves as the canonical root vertex for * graphs that are themselves trees. If not specifically assigned, * the root vertex of a graph instance is the first added vertex. * This method returns null if the graph itself is devoid of vertices * (ie: <tt>isEmpty()</tt> returns <tt>true</tt>). */ public Vertex<E,V> getRootVertex() { if ( this.isEmpty() ) return null; return this.rootVertex; } /* getVertex *//*********************************************** * * Returns the vertex with the given value, or null if no such * vertex exists. */ public Vertex<E,V> getVertex( V vertex_value ) { // for ( Vertex<E,V> v : vertices ) // if ( v.getValue().equals( vertex_value ) ) // return v; return values.get( vertex_value ); } /* isEmpty *//************************************************* * * Returns whether this tree is empty. */ public boolean isEmpty() { return vertices.isEmpty(); } /* iterator *//************************************************ * * Currently returns an iterator over the vertices in this tree, * in chronological order. This method allows statements of the * form: * <pre> * Graph<E,V> my_graph = ...; * for ( Vertex<E,V> v : my_graph ) { ... } * </pre> */ public Iterator<Vertex<E,V>> iterator() { return vertices.iterator(); } /* lastVertex *//********************************************** * * Convenience method that returns the last added vertex. */ public Vertex<E,V> lastVertex() { return ( this.isEmpty() ) ? null : lastVertex; } /* remove *//************************************************** * * Removes the given {@link Edge}, {@link Vertex}, or vertex value * from the graph, returning true if said edge or vertex was * removed from the graph. If the passed object corresponds to * the value of a vertex in the graph, then that vertex is removed. * * @throws IllegalArgumentException if the passed * object is not an edge, vertex, nor vertex value type. */ public boolean remove( Object obj ) { if ( obj instanceof Vertex ) { return remove( (Vertex<E,V>) obj ); } else if ( obj instanceof Edge ) { return remove( (Edge<E,V>) obj ); } else { try { @SuppressWarnings("unchecked") // fuck you java V value = (V) obj; return remove( getVertex( value ) ); } catch ( ClassCastException dont_care ) { throw new IllegalArgumentException( "Passed object is neither a Vertex, Edge, nor Vertex value"); } } } /* remove *//************************************************** * * Removes the given {@link Vertex} from the graph, implicitly * also removing all {@link Edge}s of this vertex. * * @throws IllegalArgumentException if the passed vertex is not in * the graph. */ public boolean remove( Vertex<E,V> v ) { if ( log.isDebugEnabled() ) log.debug("removing vertex: " + v ); if ( ! vertices.contains( v ) ) throw new IllegalArgumentException( "Vertex " + v + " not found in graph"); // need to make copy of edge list cause if not, weird shit happens List<Edge<E,V>> my_edges = new ArrayList( v.getAttachedEdges() ); for ( Edge<E,V> e : my_edges ) { remove( e ); } // log.debug("after: " + edges.size() ); // log.debug("implicitly removed " + my_edges.size() + " edges of vertex " + v ); vertices.remove( v ); values.remove( v.getValue() ); assert values.size() == vertices.size(); // there is a subtle problem with removing the last vertex, // since lastVertex() will return a vertex that is no longer // in the graph. so we null it for just this case in the hope // that this makes it easier to get picked up later. if ( v == lastVertex ) lastVertex = null; return true; } /* remove *//************************************************** * * Removes the given {@link Edge} from the graph. * * @throws IllegalArgumentException * if the passed edge is not in the graph. */ public boolean remove( Edge<E,V> e ) { if ( log.isDebugEnabled() ) log.debug("removing edge: '" + e ); if ( ! edges.contains( e ) ) throw new IllegalArgumentException( "Edge " + e + " not found in graph"); e.free(); edges.remove( e ); return true; } /* removeAll *//*********************************************** * * Removes the given vertices from the tree; any edges attached to * removed vertices are similarly removed. */ public boolean removeAll( Collection<?> vertices_to_remove ) { if ( vertices.removeAll( vertices_to_remove ) ) { // Remove edges that have vertices that are no longer in the graph. */ for ( Edge<E,V> edge : edges ) if ( ! (vertices.contains(edge.parent) && vertices.contains(edge.child)) ) remove( edge ); return true; } else return false; } /* retainAll *//************************************************** * * Removes all vertices that are not in the passed collection of * vertices. All edges that are attached to vertices removed are * similarly removed. */ public boolean retainAll( Collection<?> vertices_to_keep ) { if ( vertices.retainAll( vertices_to_keep ) ) { // Remove edges that have vertices that are no longer in the graph. // is this code even necessary? if retainAll calls remove() then // edge will already be getting removed. for ( Edge<E,V> edge : edges ) if ( ! (vertices.contains(edge.parent) && vertices.contains(edge.child)) ) remove( edge ); return true; } else return false; } /* setRootVertex *//******************************************* * * Sets the given vertex as the root vertex of this tree. */ public void setRootVertex( Vertex<E,V> v ) { assert v != null; assert this.contains( v ); rootVertex = v; } /* size *//**************************************************** * * Same as countVertices(). Only here to preserve compatibility/familiarity * with java Collection-like semantics. */ public int size() { return countVertices(); } /* traverseBreadthFirst *//************************************ * * Returns an iterator over this graph in breadth-first order, * starting at the root vertex of this graph. * * @see Graph.GraphIterator.BreadthFirst */ public GraphIterator<E,V> traverseBreadthFirst() { return traverseBreadthFirst( getRootVertex() ); } /* traverseBreadthFirst *//************************************ * * Returns an iterator over this graph in breadth-first order, * starting at the given vertex within the graph. */ public GraphIterator<E,V> traverseBreadthFirst( Vertex<E,V> starting_vertex ) { return new GraphIterator.BreadthFirst<E,V>( this, starting_vertex ); } /* traverseDepthFirst *//*************************************** * * Returns an iterator over this graph in depth-first order, * starting at the root vertex of this graph. * * @see Graph.GraphIterator.DepthFirst */ public GraphIterator<E,V> traverseDepthFirst() { return traverseDepthFirst( getRootVertex() ); } /* traverseDepthFirst *//*************************************** * * Returns an iterator over this graph in depth-first order, * starting at the given vertex within the graph. */ public GraphIterator<E,V> traverseDepthFirst( Vertex<E,V> starting_vertex ) { return new GraphIterator.DepthFirst<E,V>( this, starting_vertex ); } /* implementation of java.util.Set#toArray() */ public Object[] toArray() { return getAllVertexValues().toArray(); } /* implementation of java.util.Set#toArray(T[]) */ public <V> V[] toArray( V[] values ) { return getAllVertexValues().toArray( values ); } /* toString *//************************************************ * * Returns a simple text representation of all vertices and edges * in the current graph. This representation is meant to be more * useful for deciphering the graph, as opposed to being visually * aesthetic. * * @see java.lang.Object#toString() */ public String toString_old() { StringBuilder sb = new StringBuilder(); List<Vertex<E,V>> vlist = new ArrayList<Vertex<E,V>>( getAllVertices() ); sb.append( "GRAPH of " + vertices.size() + " vertices, " + edges.size() + " edges" ); if ( vertices.size() > 0 ) sb.append( ", with root vertex " + rootVertex + " (" + vlist.indexOf( rootVertex ) + ")\n" ); for ( Vertex<E,V> v : vlist ) { sb.append( " VERTEX " + v.getValue() + " has " + v.countAttachedEdges() + " edge(s)" ); if ( v.countAttachedEdges() > 0 ) { sb.append( ":\n" + " EDGE " + join( "\n EDGE ", v.getAttachedEdges() ) + "\n" ); } else { sb.append( "\n" ); } } return sb.toString(); } public String toString() { StringBuilder sb = new StringBuilder(); sb.append( "GRAPH of " + vertices.size() + " vertex(es), " + edges.size() + " edge(s)" ); if ( vertices.size() > 0 ) { sb.append("\nVertexes:"); List<Vertex<E,V>> vlist = new ArrayList<Vertex<E,V>>( vertices ); Collections.sort( vlist ); for ( Vertex<E,V> v : vlist ) { sb.append( "\n " + v.index + ": " + v.getValue() ); } if ( edges.size() > 0 ) { sb.append("\nEdges:"); for ( Vertex<E,V> v : vlist ) { for ( Edge<E,V> e : v.getOutgoingEdges() ) { sb.append( "\n " + e.getParent().index + " --> " + e.getChild().index + ": " + e.getValue() ); } } } } return sb.toString(); } //~~~~~~~~~~~~~~~~~~~~~~~ PRIVATE METHODS ~~~~~~~~~~~~~~~~~~~// /** Initialises/resets the current graph to its starting state. */ private final void _init( int size ) { if ( vertices == null || edges == null ) { log.debug( "initialising graph of size=" + size ); if ( size < 0 ) size = Default_Tree_Size; vertices = new HashSet<Vertex<E,V>>( size ); values = new HashMap<V,Vertex<E,V>>( size ); edges = new HashSet<Edge<E,V>>( size ); } else { log.debug( "re-initialising graph (clearing vertices/edges)" ); values.clear(); vertices.clear(); edges.clear(); } rootVertex = null; lastVertex = null; return; } /** method to prevent type-cast warnings from java compiler because * java's generic are so poorly implemented. unavoidable :( */ @SuppressWarnings("unchecked") private final Vertex<E,V> _cast_to_Vertex( Object obj ) { return (Vertex<E,V>) obj; } /** method to prevent type-cast warnings from java compiler because * java's generic are so poorly implemented. unavoidable :( */ @SuppressWarnings("unchecked") private final V _cast_to_V( Object obj ) { return (V) obj; } } // end of class Graph