/* 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.Iterator;
import java.util.NoSuchElementException;
/**
* This class decorates an object implementing the interface
* {@link xxl.core.cursors.Cursor} such that all method calls on the decorator
* are passed to its decorated object. For these purposes it realizes the design
* pattern, named <i>Decorator</i>. The intent of this design pattern is to
* attach additional responsibilities to an object dynamically. So decorators
* provide a flexible alternative to subclassing for extending functionality.
* For further information see: "Gamma et al.: <i>DesignPatterns. Elements of
* Reusable Object-Oriented Software.</i> Addision Wesley 1998."
*
* <p><b>Note:</b> When the given input iteration only implements the interface
* {@link java.util.Iterator} it is wrapped to a cursor by a call to the static
* method {@link xxl.core.cursors.Cursors#wrap(Iterator) wrap}. For guaranteeing
* the proper functionality of this decorator cursor's
* <tt>open</tt>/<tt>close</tt> and <tt>hasNext</tt>/<tt>next</tt> mechanisms,
* it reproduces the mechanisms implemented in the class
* {@link xxl.core.cursors.AbstractCursor}.</p>
*
* <p>The usage of this class can be seen for example in the classes:<br />
* <ul>
* <li>
* {@link xxl.core.cursors.filters.Dropper Dropper}
* </li>
* <li>
* {@link xxl.core.cursors.filters.Filter Filter}
* </li>
* <li>
* {@link xxl.core.cursors.filters.Remover Remover}
* </li>
* <li>
* {@link xxl.core.cursors.filters.Taker Taker}
* </li>
* </ul></p>
*
* <p><b>Note:</b> When overwriting a method from SecureDecoratorCursor there
* must always be a call to the super method.</p>
*
* @param <E> the type of the elements returned by this iteration.
* @see java.util.Iterator
* @see xxl.core.cursors.Cursor
*/
public abstract class SecureDecoratorCursor<E> implements Cursor<E> {
/**
* A flag indicating whether the iteration has more elements.
*/
protected boolean hasNext = false;
/**
* A flag indicating whether the <tt>hasNext</tt> 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
* <tt>next</tt> or <tt>peek</tt> method is valid any longer, i.e., it has
* not been removed or updated since that time.
*/
protected boolean isValid = true;
/**
* 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;
/**
* The decorated cursor that is used for passing method calls to.
*/
protected Cursor<E> cursor;
/**
* Creates a new decorator-cursor. The given iterator is wrapped to a cursor
* by calling the method
* {@link xxl.core.cursors.Cursors#wrap(java.util.Iterator)} and all method
* calls are finally passed to it.
*
* @param iterator the iterator to be decorated.
*/
public SecureDecoratorCursor(Iterator<E> iterator) {
this.cursor = Cursors.wrap(iterator);
}
/**
* Returns the cursor decorated by this decorator cursor, that is used for
* passing method calls to. Note that is returned cursor and the iterator
* submitted at construction time are usually not the same, because the
* submitted iterator will be wrapped by a call to the method
* {@link xxl.core.cursors.Cursors#wrap(java.util.Iterator)}.
*
* @return the cursor decorated by this decorator cursor.
*/
public Cursor<E> getDecoratedCursor() {
return cursor;
}
/**
* Opens the cursor, i.e., signals the cursor to reserve resources, open
* files, etc. Before a cursor has been opened calls to methods like
* <tt>next</tt> or <tt>peek</tt> are not guaranteed to yield proper
* results. Therefore <tt>open</tt> must be called before a cursor's data
* can be processed. Multiple calls to <tt>open</tt> do not have any effect,
* i.e., if <tt>open</tt> was called the cursor remains in the state
* <i>opened</i> until its <tt>close</tt> method is called.
*
* <p>Note, that a call to the <tt>open</tt> 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>
*/
public void open() {
if(!isOpened) {
isOpened = true;
cursor.open();
}
}
/**
* 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
* <tt>next</tt> or <tt>peek</tt> are not guaranteed to yield proper
* results. Multiple calls to <tt>close</tt> do not have any effect, i.e.,
* if <tt>close</tt> 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>
*/
public void close() {
if (!isClosed) {
hasNext = false;
computedHasNext = false;
isValid = false;
isClosed = true;
cursor.close();
}
}
/**
* 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>This operation is implemented idempotent, i.e., consequent calls to
* <tt>hasNext</tt> do not have any effect.</p>
*
* @return <tt>true</tt> if the cursor has more elements.
* @throws IllegalStateException if the cursor is already closed when this
* method is called.
*/
public boolean hasNext() throws IllegalStateException {
if (isClosed)
throw new IllegalStateException();
if (!isOpened)
open();
if (!computedHasNext) {
hasNext = cursor.hasNext();
computedHasNext = true;
isValid = false;
}
return hasNext;
}
/**
* Returns the next element in the iteration. This element will be
* accessible by some of the cursor'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.
* @throws IllegalStateException if the cursor is already closed when this
* method is called.
* @throws NoSuchElementException if the iteration has no more elements.
*/
public E next() throws IllegalStateException, NoSuchElementException {
if (!computedHasNext)
hasNext();
if (!hasNext)
throw new NoSuchElementException();
hasNext = false;
computedHasNext = false;
isValid = true;
return cursor.next();
}
/**
* Shows the next element in the iteration without proceeding the iteration
* (optional operation). After calling <tt>peek</tt> the returned element is
* still the cursor's next one such that a call to <tt>next</tt> 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 <tt>peek</tt> will be removed from the underlying iterator which does
* not support the <tt>peek</tt> 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 <tt>peek</tt> method a call to <tt>next</tt>
* is strongly recommended.</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 <tt>peek</tt> operation is
* not supported by the cursor.
*/
public E peek() throws IllegalStateException, NoSuchElementException, UnsupportedOperationException {
if (!supportsPeek())
throw new UnsupportedOperationException();
if (!computedHasNext)
hasNext();
if (!hasNext)
throw new NoSuchElementException();
isValid = true;
return cursor.peek();
}
/**
* Returns <tt>true</tt> if the <tt>peek</tt> operation is supported by the
* cursor. Otherwise it returns <tt>false</tt>.
*
* @return <tt>true</tt> if the <tt>peek</tt> operation is supported by the
* cursor, otherwise <tt>false</tt>.
*/
public boolean supportsPeek() {
return cursor.supportsPeek();
}
/**
* 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 <tt>next</tt> or <tt>peek</tt> and removes the element returned
* by this method. Note, that between a call to <tt>next</tt> and
* <tt>remove</tt> the invocation of <tt>peek</tt> or <tt>hasNext</tt> 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 might not work for all
* cursors.</p>
*
* @throws IllegalStateException if the <tt>next</tt> or <tt>peek</tt> method
* has not yet been called, or the <tt>remove</tt> method has already
* been called after the last call to the <tt>next</tt> or
* <tt>peek</tt> method.
* @throws UnsupportedOperationException if the <tt>remove</tt> 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;
cursor.remove();
}
/**
* Returns <tt>true</tt> if the <tt>remove</tt> operation is supported by
* the cursor. Otherwise it returns <tt>false</tt>.
*
* @return <tt>true</tt> if the <tt>remove</tt> operation is supported by
* the cursor, otherwise <tt>false</tt>.
*/
public boolean supportsRemove() {
return cursor.supportsRemove();
}
/**
* 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 <tt>next</tt> or <tt>peek</tt> and updates the element returned
* by this method. Note, that between a call to <tt>next</tt> and
* <tt>update</tt> the invocation of <tt>peek</tt> or <tt>hasNext</tt> 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 might not work for all
* cursors.</p>
*
* @param object the object that replaces the last element returned by the
* cursor.
* @throws IllegalStateException if the <tt>next</tt> or <tt>peek</tt> method
* has not yet been called, or the <tt>update</tt> method has already
* been called after the last call to the <tt>next</tt> or
* <tt>peek</tt> method.
* @throws UnsupportedOperationException if the <tt>update</tt> 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;
cursor.update(object);
}
/**
* Returns <tt>true</tt> if the <tt>update</tt> operation is supported by
* the cursor. Otherwise it returns <tt>false</tt>.
*
* @return <tt>true</tt> if the <tt>update</tt> operation is supported by
* the cursor, otherwise <tt>false</tt>.
*/
public boolean supportsUpdate() {
return cursor.supportsUpdate();
}
/**
* 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 might not work for all
* cursors.</p>
*
* @throws UnsupportedOperationException if the <tt>reset</tt> operation is
* not supported by the cursor.
*/
public void reset() throws UnsupportedOperationException {
if (!supportsReset())
throw new UnsupportedOperationException();
hasNext = false;
computedHasNext = false;
isValid = false;
cursor.reset();
}
/**
* Returns <tt>true</tt> if the <tt>reset</tt> operation is supported by
* the cursor. Otherwise it returns <tt>false</tt>.
*
* @return <tt>true</tt> if the <tt>reset</tt> operation is supported by
* the cursor, otherwise <tt>false</tt>.
*/
public boolean supportsReset() {
return cursor.supportsReset();
}
}