/* XXL: The eXtensible and fleXible Library for data processing Copyright (C) 2000-2011 Prof. Dr. Bernhard Seeger Head of the Database Research Group Department of Mathematics and Computer Science University of Marburg Germany This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; If not, see <http://www.gnu.org/licenses/>. http://code.google.com/p/xxl/ */ package xxl.core.cursors.unions; import java.util.Iterator; import java.util.Stack; import xxl.core.cursors.AbstractCursor; import xxl.core.cursors.Cursor; import xxl.core.cursors.Cursors; import xxl.core.functions.AbstractFunction; import xxl.core.functions.Function; /** * A flatten gets an input iterator which has a certain * hirarchical structure and flattens this structure, * so that only the interesting objects are returned. * <p> * For more details look into the example inside the * main method. * * @see java.util.Iterator * @see xxl.core.cursors.Cursor * @see xxl.core.cursors.AbstractCursor */ public class Flatten extends AbstractCursor { /** * Special function which can be used to flatten a cursor * that contains objects and iterators. */ public static final Function ITERATOR_FLATTEN_FUNCTION = new AbstractFunction() { public Object invoke(Object o) { if (o instanceof Iterator) return o; else return null; } }; /** * Function which gets the currently processed element * and returns an Object. There are three possibilities: * <ol> * <li>null (which means that the next element * is the next element of the cursor)</li> * <li>A cursor which contains elements (and maybe results).</li> * <li>An other object (the next element to be processed). * In this case, the function is called again on the object * until the function returns null. Then, the last element is * returned.</li> * </ol> */ protected Function getCursor; /** Stack which contains the currently processed cursors. */ protected Stack stack; /** The next object which will be returned. */ protected Object nextObject; /** * Creates a new sequentializer backed on an iteration of input iterations. * Every iterator given to this constructor is wrapped to a cursor. * * @param iteratorsCursor iteration of input iterations to be sequentialized. * @param getCursor Function which gets the currently processed element * and returns an Object. */ //TODO: Improve documentation! public Flatten(Iterator iteratorsCursor, Function getCursor) { this.getCursor = getCursor; stack = new Stack(); stack.add(Cursors.wrap(iteratorsCursor)); nextObject = null; } /** * Returns <tt>true</tt> if the iteration has more elements. (In other words, * returns <tt>true</tt> if <tt>next</tt> or <tt>peek</tt> would return an * element rather than throwing an exception.) * * <p>The attribute <tt>cursor</tt> is set by this method to the next input * iteration as follows: * <pre> * while (!cursor.hasNext()) { * cursor.close(); * if (iteratorsCursor.hasNext()) * cursor = Cursors.wrap((Iterator)iteratorsCursor.next()); * else * cursor = EmptyCursor.DEFAULT_INSTANCE; * } * </pre> * If the next input iteration is given by an iterator it is wrapped to a * cursor. The method returns whether the currently processed input iteration * contains further elements, i.e., the result of the <tt>hasNext</tt> method * of <tt>cursor</tt>. * * @return <tt>true</tt> if the sequentializer has more elements. */ protected boolean hasNextObject() { while (true) { if (stack.isEmpty()) return false; Cursor cursor = (Cursor) stack.peek(); if (!cursor.hasNext()) { stack.pop(); cursor.close(); } else { Object nextCursor = cursor.peek(); while (true) { nextCursor = getCursor.invoke(nextCursor); if (nextCursor==null) return true; // The cursor has the next element! else if (nextCursor instanceof Iterator) { cursor.next(); // The element (the cursor) is processed stack.push(Cursors.wrap((Iterator) nextCursor)); break; } else // The function returned an object which will be returned. nextObject = nextCursor; } } } } /** * Returns the next element in the iteration. This element will be * accessible by some of the sequentializer's methods, e.g., <tt>update</tt> * or <tt>remove</tt>, until a call to <tt>next</tt> or <tt>peek</tt> occurs. * This is calling <tt>next</tt> or <tt>peek</tt> proceeds the iteration and * therefore its previous element will not be accessible any more. * * @return the next element in the iteration. */ protected Object nextObject() { if (nextObject!=null) { Object ret = nextObject; nextObject = null; return ret; } else return ((Cursor) stack.peek()).next(); } }