package prefuse.data.util; import java.util.Iterator; import prefuse.Constants; import prefuse.data.Edge; import prefuse.data.Node; import prefuse.data.Tuple; import prefuse.util.collections.Queue; /** * Provides a distance-limited breadth first traversal over nodes, edges, * or both, using any number of traversal "roots". * * @author <a href="http://jheer.org">jeffrey heer</a> */ public class BreadthFirstIterator implements Iterator { protected Queue m_queue = new Queue(); protected int m_depth; protected int m_traversal; protected boolean m_includeNodes; protected boolean m_includeEdges; /** * Create an uninitialized BreadthFirstIterator. Use the * {@link #init(Object, int, int)} method to initialize the iterator. */ public BreadthFirstIterator() { // do nothing, requires init call } /** * Create a new BreadthFirstIterator starting from the given source node. * @param n the source node from which to begin the traversal * @param depth the maximum graph distance to traverse * @param traversal the traversal type, one of * {@link prefuse.Constants#NODE_TRAVERSAL}, * {@link prefuse.Constants#EDGE_TRAVERSAL}, or * {@link prefuse.Constants#NODE_AND_EDGE_TRAVERSAL} */ public BreadthFirstIterator(Node n, int depth, int traversal) { init(new Node[] {n}, depth, traversal); } /** * Create a new BreadthFirstIterator starting from the given source nodes. * @param it an Iterator over the source nodes from which to begin the * traversal * @param depth the maximum graph distance to traverse * @param traversal the traversal type, one of * {@link prefuse.Constants#NODE_TRAVERSAL}, * {@link prefuse.Constants#EDGE_TRAVERSAL}, or * {@link prefuse.Constants#NODE_AND_EDGE_TRAVERSAL} */ public BreadthFirstIterator(Iterator it, int depth, int traversal) { init(it, depth, traversal); } /** * Initialize (or re-initialize) this iterator. * @param o Either a source node or iterator over source nodes * @param depth the maximum graph distance to traverse * @param traversal the traversal type, one of * {@link prefuse.Constants#NODE_TRAVERSAL}, * {@link prefuse.Constants#EDGE_TRAVERSAL}, or * {@link prefuse.Constants#NODE_AND_EDGE_TRAVERSAL} */ public void init(Object o, int depth, int traversal) { // initialize the member variables m_queue.clear(); m_depth = depth; if ( traversal < 0 || traversal >= Constants.TRAVERSAL_COUNT ) throw new IllegalArgumentException( "Unrecognized traversal type: "+traversal); m_traversal = traversal; m_includeNodes = (traversal == Constants.NODE_TRAVERSAL || traversal == Constants.NODE_AND_EDGE_TRAVERSAL); m_includeEdges = (traversal == Constants.EDGE_TRAVERSAL || traversal == Constants.NODE_AND_EDGE_TRAVERSAL); // seed the queue // TODO: clean this up? (use generalized iterator?) if ( m_includeNodes ) { if ( o instanceof Node ) { m_queue.add(o, 0); } else { Iterator tuples = (Iterator)o; while ( tuples.hasNext() ) m_queue.add(tuples.next(), 0); } } else { if ( o instanceof Node ) { Node n = (Node)o; m_queue.visit(n, 0); Iterator edges = getEdges(n); while ( edges.hasNext() ) { Edge e = (Edge)edges.next(); Node nn = e.getAdjacentNode(n); m_queue.visit(nn, 1); if ( m_queue.getDepth(e) < 0 ) m_queue.add(e, 1); } } else { Iterator tuples = (Iterator)o; while ( tuples.hasNext() ) { // TODO: graceful error handling when non-node in set? Node n = (Node)tuples.next(); m_queue.visit(n, 0); Iterator edges = getEdges(n); while ( edges.hasNext() ) { Edge e = (Edge)edges.next(); Node nn = e.getAdjacentNode(n); m_queue.visit(nn, 1); if ( m_queue.getDepth(e) < 0 ) m_queue.add(e, 1); } } } } } // ------------------------------------------------------------------------ /** * @see java.util.Iterator#remove() */ public void remove() { throw new UnsupportedOperationException(); } /** * @see java.util.Iterator#hasNext() */ public boolean hasNext() { return !m_queue.isEmpty(); } /** * Determines which edges are traversed for a given node. * @param n a node * @return an iterator over edges incident on the node */ protected Iterator getEdges(Node n) { return n.edges(); // TODO: add support for all edges, in links only, out links only } /** * Get the traversal depth at which a particular tuple was encountered. * @param t the tuple to lookup * @return the traversal depth of the tuple, or -1 if the tuple has not * been visited by the traversal. */ public int getDepth(Tuple t) { return m_queue.getDepth(t); } /** * @see java.util.Iterator#next() */ public Object next() { Tuple t = (Tuple)m_queue.removeFirst(); switch ( m_traversal ) { case Constants.NODE_TRAVERSAL: case Constants.NODE_AND_EDGE_TRAVERSAL: for ( ; true; t = (Tuple)m_queue.removeFirst() ) { if ( t instanceof Edge ) { return t; } else { Node n = (Node)t; int d = m_queue.getDepth(n); if ( d < m_depth ) { int dd = d+1; Iterator edges = getEdges(n); while ( edges.hasNext() ) { Edge e = (Edge)edges.next(); Node v = e.getAdjacentNode(n); if ( m_includeEdges && m_queue.getDepth(e) < 0 ) m_queue.add(e, dd); if ( m_queue.getDepth(v) < 0 ) m_queue.add(v, dd); } } else if ( m_includeEdges && d == m_depth ) { Iterator edges = getEdges(n); while ( edges.hasNext() ) { Edge e = (Edge)edges.next(); Node v = e.getAdjacentNode(n); int dv = m_queue.getDepth(v); if ( dv > 0 && m_queue.getDepth(e) < 0 ) { m_queue.add(e, Math.min(d,dv)); } } } return n; } } case Constants.EDGE_TRAVERSAL: Edge e = (Edge)t; Node u = e.getSourceNode(); Node v = e.getTargetNode(); int du = m_queue.getDepth(u); int dv = m_queue.getDepth(v); if ( du != dv ) { Node n = (dv > du ? v : u); int d = Math.max(du, dv); if ( d < m_depth ) { int dd = d+1; Iterator edges = getEdges(n); while ( edges.hasNext() ) { Edge ee = (Edge)edges.next(); if ( m_queue.getDepth(ee) >= 0 ) continue; // already visited Node nn = ee.getAdjacentNode(n); m_queue.visit(nn, dd); m_queue.add(ee, dd); } } } return e; default: throw new IllegalStateException(); } } } // end of class BreadthFirstIterator