/* 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.mappers;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import xxl.core.cursors.AbstractCursor;
import xxl.core.cursors.Cursor;
import xxl.core.cursors.Cursors;
import xxl.core.functions.Function;
/**
* A mapper invokes a given mapping function on an array of input iterations.
* The mapping function is applied to <code>n</code> input iterations at the
* same time, that means a <code>n</code>-dimensional function is called and
* its arguments are the elements of each input iteration (an array storing one
* element per input iteration). The result of this mapping is an array storing
* the results of the different functions that is returned by the mapper. Also
* a partial input is allowed with the intention to apply the mapping function
* on less than <code>n</code> arguments.
*
* <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}. Additionally
* some mapping functions are supplied in the class
* {@link xxl.core.functions.Functions}.</p>
*
* <p><b>Example usage (1):</b>
* <pre>
* Mapper<Integer, Integer> mapper = new Mapper<Integer, Integer>(
* new Function;Integer, Integer>() {
* public Integer invoke(Integer[] arguments) {
* return arguments[0] * 2;
* }
* },
* new Enumerator(21)
* );
*
* mapper.open();
*
* while (mapper.hasNext())
* System.out.print(mapper.next() + "; ");
* System.out.flush();
*
* mapper.close();
* </pre>
* This mapper maps the given numbers of the enumerator with range 0,...,20
* concerning the above defined function
* <pre>
* f : x --> 2*x.
* </pre>
* The function is applied on each element of the given enumerator, therefore
* the following output is printed to the output stream:
* <pre>
* 0; 2; 4; 6; 8; 10; ... ; 36; 38; 40;
* </pre>
* But pay attention that the function's <tt>invoke</tt> method gets an object
* array as parameter!</p>
*
* <p><b>Example usage (2):</b>
* <pre>
* HashGrouper<Integer> hashGrouper = new HashGrouper<Integer>(
* new Function<Integer, Integer>() {
* public Integer invoke(Integer next) {
* return next % 5;
* }
* },
* new Enumerator(21)
* );
*
* hashGrouper.open();
*
* Cursor<Integer>[] cursors = (Cursor<Integer>[])new Object[5];
* for (int i = 0; hashGrouper.hasNext(); i++)
* cursors[i] = hashGrouper.next();
*
* mapper = new Mapper<Integer, Integer>(
* cursors,
* new Function<Integer, Integer>() {
* public Integer invoke(Integer[] arguments) {
* return Cursors.minima(new ArrayCursor<Integer>(arguments)).getFirst();
* }
* }
* );
*
* mapper.open();
*
* while (mapper.hasNext())
* System.out.print(mapper.next() + "; ");
* System.out.flush();
*
* mapper.close();
* hashGrouper.close();
* </pre>
* This example uses the a hash-grouper to partition the delivered numbers of
* the input enumerator. For further information see
* {@link xxl.core.cursors.groupers.HashGrouper}. The buckets of the hash-map
* used by the hash-grouper are stored in the cursor list <code>cursors</code>.
* Then a new mapper is created that maps this cursor array to the first
* element of its contained minima. Therefore the static method
* <code>minima</code> is used returning a linked list, where the
* <code>getFirst</code> method is applied. For the first call to
* <code>next</code> the cursor array contains the integer objects with value
* 0,...,4, namely the first element of each bucket. The returned minimum is 0.
* The next call to this method returns the minimum of 5,...,10, namely the
* second element in each bucket. So the output of this use case is:
* <pre>
* 0; 5; 10; 15;
* </pre>
*
* @param <I> the type of the elements consumed by this iteration.
* @param <E> the type of the elements returned by this mapped iteration.
* @see java.util.Iterator
* @see xxl.core.cursors.Cursor
* @see xxl.core.cursors.AbstractCursor
*/
public class Mapper<I, E> extends AbstractCursor<E> {
/**
* The list of input iterations holding the data to be mapped.
*/
protected List<Cursor<? extends I>> inputs;
/**
* The function used to map the input-elements to an output-element.
*/
protected Function<? super I, ? extends E> function;
/**
* The arguments the function is applied to.
*/
protected List<I> arguments = null;
/**
* A flag to detect if the function can be applied to less than one element
* of each input iteration.
*/
protected boolean allowPartialInput = true;
/**
* Creates a new mapper using an input iteration array and a user defined
* function to map the elements. The flag <code>allowPartialInput</code>
* defines whether the function can be used with a lower dimension than
* <code>iterators.length</code>. Every iterator to this constructor is
* wrapped to a cursor.
*
* @param iterators the input iterations.
* @param function the function used to map the input-elements to an
* output-element.
* @param allowPartialInput <code>true</code> if the function can be
* applied to less than one element of each input iterator.
*/
public Mapper(Function<? super I, ? extends E> function, boolean allowPartialInput, Iterator<? extends I>... iterators) {
this.inputs = new ArrayList<Cursor<? extends I>>(iterators.length);
for (Iterator<? extends I> iterator : iterators)
inputs.add(Cursors.wrap(iterator));
this.function = function;
this.allowPartialInput = allowPartialInput;
}
/**
* Creates a new mapper using the given input iteration array and user
* defined function to map the elements. Every iterator given to this
* constructor is wrapped to a cursor and no partial input is allowed.
*
* @param iterators the input iterations.
* @param function the function used to map the input-elements to an
* output-element.
*/
public Mapper(Function<? super I, ? extends E> function, Iterator<? extends I>... iterators) {
this(function, false, iterators);
}
/**
* Opens the mapper, i.e., signals the cursor to reserve resources, open
* input iterations, 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>
*/
public void open() {
if (isOpened)
return;
super.open();
for (Cursor<?> input : inputs)
input.open();
}
/**
* Closes the mapper. Signals the mapper to clean up resources, close input
* iterations, etc. After a call to <code>close</code> calls to methods
* like <code>next</code> or <code>peek</code> are not guarantied to yield
* proper results. Multiple calls to <code>close</code> do not have any
* effect, i.e., if <code>close</code> was called the mapper remains in the
* state "closed".
*/
public void close() {
if (isClosed)
return;
super.close();
for (Cursor<?> input : inputs)
input.close();
}
/**
* 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 method returns <code>true</code> if all input iterations have
* more elements or if at least one input iteration has more elements and
* partial input is allowed.</p>
*
* @return <code>true</code> if the mapper has more elements.
*/
protected boolean hasNextObject() {
int count = 0;
for (Cursor<?> input : inputs)
if (input.hasNext())
count++;
return count == 0 ?
false :
count == inputs.size() ?
true :
allowPartialInput;
}
/**
* Computes the next element to be returned by a call to <code>next</code>
* or <code>peek</code>.
*
* <p>A new object array <code>arguments</code> with length
* <code>size()</code> is assigned and filled with the next elements of the
* input iterations or <code>null</code>, if the next element of such an
* input iteration does not exist. Then the mapping function is applied and
* the next element to be returned by the mapper is returned.</p>
*
* @return the next element in the iteration.
*/
protected E nextObject() {
arguments = new ArrayList<I>(inputs.size());
for (Cursor<? extends I> input : inputs)
arguments.add(input.hasNext() ? input.next() : null);
return function.invoke(arguments);
}
/**
* Removes from the underlying data structure the last element returned by
* the mapper (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 might not work for all
* cursors.</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 {
super.remove();
for (Cursor<?> input : inputs)
input.remove();
}
/**
* Returns <code>true</code> if the <code>remove</code> operation is
* supported by the mapper. Otherwise it returns <code>false</code>.
*
* @return <code>true</code> if the <code>remove</code> operation is
* supported by the mapper, otherwise <code>false</code>.
*/
public boolean supportsRemove() {
boolean supportsRemove = true;
for (Cursor<?> input : inputs)
supportsRemove &= input.supportsRemove();
return supportsRemove;
}
/**
* Resets the mapper 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 <code>reset</code>
* operation is not supported by the mapper.
*/
public void reset() throws UnsupportedOperationException {
super.reset();
for (Cursor<?> input : inputs)
input.reset();
}
/**
* Returns <code>true</code> if the <code>reset</code> operation is
* supported by the mapper. Otherwise it returns <code>false</code>.
*
* @return <code>true</code> if the <code>reset</code> operation is
* supported by the mapper, otherwise <code>false</code>.
*/
public boolean supportsReset() {
boolean supportsReset = true;
for (Cursor<?> input : inputs)
supportsReset &= input.supportsReset();
return supportsReset;
}
}