/******************************************************************************* * Copyright (c) 2009, 2010 Fraunhofer IWU and others. * 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: * Fraunhofer IWU - initial API and implementation *******************************************************************************/ package net.enilink.komma.common.util; import java.util.Iterator; /** * An extensible tree iterator implementation that iterates over an object, it's * children, their children, and so on. Clients need only implement * {@link #getChildren getChildren} in order to implement a fully functional * tree iterator. */ public abstract class AbstractTreeIterator<E> extends ExtensibleList<Iterator<? extends E>> implements ITreeIterator<E> { private static final long serialVersionUID = 1L; /** * Whether the first call to next returns the initial root object or begins * with the first child of the root object. */ protected boolean includeRoot; /** * The root object for which the iteration is initiated. */ protected Object object; /** * The iterator that would be cut short by a call to {@link #prune}. */ protected Iterator<? extends E> nextPruneIterator; /** * The iterator to which a {@link #remove} call will delegated. */ protected Iterator<? extends E> nextRemoveIterator; /** * Creates an instance that iterates over an object, it's children, their * children, and so on. * * @param object * the root object of the tree. */ public AbstractTreeIterator(E object) { this.object = object; this.includeRoot = true; } /** * <p> * Creates and instance that iterates over an object (but only if * <code>includeRoot</code> is <code>true</code>), it's children, their * children, and so on. * <p> * <p> * If <code>includeRoot</code> is <code>true</code>, the <code>object</code> * is expected to be of the type <code>E</code>. */ public AbstractTreeIterator(Object object, boolean includeRoot) { this.object = object; this.includeRoot = includeRoot; } /** * Returns the iterator that yields the children of the object. * * @param object * the object for which children are required. * @return the iterator that yields the children. */ protected abstract Iterator<? extends E> getChildren(Object object); /** * Returns whether there are more elements. * * @return whether there are more elements. */ public boolean hasNext() { if (data == null && !includeRoot) { return hasAnyChildren(); } else { return hasMoreChildren(); } } private boolean hasAnyChildren() { Iterator<? extends E> nextPruneIterator = this.nextPruneIterator; nextPruneIterator = getChildren(object); add(nextPruneIterator); return nextPruneIterator.hasNext(); } private boolean hasMoreChildren() { // We don't create an iterator stack until the root mapping itself has // been returned by next once. // After that the stack should be non-empty and the top iterator should // yield true for hasNext. return data == null || !isEmpty() && ((Iterator<?>) data[size - 1]).hasNext(); } /** * Returns the next object and advances the iterator. * * @return the next object. */ public E next() { // If we are still on the root mapping itself... // if (data == null) { // Yield that mapping, create a stack, record it as the next one to // prune, and add it to the stack. // nextPruneIterator = getChildren(object); add(nextPruneIterator); if (includeRoot) { @SuppressWarnings("unchecked") E result = (E) object; return result; } } // Get the top iterator, retrieve it's result, and record it as the one // to which remove will be delegated. // @SuppressWarnings("unchecked") Iterator<? extends E> currentIterator = (Iterator<? extends E>) data[size - 1]; E result = currentIterator.next(); nextRemoveIterator = currentIterator; // If the result about to be returned has children... // Iterator<? extends E> iterator = getChildren(result); if (iterator.hasNext()) { // Record the iterator as the next one to prune, and add it to the // stack. // nextPruneIterator = iterator; add(iterator); } else { // There will be no iterator to prune. // nextPruneIterator = null; // While the current iterator has no next... // while (!currentIterator.hasNext()) { // Pop it from the stack. // data[--size] = null; // If the stack is empty, we're done. // if (isEmpty()) { break; } // Get the next one down and then test it for has next. // @SuppressWarnings("unchecked") Iterator<? extends E> nextIterator = (Iterator<? extends E>) data[size - 1]; currentIterator = nextIterator; } } return result; } /** * Removes the last object returned by {@link #next()} from the underlying * tree; it's an optional operation. * * @exception IllegalStateException * if <code>next</code> has not yet been called or has been * called only the yield the root object, or * <code>remove</code> has already been called after the last * call to the <code>next</code> method. * */ public void remove() { if (nextRemoveIterator == null) { throw new IllegalStateException( "There is no valid object to remove."); } nextRemoveIterator.remove(); } /** * Prunes the iterator so that it skips over all the nodes below the most * recent result of calling {@link #next next()}. */ public void prune() { // If there is an iterator to prune. // if (nextPruneIterator != null) { // If that iterator is still at the top of the stack... // if (!isEmpty() && data[size - 1] == nextPruneIterator) { // Pop it off the stack. // data[--size] = null; // Keep popping the stack until an iterator that has a next is // at the top. // while (!isEmpty() && !((Iterator<?>) data[size - 1]).hasNext()) { data[--size] = null; } } // You can only prune once. // nextPruneIterator = null; } } }