/* 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; import java.util.NoSuchElementException; /** * An abstract cursor implements the methods of the interface * {@link xxl.core.cursors.Cursor} and is useful in those cases when you * produce elements and want to <i>return</i> them by a cursor. For example * consider a query-operation on an index structure that returns an iteration * over its results. It is sufficient then to create an abstract cursor and * implement the abstract methods <code>hasNextObject</code> and * <code>nextObject</code>.<br /> * Both methods are protected ones that are only called internally by the * abstract cursor whenever a new result is requested on that cursor. The * contract of these methods is that: * <ul> * <li> * <code>hasNextObject</code> determines whether the iteration has more * elements. * </li> * <li> * <code>nextObject</code> computes the next object in the iteration. * </li> * </ul> * * <b>Important:</b> In order to guarantee a certain semantics, an extension of * an <code>AbstractCursor</code> has to ensure that a call of * <code>hasNext</code> always returns <code>false</code> after the first time * <code>false</code> is delivered. Thus, it should not be possible to receive * an element by calling <code>hasNext</code> and <code>next()</code> at a * later point in time, if <code>hasNext</code> returned <code>false</code> * before (even if the underlying data structure received a new element * meanwhile). * * <p><b>Example usage:</b> * The cursor {@link xxl.core.cursors.sources.DiscreteRandomNumber} produces a * certain number (<code>noOfObjects</code>) of random integer objects by * calling {@link java.util.Random#nextInt()}. Therefore * <tcodet>RandomIntegers</code> extends <code>AbstractCursor</code> and the * implementation of its abstract methods is as follows: * <code><pre> * protected boolean hasNextObject() { * return noOfObjects == -1 || noOfObjects > 0; * } * * protected Object nextObject() { * if (noOfObjects > 0) * noOfObjects--; * return new Integer(random.nextInt(maxValue)); * } * </pre></code> * This means, as long as <code>noOfObjects > 0</code> holds (or * <code>noOfObjects == -1</code> for an infinite number of random * integer objects), the cursor has more elements and the next element will be * set to <code>new Integer(random.nextInt(maxValue))</code>.</p> * * <p>Note, that the abstract methods of the cursor interface are implemented in * this class, i.e., an abstract cursor already provides the mechanisms that are * required for its functionality. * <ul> * <li> * An abstract cursor provides a <code>hasNext</code>/<code>next</code> * mechanism guaranteeing a call to the method <code>hasNext</code> * before the next element in the iteration will be accessed, i.e., * there will be at least on call to the abstract method * <code>hasNextObject</code> before the second abstract method * <code>nextObject</code> is called. * </li> * <li> * It also provides an <code>open</code>/<code>close</code> mechanism. * Due to the decoupling of a cursor's construction and its open phase, * it must be guaranteed that a cursor is opened before it is accessed. * For this reason the <code>hasNext</code> method checks whether the * cursor has already been opened and opens it explicitly if not. When * a cursor has been closed, its elements cannot be accessed any more, * i.e., a call to the methods <code>peek</code> and <code>next</code> * will throw an exception rather than returning an element. Also a * closed cursor cannot be re-opened by a call to its <code>open</code> * method. Both methods (<code>open</code> and <code>close</code> are * implemented idempotent, i.e., consecutive calls to this methods will * have the same effect as a single call. * </li> * </ul> * * @param <E> the type of the elements returned by this iteration. * @see xxl.core.cursors.Cursor */ public abstract class AbstractCursor<E> implements Cursor<E> { /** * A flag indicating whether the iteration has more elements. */ protected boolean hasNext = false; /** * A flag indicating whether the <code>hasNext</code> method has already * been called for the next element in the iteration. */ protected boolean computedHasNext = false; /** * A flag indicating whether the element returned by the last call to the * <code>next</code> or <code>peek</code> method is valid any longer, i.e., * it has not been removed or updated since that time. */ protected boolean isValid = true; /** * The next element in the iteration. This object will be determined by the * first call to the <code>next</code> or <code>peek</code> method and * simply returned by consecutive calls. */ protected E next = null; /** * A flag indicating whether the element stored by the field {@link #next} * is already returned by a call to the <code>next</code> method. */ protected boolean assignedNext = false; /** * A flag indicating whether the cursor is already opened. */ protected boolean isOpened = false; /** * A flag indicating whether the cursor is already closed. */ protected boolean isClosed = false; /** * Opens the cursor, i.e., signals the cursor to reserve resources, open * files, etc. Before a cursor has been opened calls to methods like * <code>next</code> or <code>peek</code> are not guaranteed to yield * proper results. Therefore <code>open</code> must be called before a * cursor's data can be processed. Multiple calls to <code>open</code> do * not have any effect, i.e., if <code>open</code> was called the cursor * remains in the state <i>opened</i> until its <code>close</code> method * is called. * * <p>Note, that a call to the <code>open</code> method of a closed cursor * usually does not open it again because of the fact that its state * generally cannot be restored when resources are released respectively * files are closed.</p> * * <p>The current implementation of this method is as follows: * <code><pre> * public void open() { * if (!isOpened) * isOpened = true; * } * </pre></code></p> */ public void open() { if (!isOpened) isOpened = true; } /** * Closes the cursor, i.e., signals the cursor to clean up resources, close * files, etc. When a cursor has been closed calls to methods like * <code>next</code> or <code>peek</code> are not guaranteed to yield * proper results. Multiple calls to <code>close</code> do not have any * effect, i.e., if <code>close</code> was called the cursor remains in the * state <i>closed</i>. * * <p>Note, that a closed cursor usually cannot be opened again because of * the fact that its state generally cannot be restored when resources are * released respectively files are closed.</p> * * <p>The current implementation of this method is as follows: * <code><pre> * public void close() { * if (!isClosed) { * hasNext = false; * computedHasNext = false; * isValid = false; * isClosed = true; * } * } * </pre></code></p> */ public void close() { if (!isClosed) { hasNext = false; computedHasNext = false; isValid = false; isClosed = true; } } /** * Returns <code>true</code> if the cursor has been closed. * * @return <code>true</code> if the cursor has been closed. */ public boolean isClosed() { return isClosed; } /** * Returns <code>true</code> if the iteration has more elements. (In other * words, returns <code>true</code> if <code>next</code> or * <code>peek</code> would return an element rather than throwing an * exception.) * * <p>This operation is implemented idempotent, i.e., consequent calls to * <code>hasNext</code> do not have any effect.</p> * * <b>Important:</b> In order to guarantee a certain semantics, an * extension of an <code>AbstractCursor</code> has to ensure that a call of * <code>hasNext</code> always returns <code>false</code> after the first * time <code>false</code> is delivered. Thus, it should not be possible to * receive an element by calling <code>hasNext()</code> and * <code>next()</code> at a later point in time, if <code>hasNext()</code> * returned <tcodet>false</code> before (even if the underlying data * structure received a new element meanwhile). * * <p>The current implementation of this method is as follows: * <code><pre> * public final boolean hasNext() throws IllegalStateException { * if (isClosed) * throw new IllegalStateException(); * if (!isOpened) * open(); * if (!computedHasNext) { * hasNext = hasNextObject(); * computedHasNext = true; * isValid = false; * assignedNext = false; * } * return hasNext; * } * </pre></code></p> * * @return <code>true</code> if the cursor has more elements. * @throws IllegalStateException if the cursor is already closed when this * method is called. */ public final boolean hasNext() throws IllegalStateException { if (isClosed) throw new IllegalStateException(); if (!isOpened) open(); if (!computedHasNext) { hasNext = hasNextObject(); computedHasNext = true; isValid = false; assignedNext = false; } return hasNext; } /** * Returns <code>true</code> if the iteration has more elements. (In other * words, returns <code>true</code> if <code>next</code> or * <code>peek</code> would return an element rather than throwing an * exception.) * * <p>This abstract operation should implement the core functionality of * the <code>hasNext</code> method which secures that the cursor is in a * proper state when this method is called. Due to this the * <code>hasNextObject</code> method need not to deal with exception * handling.</p> * * @return <code>true</code> if the cursor has more elements. */ protected abstract boolean hasNextObject(); /** * Returns the next element in the iteration. This element will be * accessible by some of the cursor's methods, e.g., <code>update</code> or * <code>remove</code>, until a call to <code>next</code> or * <code>peek</code> occurs. This is calling <code>next</code> or * <code>peek</code> proceeds the iteration and therefore its previous * element will not be accessible any more. * * <p>The current implementation of this method is as follows: * <code><pre> * public final E next() throws IllegalStateException, * NoSuchElementException { * if (!computedHasNext) * hasNext(); * if (!hasNext) * throw new NoSuchElementException(); * if (!assignedNext) { * next = nextObject(); * assignedNext = true; * } * hasNext = false; * computedHasNext = false; * isValid = true; * return next; * } * </pre></code></p> * * @return the next element in the iteration. * @throws IllegalStateException if the cursor is already closed when this * method is called. * @throws NoSuchElementException if the iteration has no more elements. */ public final E next() throws IllegalStateException, NoSuchElementException { if (!computedHasNext) hasNext(); if (!hasNext) throw new NoSuchElementException(); if (!assignedNext) { next = nextObject(); assignedNext = true; } hasNext = false; computedHasNext = false; isValid = true; return next; } /** * Returns the next element in the iteration. This element will be * accessible by some of the cursor's methods, e.g., <code>update</code> or * <code>remove</code>, until a call to <code>next</code> or * <code>peek</code> occurs. This is calling <code>next</code> or * <code>peek</code> proceeds the iteration and therefore its previous * element will not be accessible any more. * * <p>This abstract operation should implement the core functionality of * the <code>next</code> method which secures that the cursor is in a * proper state when this method is called. Due to this the * <code>nextObject</code> method need not to deal with exception * handling.</p> * * @return the next element in the iteration. */ protected abstract E nextObject(); /** * Shows the next element in the iteration without proceeding the iteration * (optional operation). After calling <code>peek</code> the returned * element is still the cursor's next one such that a call to * <code>next</code> would be the only way to proceed the iteration. But be * aware that an implementation of this method uses a kind of * buffer-strategy, therefore it is possible that the returned element will * be removed from the <i>underlying</i> iteration, e.g., the caller can * use an instance of a cursor depending on an iterator, so the next * element returned by a call to <code>peek</code> will be removed from the * underlying iterator which does not support the <code>peek</code> * operation and therefore the iterator has to be wrapped and buffered. * * <p>Note, that this operation is optional and might not work for all * cursors. After calling the <code>peek</code> method a call to * <code>next</code> is strongly recommended.</p> * * <p>The current implementation of this method is as follows: * <code><pre> * public final E peek() throws IllegalStateException, * NoSuchElementException, * UnsupportedOperationException { * if (!supportsPeek()) * throw new UnsupportedOperationException(); * if (!computedHasNext) * hasNext(); * if (!hasNext) * throw new NoSuchElementException(); * if (!assignedNext) { * next = nextObject(); * assignedNext = true; * } * isValid = true; * return next; * } * </pre></code></p> * * @return the next element in the iteration. * @throws IllegalStateException if the cursor is already closed when this * method is called. * @throws NoSuchElementException iteration has no more elements. * @throws UnsupportedOperationException if the <code>peek</code> operation * is not supported by the cursor. */ public final E peek() throws IllegalStateException, NoSuchElementException, UnsupportedOperationException { if (!supportsPeek()) throw new UnsupportedOperationException(); if (!computedHasNext) hasNext(); if (!hasNext) throw new NoSuchElementException(); if (!assignedNext) { next = nextObject(); assignedNext = true; } isValid = true; return next; } /** * Returns <code>true</code> if the <code>peek</code> operation is * supported by the cursor. Otherwise it returns <code>false</code>. * * <p>The current implementation of this method is as follows: * <code><pre> * public final boolean supportsPeek() { * return true; * } * </pre></code></p> * * @return <code>true</code> if the <code>peek</code> operation is * supported by the cursor, otherwise <code>false</code>. */ public final boolean supportsPeek() { return true; } /** * Removes from the underlying data structure the last element returned by * the cursor (optional operation). This method can be called only once per * call to <code>next</code> or <code>peek</code> and removes the element * returned by this method. Note, that between a call to <code>next</code> * and <code>remove</code> the invocation of <code>peek</code> or * <code>hasNext</code> is forbidden. The behaviour of a cursor is * unspecified if the underlying data structure is modified while the * iteration is in progress in any way other than by calling this method. * * <p>Note, that this operation is optional and does not work for this * cursor.</p> * * <p>The current implementation of this method is as follows: * <code><pre> * public void remove() throws IllegalStateException, * UnsupportedOperationException { * if (!supportsRemove()) * throw new UnsupportedOperationException(); * if (!isValid) * throw new IllegalStateException(); * hasNext = false; * computedHasNext = false; * isValid = false; * assignedNext = false; * } * </pre></code></p> * * @throws IllegalStateException if the <code>next</code> or * <code>peek</code> method has not yet been called, or the * <code>remove</code> method has already been called after the * last call to the <code>next</code> or <code>peek</code> method. * @throws UnsupportedOperationException if the <code>remove</code> * operation is not supported by the cursor. */ public void remove() throws IllegalStateException, UnsupportedOperationException { if (!supportsRemove()) throw new UnsupportedOperationException(); if (!isValid) throw new IllegalStateException(); hasNext = false; computedHasNext = false; isValid = false; assignedNext = false; } /** * Returns <code>true</code> if the <code>remove</code> operation is * supported by the cursor. Otherwise it returns <code>false</code>. * * <p>The current implementation of this method is as follows: * <code><pre> * public boolean supportsRemove() { * return false; * } * </pre></code></p> * * @return <code>true</code> if the <code>remove</code> operation is * supported by the cursor, otherwise <code>false</code>. */ public boolean supportsRemove() { return false; } /** * Replaces the last element returned by the cursor in the underlying data * structure (optional operation). This method can be called only once per * call to <code>next</code> or <code>peek</code> and updates the element * returned by this method. Note, that between a call to <code>next</code> * and <code>update</code> the invocation of <code>peek</code> or * <code>hasNext</code> is forbidden. The behaviour of a cursor is * unspecified if the underlying data structure is modified while the * iteration is in progress in any way other than by calling this method. * * <p>Note, that this operation is optional and does not work for this * cursor.</p> * * <p>The current implementation of this method is as follows: * <code><pre> * public void update(E object) throws IllegalStateException, * UnsupportedOperationException { * if (!supportsUpdate()) * throw new UnsupportedOperationException(); * if (!isValid) * throw new IllegalStateException(); * hasNext = false; * computedHasNext = false; * isValid = false; * assignedNext = false; * } * </pre></code></p> * * @param object the object that replaces the last element returned by the * cursor. * @throws IllegalStateException if the <code>next</code> or * <code>peek</code> method has not yet been called, or the * <code>update</code> method has already been called after the * last call to the <code>next</code> or <code>peek</code> method. * @throws UnsupportedOperationException if the <code>update</code> * operation is not supported by the cursor. */ public void update(E object) throws IllegalStateException, UnsupportedOperationException { if (!supportsUpdate()) throw new UnsupportedOperationException(); if (!isValid) throw new IllegalStateException(); hasNext = false; computedHasNext = false; isValid = false; assignedNext = false; } /** * Returns <code>true</code> if the <code>update</code> operation is * supported by the cursor. Otherwise it returns <code>false</code>. * * <p>The current implementation of this method is as follows: * <code><pre> * public boolean supportsUpdate() { * return false; * } * </pre></code></p> * * @return <code>true</code> if the <code>update</code> operation is * supported by the cursor, otherwise <code>false</code>. */ public boolean supportsUpdate() { return false; } /** * Resets the cursor to its initial state such that the caller is able to * traverse the underlying data structure again without constructing a new * cursor (optional operation). The modifications, removes and updates * concerning the underlying data structure, are still persistent. * * <p>Note, that this operation is optional and does not work for this * cursor.</p> * * <p>The current implementation of this method is as follows: * <code><pre> * public void reset() throws IllegalStateException, * UnsupportedOperationException { * if (!supportsReset()) * throw new UnsupportedOperationException(); * hasNext = false; * computedHasNext = false; * isValid = false; * assignedNext = false; * } * </pre></code></p> * * @throws UnsupportedOperationException if the <code>reset</code> * operation is not supported by the cursor. */ public void reset() throws UnsupportedOperationException { if (!supportsReset()) throw new UnsupportedOperationException(); hasNext = false; computedHasNext = false; isValid = false; assignedNext = false; } /** * Returns <code>true</code> if the <code>reset</code> operation is * supported by the cursor. Otherwise it returns <code>false</code>. * * <p>The current implementation of this method is as follows: * <code><pre> * public boolean supportsReset() { * return false; * } * </pre></code></p> * * @return <code>true</code> if the <code>reset</code> operation is * supported by the cursor, otherwise <code>false</code>. */ public boolean supportsReset() { return false; } }