/******************************************************************************* * Copyright (c) 2002 - 2006 IBM Corporation. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package com.ibm.wala.util.graph.traverse; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Stack; import com.ibm.wala.util.collections.EmptyIterator; import com.ibm.wala.util.debug.UnimplementedError; import com.ibm.wala.util.graph.Graph; /** * This class implements depth-first search over a {@link Graph}, return an enumeration of the nodes of the graph in order of increasing * finishing time. This class follows the outNodes of the graph nodes to define the graph, but this behavior can be changed by * overriding the getConnected method. */ public abstract class DFSFinishTimeIterator<T> extends Stack<T> implements Iterator<T> { /** * the current next element in finishing time order */ private T theNextElement; /** * an enumeration of all nodes to search from */ private Iterator<? extends T> roots; /** * The governing graph. */ private Graph<T> G; /** * Subclasses must call this in the constructor! * * @param G * @param nodes */ protected void init(Graph<T> G, Iterator<? extends T> nodes) { this.G = G; roots = nodes; if (roots.hasNext()) theNextElement = roots.next(); } /** * Return whether there are any more nodes left to enumerate. * * @return true if there nodes left to enumerate. */ @Override public boolean hasNext() { return (!empty() || (theNextElement != null && getPendingChildren(theNextElement) == null)); } abstract Iterator<T> getPendingChildren(T n); abstract void setPendingChildren(T v, Iterator<T> iterator); /** * Find the next graph node in finishing time order. * * @return the next graph node in finishing time order. */ @Override @SuppressWarnings("unchecked") public T next() throws NoSuchElementException { if (!hasNext()) { throw new NoSuchElementException(); } if (empty()) { T v = theNextElement; setPendingChildren(v, getConnected(v)); push(v); } recurse: while (!empty()) { T v = peek(); Iterator<? extends T> pc = getPendingChildren(v); for (Iterator<? extends T> e = pc; e.hasNext();) { T n = e.next(); assert n != null : "null node in pc"; Iterator<T> nChildren = getPendingChildren(n); if (nChildren == null) { // found a new child: recurse to it. setPendingChildren(n, getConnected(n)); push(n); continue recurse; } } // the following saves space by allowing the original iterator to be GCed setPendingChildren(v, (Iterator<T>) EmptyIterator.instance()); // no more children to visit: finished this vertex while (getPendingChildren(theNextElement) != null && roots.hasNext()) { theNextElement = roots.next(); } return pop(); } return null; } /** * get the out edges of a given node * * @param n the node of which to get the out edges * @return the out edges * */ protected Iterator<T> getConnected(T n) { return G.getSuccNodes(n); } /** * @see java.util.Iterator#remove() */ @Override public void remove() throws UnimplementedError { throw new UnimplementedError(); } }