/* 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.relational.cursors;
import java.sql.ResultSet;
import java.util.Arrays;
import java.util.NoSuchElementException;
import xxl.core.cursors.MetaDataCursor;
import xxl.core.cursors.SecureDecoratorCursor;
import xxl.core.functions.Function;
import xxl.core.relational.metaData.ProjectedResultSetMetaData;
import xxl.core.relational.metaData.ResultSetMetaDatas;
import xxl.core.relational.tuples.ArrayTuple;
import xxl.core.relational.tuples.Tuple;
import xxl.core.util.metaData.CompositeMetaData;
/**
* Straight forward implementation of the operator projection.
*
* <p>In earlier versions of XXL it was possible to hand over a string array to
* the constructor of the operators instead of an array of indices. To get this
* functionality, use
* {@link xxl.core.relational.resultSets.ResultSets#getColumnIndices(ResultSet, String[])}.</p>
*
* <p>The example in the main method wraps an iteration containing arrays of
* five <code>int</code> values <tt>[i, i+1, i+2, i+3, i+4]
* to a metadata cursor using
* {@link xxl.core.cursors.Cursors#wrapToMetaDataCursor(java.util.Iterator, Object)}.
* Then, the column becomes projected and the cursor is printed on
* System.out. The interesting call is:
* <code><pre>
* cursor = new Projection(
* cursor,
* ArrayTuple.FACTORY_METHOD,
* 1,
* 3,
* 5
* );
* </pre></code>
*/
public class Projection extends SecureDecoratorCursor<Tuple> implements MetaDataCursor<Tuple, CompositeMetaData<Object, Object>> {
/**
* A function used for projecting the tuples (their columns).
*/
protected xxl.core.functions.Projection<Object> projection;
/**
* A function that maps a list of objects (column values) to a tuple.
*/
protected Function<Object, ? extends Tuple> tupleFactory;
/**
* The metadata provided by the projection.
*/
protected CompositeMetaData<Object, Object> globalMetaData;
/**
* Creates a new instance of projection. The projection of the tuples
* derived from the underlying cursor is done by the given
* projection-function.
*
* @param cursor the input metadata cursor delivering the input elements.
* @param tupleFactory a function that maps a list of objects (column
* values) to a new tuple. Classes implementing the Tuple interface
* should provide factory methods for this task. If
* <code>null</code> is passed, a factory method producing
* {@link ArrayTuple array-tuples} is used.
* @param projection a function that maps an object array to a projected
* object array.
*/
public Projection(MetaDataCursor<Tuple, CompositeMetaData<Object, Object>> cursor, Function<Object, ? extends Tuple> tupleFactory, xxl.core.functions.Projection<Object> projection) {
super(cursor);
globalMetaData = new CompositeMetaData<Object, Object>();
globalMetaData.add(
ResultSetMetaDatas.RESULTSET_METADATA_TYPE,
new ProjectedResultSetMetaData(
ResultSetMetaDatas.getResultSetMetaData(cursor),
xxl.core.util.Arrays.incrementIntArray(projection.getIndices(), 1)
)
);
this.projection = projection;
this.tupleFactory = tupleFactory == null ? ArrayTuple.FACTORY_METHOD : tupleFactory;
}
/**
* Creates a new instance of projection. The tuples derived from the
* underlying metadata cursor are projected onto the specified columns.
*
* @param cursor the input metadata cursor delivering the input elements.
* @param tupleFactory a function that maps a list of objects (column
* values) to a new tuple. Classes implementing the Tuple interface
* should provide factory methods for this task. If
* <code>null</code> is passed, a factory method producing
* {@link ArrayTuple array-tuples} is used.
* @param columns an array of column numbers the tuples are projected onto.
*/
public Projection(MetaDataCursor<Tuple, CompositeMetaData<Object, Object>> cursor, Function<Object, ? extends Tuple> tupleFactory, int... columns) {
this(cursor, tupleFactory, new xxl.core.functions.Projection<Object>(xxl.core.util.Arrays.decrementIntArray(columns, 1)));
}
/**
* Creates a new instance of projection. The projection of the tuples
* derived from the underlying result set is done by the given
* projection-function.
*
* @param resultSet the input result set delivering the input elements.
* @param tupleFactory a function that maps a list of objects (column
* values) to a new tuple. Classes implementing the Tuple interface
* should provide factory methods for this task. If
* <code>null</code> is passed, a factory method producing
* {@link ArrayTuple array-tuples} is used.
* @param projection a function that maps an object array to a projected
* object array.
*/
public Projection(ResultSet resultSet, Function<Object, ? extends Tuple> tupleFactory, xxl.core.functions.Projection<Object> projection) {
this(new ResultSetMetaDataCursor(resultSet), tupleFactory, projection);
}
/**
* Creates a new instance of projection. The tuples derived from the
* underlying result set are projected onto the specified columns.
*
* @param resultSet the input result set delivering the input elements.
* @param tupleFactory a function that maps a list of objects (column
* values) to a new tuple. Classes implementing the Tuple interface
* should provide factory methods for this task. If
* <code>null</code> is passed, a factory method producing
* {@link ArrayTuple array-tuples} is used.
* @param columns an array of column numbers the tuples are projected
* onto.
*/
public Projection(ResultSet resultSet, Function<Object, ? extends Tuple> tupleFactory, int... columns) {
this(new ResultSetMetaDataCursor(resultSet), tupleFactory, columns);
}
/**
* 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.
*/
@Override
public Tuple peek() throws IllegalStateException, NoSuchElementException, UnsupportedOperationException {
return tupleFactory.invoke(Arrays.asList(projection.invoke(super.peek().toArray())));
}
/**
* 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.
*/
@Override
public Tuple next() throws IllegalStateException, NoSuchElementException {
return tupleFactory.invoke(Arrays.asList(projection.invoke(super.next().toArray())));
}
/**
* Returns the metadata information for this metadata-cursor as a composite
* metadata ({@link CompositeMetaData}).
*
* @return the metadata information for this metadata-cursor as a composite
* metadata ({@link CompositeMetaData}).
*/
public CompositeMetaData<Object, Object> getMetaData() {
return globalMetaData;
}
}