/* 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; import java.util.Observable; import xxl.core.functions.Function; import xxl.core.functions.Identity; import xxl.core.predicates.Predicate; import xxl.core.predicates.Predicates; /** * This class provides a wrapper for {@link java.util.Iterator iterators} with * the intention to {@link java.util.Observer observe} them. The notification * can be controlled using a {@link xxl.core.predicates.Predicate predicate} * that decides whether the observers should be notified about the currently * delivered object by the wrapped iterator. Moreover a * {@link xxl.core.functions.Function function} could be used to map the * currently delivered object to informations the oberservers should get. * * <p><b>Example usage (1):</b> * <code><pre> * ObservableIterator<Integer> iterator = new ObservableIterator<Integer>( * new new DiscreteRandomNumber(new JavaDiscreteRandomWrapper(10), 100), * Functions.aggregateUnaryFunction( * new CountAll() * ) * ); * Observer observer = new Observer() { * public void update(Observable observable, Object object) { * System.out.println("getting " + object + " from " + observable); * } * }; * iterator.addObserver(observer); * * while (iterator.hasNext()) * System.out.println("next=" + iterator.next()); * </pre></code> * This example illustrates how an iteration over random integers gets * observed, whereas the observer only sees the results of an unary aggregation * function, namely a count of all elements delivered by the wrapped cursor. * </p> * * @param <E> the type of the elements returned by this iteration. * @see java.util.Iterator * @see java.util.Observable * @see java.util.Observer */ public class ObservableIterator<E> extends Observable implements Iterator<E> { /** * The iterator to observe. */ protected Iterator<? extends E> iterator; /** * A predicate that determines whether the observers should be notified or * not. */ protected Predicate<? super E> predicate; /** * A function that maps the object received by the wrapped iterator to the * object handed out to the observers. */ protected Function<? super E, ?> function; /** * The last object that has been delivered by this iterator. */ protected E next; /** * Constructs an observable iterator. * * @param iterator the iterator to observe. * @param function function that maps the object received by the wrapped * iterator to the object handed out to the observers. * @param predicate predicate that determines whether the observers should * be notified about the currently received object or not. */ public ObservableIterator(Iterator<? extends E> iterator, Function<? super E, ?> function, Predicate<? super E> predicate) { this.iterator = iterator; this.predicate = predicate; this.function = function; } /** * Constructs an observable iterator. By using this constructor every seen * object of this iterator will be passed to the registerd observers * meaning the constant TRUE will be used as predicate for determining if * the observers should be notified. * * @param iterator the iterator to observe. * @param function function that maps the object received by the wrapped * iterator to the object handed out to the observers. */ public ObservableIterator(Iterator<? extends E> iterator, Function<? super E, ?> function) { this(iterator, function, Predicates.TRUE); } /** * Constructs an observable iterator. By using this constructor every * object itself will be passed to the registered observers meaning the * identity function will be used to map the seen objects. * * @param iterator the iterator to observe. * @param predicate predicate that determines whether the observers should * be notified about the currently received object or not. */ public ObservableIterator(Iterator<? extends E> iterator, Predicate<? super E> predicate) { this(iterator, Identity.DEFAULT_INSTANCE, predicate); } /** * Constructs an observable iterator. Every seen object will be passed to * the registered observers. * * @param iterator the iterator to observe. */ public ObservableIterator(Iterator<? extends E> iterator) { this(iterator, Identity.DEFAULT_INSTANCE, Predicates.TRUE); } /** * Returns true if the wrapped iteration has more elements. * * @return true if the iterator has more elements */ public boolean hasNext() { return iterator.hasNext(); } /** * By calling the method <code>next</code> the next object of the wrapped * iterator will be returned. All registerd observers will be notified if * and only if the given predicate returns <code>true</code> for this * object. Furthermore all observers receive only a transformed object * given by the transformation function. * * @throws NoSuchElementException if the wrapped iterator has no further * objects. * @return the next object of the wrapped iterator. */ public E next() throws NoSuchElementException { next = iterator.next(); if (predicate.invoke(next)) { setChanged(); notifyObservers(function.invoke(next)); } return next; } /** * By calling remove the last seen object of the wrapped iterator is * removed (optional operation). * * <p><b>Note</b> that the last delivered object will be removed, * <b>not</b> the last object notified to the observers.</p> * * @throws UnsupportedOperationException if remove is not supported by the * wrapped iterator. */ public void remove() throws UnsupportedOperationException { iterator.remove(); if (predicate.invoke(next)) { setChanged(); notifyObservers(); } } }