/* * 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: 1210 $ by $Author: glycoslave $ on $Date:: 2009-06-12 #$ */ package org.eurocarbdb.util.graph; import java.util.Set; import java.util.HashSet; import org.apache.log4j.Logger; import org.eurocarbdb.util.Visitor; /** * Utility class to traverse the elements of a {@link Graph} in * depth-first order, following the {@link Visitor} pattern. The normal * usage of this class is through subclassing, and adding the desired * data and behaviour to the subclass. * *<h2>Example</h2> * * Given the graph: *<pre> * F * \ * E -- C * / \ * G B -- A * / * H -- D * *</pre> * The depth-first traversal of this graph (with lower sorting * alternatives traversed first) starting from A would traverse * vertices in the following order: A, B, C, E, F, G, D, H. For example, * given the code (minus generic arguments for clarity): *<pre> * DepthFirstGraphVisitor visitor = new DepthFirstGraphVisitor() * { * int depth = 0; * * public void accept( Vertex v ) * { * depth++; * log.debug( "> approaching vertex " + v.getValue() ); * * super.accept( v ); * * log.debug( "< leaving vertex " + v.getValue() ); * depth--; * } * }; * *</pre> * would print (with added indenting for clarity): *<pre> * > approaching vertex A * > approaching vertex B * > approaching vertex C * > approaching vertex E * > approaching vertex F * < leaving vertex F * > approaching vertex G * < leaving vertex G * < leaving vertex E * < leaving vertex C * > approaching vertex D * > approaching vertex H * < leaving vertex H * < leaving vertex D * < leaving vertex B * < leaving vertex A * *</pre> * * Note that this traversal class is not re-entrant; the default * implementation cannot traverse a Graph that contains nested Graphs. * * @author mjh */ public class DepthFirstGraphVisitor<E,V> extends Visitor { /** logging handle */ protected static Logger log = Logger.getLogger( DepthFirstGraphVisitor.class ); /** Set of visited vertices, initially empty. */ protected Set<Vertex<E,V>> visitedVertices = new HashSet<Vertex<E,V>>(); /** Set of unvisited vertices, initially null. */ protected Set<Vertex<E,V>> unvisitedVertices = null; //new HashSet<Vertex<E,V>>(); /** * Resets this visitor to its starting state, including the * {@link Set} of {@link #visitedVertices visited} vertices. */ public void clear() { visitedVertices = new HashSet<Vertex<E,V>>(); } /** * Called when visiting a {@link Graph}. * * The default implementation visits all {@link Vertex}es and {@link Edge}s * in depth-first order, starting from the Graph's root vertex, as given * by the method {@link Graph#getRootVertex()}. * */ public void accept( Graph<E,V> g ) { if ( g.isEmpty() ) { log.debug("graph is empty, returning..."); return; } log.debug("visiting graph of size=" + g.size() ); Vertex<E,V> v = g.getRootVertex(); unvisitedVertices = new HashSet<Vertex<E,V>>( g.getAllVertices() ); visit( v ); if ( visitedVertices.size() < g.countVertices() ) { log.info("note: imperfect depth-first traversal - graph is not fully connected"); // graph isn't fully connected, visit remaing vertices in // semi-arbitrary order while ( ! unvisitedVertices.isEmpty() ) { v = unvisitedVertices.iterator().next(); visit( v ); } } } /** * Called when visiting a {@link Vertex}. * * The default implementation visits this Vertex's value, then all * outgoing {@link Edge}s of this Vertex, as given by the method * {@link Vertex#getOutgoingEdges()}. * * @see Vertex.getValue() * @see Vertex.getOutgoingEdges() */ public void accept( Vertex<E,V> v ) { log.debug("visiting vertex: " + v ); if ( ! visitedVertices.add( v ) ) return; if ( unvisitedVertices != null ) unvisitedVertices.remove( v ); visit( v.getValue() ); for ( Edge<E,V> e : v.getOutgoingEdges() ) visit( e ); return; } /** * Called when visiting an {@link Edge}. * * The default implementation visits the child {@link Vertex} * of this Edge, as given by the method {@link Edge#getChild()}. */ public void accept( Edge<E,V> e ) { log.debug("visiting edge: " + e ); visit( e.getValue() ); Vertex<E,V> child_vertex = e.getChild(); if ( visitedVertices.contains( child_vertex ) ) return; visit( child_vertex ); } } // end class