com.phoenixst.plexus.traversals.BreadthFirstTraverser.java Source code

Java tutorial

Introduction

Here is the source code for com.phoenixst.plexus.traversals.BreadthFirstTraverser.java

Source

/*
 *  $Id: BreadthFirstTraverser.java,v 1.6 2005/10/03 15:16:31 rconner Exp $
 *
 *  Copyright (C) 1994-2005 by Phoenix Software Technologists,
 *  Inc. and others.  All rights reserved.
 *
 *  THIS PROGRAM AND DOCUMENTATION IS PROVIDED UNDER THE TERMS OF THE
 *  COMMON PUBLIC LICENSE ("AGREEMENT") WHICH ACCOMPANIES IT.  ANY
 *  USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES
 *  RECIPIENT'S ACCEPTANCE OF THE AGREEMENT.
 *
 *  The license text can also be found at
 *    http://opensource.org/licenses/cpl.php
 */

package com.phoenixst.plexus.traversals;

import java.util.*;

import org.apache.commons.collections.*;
import org.apache.log4j.Logger;

import com.phoenixst.plexus.*;
import com.phoenixst.plexus.util.*;

/**
 *  A breadth-first <code>Traverser</code> for a <code>Graph</code>,
 *  with no cycle detection.  The first node returned is the start
 *  node, and no <code>Edge</code> is traversed to reach it.  All of
 *  the caveats concerning the ordering of the operations
 *  <code>hasNext()</code>, <code>next()</code>, and
 *  <code>remove()</code> detailed by the {@link Traverser} class
 *  documentation apply here.
 *
 *  @version    $Revision: 1.6 $
 *  @author     Ray A. Conner
 *
 *  @since      1.0
 */
public class BreadthFirstTraverser implements PruningTraverser {

    /**
     *  The Logger.
     */
    private static final Logger LOGGER = Logger.getLogger(BreadthFirstTraverser.class);

    /**
     *  The factory for producing new Traversers.
     */
    private final Transformer traverserFactory;

    /**
     *  A FIFO queue of Traversers.  The next node to be returned is
     *  from the Traverser at the front of the queue.  As nodes are
     *  returned (and not pruned or removed), the above factory is
     *  used to create new Traversers which are added to the end of
     *  the queue (if the Traverser is not empty).
     */
    private final LinkedList traverserQueue = new LinkedList();

    /**
     *  The Traverser created from the last node returned by next().
     *  A value of <code>null</code> indicates that the last node or
     *  edge traversed has been removed or pruned.
     */
    private Traverser current = null;

    ////////////////////////////////////////
    // Constructors
    ////////////////////////////////////////

    /**
     *  Creates a new <code>BreadthFirstTraverser</code>.
     */
    public BreadthFirstTraverser(Object startNode, Graph graph, Predicate traverserPredicate) {
        this(startNode, graph, new DefaultTraverserFactory(graph, traverserPredicate));
    }

    /**
     *  Creates a new <code>BreadthFirstTraverser</code>, which
     *  breadth-first traverses the descendants of the specified
     *  <code>startNode</code>.  The specified <code>startNode</code>
     *  cannot be removed by {@link #remove} when using this
     *  constructor.
     */
    public BreadthFirstTraverser(Object startNode, OrientedForest forest) {
        this(startNode, null, new ChildTraverserFactory(forest));
    }

    /**
     *  Creates a new <code>BreadthFirstTraverser</code>.  The
     *  specified <code>startNode</code> cannot be removed by {@link
     *  #remove} when using this constructor.
     */
    public BreadthFirstTraverser(Object startNode, Transformer traverserFactory) {
        this(startNode, null, traverserFactory);
    }

    /**
     *  Creates a new <code>BreadthFirstTraverser</code>.  If the
     *  <code>graph</code> argument is <code>null</code>, the
     *  specified <code>startNode</code> cannot be removed by {@link
     *  #remove}.
     */
    public BreadthFirstTraverser(Object startNode, Graph graph, Transformer traverserFactory) {
        super();
        this.traverserFactory = traverserFactory;

        if (traverserFactory == null) {
            throw new IllegalArgumentException("Traverser Factory is null.");
        }
        if (graph == null) {
            // This is the only way to make sure that startNode is in
            // the graph in this case.
            traverserFactory.transform(startNode);
        } else if (!graph.containsNode(startNode)) {
            throw new NoSuchNodeException("Graph does not contain start node: " + startNode);
        }

        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Constructor: Adding trivial Traverser to " + startNode + " to end of queue.");
        }
        traverserQueue.addLast(new SingletonTraverser(graph, startNode, null));
    }

    ////////////////////////////////////////
    // Traverser
    ////////////////////////////////////////

    public boolean hasNext() {
        if (traverserQueue.isEmpty()) {
            return false;
        }
        if (traverserQueue.size() > 1) {
            return true;
        }

        // Because it's quicker to check current than the one
        // on the queue.
        LOGGER.debug("hasNext(): Calling hasNext() on last created Traverser.");
        if (current != null && current.hasNext()) {
            return true;
        }

        LOGGER.debug("  Calling hasNext() on first Traverser in queue.");
        return ((Traverser) traverserQueue.getFirst()).hasNext();
    }

    public Object next() {
        LOGGER.debug("next():");
        if (current != null && current.hasNext()) {
            LOGGER.debug("  Adding last created Traverser to end of queue.");
            traverserQueue.addLast(current);
        }
        current = null;

        while (!traverserQueue.isEmpty()) {
            Traverser t = (Traverser) traverserQueue.getFirst();
            LOGGER.debug("  Calling hasNext() on first Traverser in queue.");
            if (t.hasNext()) {
                Object node = t.next();
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("  Creating new Traverser for " + node + ".");
                }
                current = (Traverser) traverserFactory.transform(node);
                return node;
            }
            LOGGER.debug("  Removing first Traverser in queue.");
            traverserQueue.removeFirst();
        }

        throw new NoSuchElementException();
    }

    /**
     *  Removes from the underlying <code>Graph</code> the last node
     *  returned by {@link #next}.  This will prevent the exploration
     *  of those nodes that would have been reached through the
     *  removed node (unless they are reachable by another route).
     *  This method can be called only once per call to
     *  <code>next()</code>.  The behavior of this
     *  <code>Traverser</code> is unspecified if the underlying graph
     *  structure is modified while the traversal is in progress in
     *  any way other than by calling this method or {@link
     *  #removeEdge}.
     *
     *  @throws IllegalStateException if <code>next()</code> has not
     *  yet been called, or <code>remove()</code> or
     *  <code>removeEdge</code> has been called after the last call to
     *  <code>next()</code>.
     */
    public void remove() {
        if (current == null) {
            throw new IllegalStateException();
        }
        LOGGER.debug("remove(): Calling remove() on first Traverser in queue.");
        ((Traverser) traverserQueue.getFirst()).remove();
        current = null;
    }

    public Graph.Edge getEdge() {
        if (current == null) {
            throw new IllegalStateException();
        }
        LOGGER.debug("getEdge(): Calling getEdge() on first Traverser in queue.");
        return ((Traverser) traverserQueue.getFirst()).getEdge();
    }

    /**
     *  Removes from the underlying {@link Graph} the
     *  <code>Edge</code> that would be returned by {@link #getEdge
     *  getEdge()}.  This will prevent the exploration of those nodes
     *  that would have been reached through the removed
     *  <code>Edge</code> (unless they are reachable by another
     *  route).
     *
     *  <P><b>Description copied from interface: {@link
     *  Traverser}</b><br> {@inheritDoc}
     */
    public void removeEdge() {
        if (current == null) {
            throw new IllegalStateException();
        }
        LOGGER.debug("removeEdge(): Calling removeEdge() on first Traverser in queue.");
        ((Traverser) traverserQueue.getFirst()).removeEdge();
        current = null;
    }

    public void prune() {
        if (current == null) {
            throw new IllegalStateException();
        }
        LOGGER.debug("prune(): Setting last created Traverser to null.");
        current = null;
    }

}