/* Copyright (C) 2006 Christian Schneider * * This file is part of Nomad. * * Nomad is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Nomad 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Nomad; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * Created on Sep 5, 2006 */ package net.sf.nmedit.nmutils.iterator; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.LinkedList; import java.util.Queue; /** * An iterator that implements a breadth-first search on a tree. * * @author Christian Schneider */ public abstract class BFSIterator<E> implements Iterator<E> { // bfs node buffer private Queue<E> queue; // the element returned by the latest next() call private E current = null; /** * Creates a breadth-first search iterator starting from the specified element * and using a {@link LinkedList} as queue. * If the start element is <code>null</code> the iteration contains no elements. * * @param start <code>null</code> or the first element of the iteration */ public BFSIterator(E start) { this(new LinkedList<E>(), start); } /** * Creates a breadth-first search iterator starting from the specified element * using the provided {@link Queue} for buffering nodes. * If the start element is <code>null</code> the iteration contains no elements. * * @param start <code>null</code> or the first element of the iteration * * @throws NullPointerException when the queue is <code>null</code> * @throws IllegalArgumentException when the queue is not empty */ public BFSIterator(Queue<E> queue, E start) { this.queue = queue; if (!queue.isEmpty()) throw new IllegalArgumentException("queue <"+queue+"> not empty"); if (start!=null) queue.offer(start); } /** * @see Iterator#hasNext() */ public final boolean hasNext() { // when the queue is empty the iteration is complete return !queue.isEmpty(); } /** * @see Iterator#next() */ public final E next() { // check for modifications according to a custom policy checkMod(); // remove the head of the queue which is the next element in the iteration // we do not check hasNext() since {@link Queue#remove()} throws the NoSuchElementException for us. // the element is stored in the 'current' variable which can be used for removing it using Iterator#remove() current = queue.remove(); // for BFS search we now have to enqueue the nodes children enqueueChildren(queue, current); // return next element in the iteration return current; } /** * Returns the current element of the iteration. * * @return the current element of the iteration (never returns null) * @see #remove() * @throws IllegalStateException when {@link #next()} was not called or the element has already been removed */ protected final E getRemoveElement() { checkMod(); E e = current; if (e==null) throw new IllegalStateException(); current = null; return e; } /** * throws an {@link UnsupportedOperationException}. An implementing of this operation * can use {@link #getRemoveElement()} to get the latest element in the iteration * that will be removed. * * @see Iterator#remove() * @throws UnsupportedOperationException */ public void remove() { throw new UnsupportedOperationException(); } /** * Custom implementation of the 'check for modification' policy. * This operation is called by {@link #next()} and {@link #remove()} * (if implemented) to check for modifications on the underlying collection that * interfere with the iteration. When such modifications are detected * it throws a {@link ConcurrentModificationException}. * * @throws ConcurrentModificationException modifications are detected that interfere with the iteration */ protected void checkMod() throws ConcurrentModificationException { } /** * Enqueues the children of the specified element in the specified queue if it has some. * * @param queue where the children are enqueued * @param parent parent of the enqueued children * @throws NullPointerException when queue or parent are <code>null</code> */ protected abstract void enqueueChildren(Queue<E> queue, E parent); }