/* 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.xxql; import java.lang.reflect.Array; import java.lang.reflect.Method; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import xxl.core.collections.MapEntry; import xxl.core.cursors.AbstractCursor; import xxl.core.cursors.Cursor; import xxl.core.cursors.Cursors; import xxl.core.cursors.MetaDataCursor; import xxl.core.cursors.wrappers.IteratorCursor; import xxl.core.functions.AbstractFunction; import xxl.core.functions.Function; import xxl.core.relational.cursors.ResultSetMetaDataCursor; import xxl.core.relational.metaData.ColumnMetaData; import xxl.core.relational.metaData.ResultSetMetaDatas; import xxl.core.relational.tuples.ArrayTuple; import xxl.core.relational.tuples.Tuple; import xxl.core.util.metaData.CompositeMetaData; import xxl.core.xxql.columns.Column; import xxl.core.xxql.columns.ColumnUtils; /** * A Cursor wrapping another Cursor/Iterator/Iterable to a Tuple Cursor * with AdvResultSetMetaData and CompositeMetaData. The central class of "LINQ for XXL" with * join, union, where, select, etc as methods. * */ @SuppressWarnings("static-access") // shut up, dummy, we're doing this on purpose public class AdvTupleCursor extends AbstractCursor<Tuple> implements MetaDataCursor<Tuple, CompositeMetaData<Object, Object>>, Iterable<Tuple>{ /** * metadata, compatible to ResultSetMetaData - has to match the tuples contained in this cursor! */ AdvResultSetMetaData metadata; /** * The compositeMetaData required by the cursors in the relational package */ CompositeMetaData<Object, Object> compositeMetaData=null; /** * An Object of a class implementing the operators (join, where, select etc).<br> * Might be changes with setOperatorImpl() to allow using of custom operator implementations. */ protected OperatorImplementation operatorImpl = OperatorImplementation.getOperatorImpl(); /** * The wrapped Cursor */ protected Cursor<?> wrappedCursor=null; /** * This Function is used to create a AdvTuple from a given Object (e.g. for mapping a given * Cursor's elements to an AdvTuple returned by this Cursor) */ protected Function<Object, Tuple> objToTuple; /** * If caching isn't disabled, the elements of the wrapped Cursor will be mapped to AdvTuple * and saved in this list. */ protected List<Tuple> tuples = new LinkedList<Tuple>(); /** * While this is true (and if caching isn't disabled) we will fill the * {@link AdvTupleCursor#tuples tuples List} on each next() */ protected boolean firstRun=true; /** * true, if the cursor has already delivered an element (i.e. it's too late to change the * caching strategy) */ protected boolean inUse=false; /** * If the {@link AdvTupleCursor#tuples tuples List} is done (after first "run" of the cursor) * this will be an iterator from it used in this cursors * {@link AdvTupleCursor#hasNextObject() hasNextObject()} and * {@link AdvTupleCursor#nextObject() nextObject()} methods. On * {@link AdvTupleCursor#reset() reset()} a new iterator is obtained from the list. */ protected Iterator<Tuple> tupleIt; /** * If this is true, the tuples-List will not be used, i.e. the query will be re-evaluated * after reset(). That is necessary if this is a (potentially correlated) subquery. */ protected boolean doNotCache = true; // if true, we'll cache right father on SMART caching boolean isExpensive = false; /** * A list of Objects implementing {@link CorrTuplesReceiver} (atm only Predicates from WHERE).<br> * In this list all CorrTupleReceivers of this Query (this Cursor and it's parents) are collected * so EXISTS etc can set the correlated Tuples in their (possibly correlated) subqueries. */ List<CorrTuplesReceiver> corrTuplesReceivers = new LinkedList<CorrTuplesReceiver>(); /** * the "parents" of this Cursor. for example on <br> * <code> cur1.join(cur2, pred, "blah").select(...)</code><br> * cur1 and cur2 are parents of the cursor returned by join, which itself is the parent of * the cursor returned by select.<br> * References to them are needed to pass on information to all Cursors within a query.<br> * This is used to disable caching for the whole Cursor on subqueries.<br> * The "root" cursor(s) of a query have parents set to null (in the example above cur1 and * cur2 are the roots). */ AdvTupleCursor[] parents=null; /** * The child of this cursor. For example on <br> * <code> cur1.join(cur2, pred, "blah").select(...)</code><br> * the cursor returned by select is the child of the one returned by join, which is the child * of cur1 and cur2. */ List<AdvTupleCursor> children=null; /** * The Caching-Strategy to be used in a query's {@link AdvTupleCursor}s:<br/> * <ul> * <li><b>ALL:</b> Cache within all operators of a query</li> * <li><b>NONE:</b> Don't cache at all</li> * <li><b>ONLY_FIRST:</b> Only cache the first cursor so the Elements of the Input-Cursor don't * have to be mapped to Tuples again after reset(). This is default.</li> * <li><b>MAYBE_FIRST:</b> Cache the first cursor if its wrapped cursor is not resettable</li> * <li><b>ONLY_LAST:</b> Only cache the last cursor. Makes sense for "inner" cursors of joins, * differences, intersections</li> * <li><b>SMART:</b> Cache the last cursor of the query and also the last cursors queries that are * joined/intersected/subtracted with/from this query</li> * <li><b>SMART_NOTLAST:</b> Like SMART, but don't cache the last cursor in the query * </ul> * <h3>Example:</h3> * Consider the following Code: <pre><code> * List{@code <SomeType>} mylist = ...; // some list containing Objects of an arbitrary class * AdvTupleCursor fooCur = new AdvTupleCursor(someList, "foo"); * AdvTupleCursor barCur = new AdvTupleCursor(mylist, "bar") * .where( col("bar.x").LEQ(val(42)) ) * .join(<b>fooCur</b>, col("foo.a").EQ(col("bar.y"), "joinedCursor") * .select("selectedCursor", col("foo.a"), col("bar.x")); * * barCur.setCachingStrategy(CachingStrategy.ALL); // or NONE, SMART, ... * </code></pre> * * <i>barCur</i> will consist of multiple AdvTupleCursors: The first one is the new cursor created * from mylist, the second one is the one created by where(..), the third one is created by * the join with the "foo" cursor, which itself is the fifth one, and the fourth (and in this case last) * one is the one created by select (with the name "selectedCursor").<br> * With the <b>ALL</b> caching strategy, all five cursors will have an internal list containing their tuples * (it's created in the first run before the first reset(), after that the cursor will simply iterate * through that list). This is pretty fast, but needs most memory (all those lists have some * overhead..) and doesn't make any sense most of the time.<br> * With the <b>NONE</b> caching strategy, none of the cursors will use such an internal list, so on * each reset() the cursors (especially the "foo"-cursor, which is reset() <i>n</i> times in the join..) * need to be re-evaluated. This can be quite expensive, especially for the initial mapping of * the elements in the list to {@link Tuple}s and in joins, differences etc. But it needs least * memory.<br> * <i>NOTE</i>: If the input-cursor/iterator of the first cursor is not resettable, you should use the * MAYBE_FIRST-strategy, if you want to get an resettable cursor. See below for operations that need * a resettable cursor.<br> * With the <b>ONLY_FIRST</b> caching strategy only the first cursor ("bar") saves it's tuples in * an internal list, so the mapping from <i>SomeType</i> to {@link Tuple} doesn't have to be done * again after each reset(). Joins etc are still expensive.<br> * With the <b>MAYBE_FIRST</b> strategy the first cursor <i>might</i> be cached - or rather * <i>will be cached if it is not resettable</i>. So this strategy guarantees a resettable * cursor with least overhead.<br> * The <b>ONLY_LAST</b> strategy will cache only the last cursor in your query, so you can * iterate and reset it efficiently as often as you like.<br> * With the <b>SMART</b> caching strategy, the last cursor and heavily used (=resetted often) cursors * will be cached. Specifically, the inner cursors in joins, intersections and differences will * be cached. In the example above the "foo" cursor (because it's an inner join partner) and the * cursor returned by select(..) (because it's the last one) would be cached. <br> * <b>SMART_NOTLAST</b> is similar, but it doesn't cache the last cursor, so in this example only * the "foo" cursor would be cached.<br><br> * * The SMART strategies may both (but SMART_NOTLAST less then pure SMART) need much memory * because of the overhead of the Lists containing the tuples. However, they should generally * need a lot less memory than ALL while being just as fast. * <br><br> * As a rule of thumb it makes sense to perform "heavy" caching (SMART) on a cursor if it * needs to be resettable and it either needs to be resetted multiple times or it contains * several expensive calculations (joins, differences, intersections, aggregates, subqueries, ..) * and needs to be reset() at least one time.<br><br> * * <ul> * <li>The cursor is an <b>inner</b> join/difference/intersect-partner ("foo" in the foregoing example) it will * be resetted several times => cache it!</li> * <li>If the cursor is used in a subquery (EXISTS/ALL/ANY in WHERE-clause) it needs to be resettable, * but it may not cache anything but the first cursor (because of correlation) => use ONLY_FIRST * if your input to the first-cursor is an Iterator or non-resettable Cursor! (This is default anyway, so * just don't change it if you don't know what you're doing)</li> * <ul> * */ // TODO: SMART weitergedacht koennte ausserdem bei reset von gecachetem cursor j die listen in // cursorn i mit i<j geloescht werden - solange die nicht geclont wurden... public enum CachingStrategy { ALL, NONE, ONLY_FIRST, MAYBE_FIRST, ONLY_LAST, SMART, SMART_NOTLAST } public enum JOIN_TYPE { /** * A constant specifying a theta-join. Only the tuples for which the * specified predicate is <code>true</code> will be returned. */ THETA_JOIN, /** * A constant specifying a left outer-join. The tuples for which the * specified predicate is <code>true</code> as well as all elements of * <code>input0</code> not qualifying concerning the predicate will be * returned. The function <code>newResult</code> is called with an * element of <code>input0</code> and the <code>null</code> value. */ LEFT_OUTER_JOIN, /** * A constant specifying a right outer-join. The tuples for which the * specified predicate is <code>true</code> as well as all elements of * <code>input1</code> not qualifying concerning the predicate will be * returned. The function <code>newResult</code> is called with an * element of <code>input1</code> and the <code>null</code> value. */ RIGHT_OUTER_JOIN, /** * A constant specifying a full outer-join. The tuples for which the * specified predicate is <code>true</code> as well as all tuples * additionally returned by the left and right outer-join will be * returned. */ OUTER_JOIN }; /** * The caching strategy used by this cursor. * * @see CachingStrategy */ CachingStrategy cacheStrat = null; // has to be set via setCachingStrategy(), so doNotCache etc are set properly /** * Constructor that takes an arbitrary Cursor, a Function that maps its Objects to Tuples, * Metadata for <b>the resulting</b> tuples. If you want this Cursor to have a new alias, use * {@link AdvResultSetMetaData#clone(String newAlias)} on your existing metadata.<br> * <i>Note</i>: This is the constructor that is also be used, explicitly or via * {@link AdvTupleCursor#factorFromCursorWithTuples(Cursor, AdvResultSetMetaData, AdvTupleCursor...) * factorFromCursorWithTuples()}, by our operators like join, select, where, ... * @param cur some Cursor * @param mapping Maps Objects from Cur to Tuple * @param metadata Metadata appropriate for the resulting Tuples * @param parents "{@link #parents}" of this cursor (if any) */ public AdvTupleCursor(Cursor<?> cur, Function<Object, Tuple> mapping, AdvResultSetMetaData metadata, AdvTupleCursor... parents){ this(cur, mapping, metadata, null, parents); } /** * Constructor that takes an arbitrary Cursor, a Function that maps its Objects to Tuples, * Metadata for <b>the resulting</b> tuples. If you want this Cursor to have a new alias, use * {@link AdvResultSetMetaData#clone(String newAlias)} on your existing metadata.<br> * <i>Note</i>: This is the constructor that is also be used, explicitly or via * {@link AdvTupleCursor#factorFromCursorWithTuples(Cursor, AdvResultSetMetaData, AdvTupleCursor...) * factorFromCursorWithTuples()}, by our operators like join, select, where, ... * @param cur some Cursor * @param mapping Maps Objects from Cur to Tuple * @param metadata Metadata appropriate for the resulting Tuples * @param operatorImpl an implementation of operators, in case you want to provide your own * (<i>null</i> for default operator - e.g. {@link OperatorImplementation#getOperatorImpl()} or the left parent's * implementation, if any) * @param parents "{@link #parents}" of this cursor (if any) */ public AdvTupleCursor(Cursor<?> cur, Function<Object, Tuple> mapping, AdvResultSetMetaData metadata, OperatorImplementation operatorImpl, AdvTupleCursor... parents) { this.metadata = metadata; this.objToTuple = mapping; this.wrappedCursor = cur; if(operatorImpl != null) this.operatorImpl = operatorImpl; if(parents != null && parents.length>0){ this.parents = parents; for(AdvTupleCursor parent : parents){ // add this cursor to parents children-list parent.addChild(this); // add parents correlated tuples receivers to this cursors list this.corrTuplesReceivers.addAll(parent.getCorrTuplesRec()); } if(operatorImpl == null){ // use the same operator implementation as the left parent if not told otherwise this.setOperatorImpl(parents[0].operatorImpl, false); } // use same caching-strategy as (left) parent this.setCachingStrategy(parents[0].getCachingStrategy(), false); // TODO: hier koennte man ganz smarten kram machen und die strategien beider eltern // betrachten und die bessere (ueber)nehmen oder sowas } else // set default caching strategy this.setCachingStrategy(CachingStrategy.ONLY_FIRST, false); } /** * Creates an AdvTupleCursor from a list of AdvTuples and appropriate metadata.<br> * If you want this Cursor to have a new alias, use * {@link AdvResultSetMetaData#clone(String newAlias)} on your existing metadata.<br> * (mainly useful for cloning) * @param list a List of AdvTuples * @param metadata Metadata suitable for those tuples * @param operImpl the {@link OperatorImplementation} to be used in this cursor (or null for default) */ public AdvTupleCursor(List<Tuple> list, AdvResultSetMetaData metadata, OperatorImplementation operImpl){ this.tuples = list; this.metadata = metadata; this.firstRun = false; this.tupleIt = tuples.iterator(); // this cursor has no parents and does cache (with the given list) this.setCachingStrategy(CachingStrategy.ONLY_FIRST, true); if(operImpl != null) this.operatorImpl = operImpl; } /** * A constructor that an AdvTupleCursor from a * <b>nonempty</b> Cursor of arbitrary type<br> * <b>Don't</b> use this Constructor for Cursors containing Tuples (e.g. from a join)! * We have a factory Method ({@link AdvTupleCursor#factorFromCursorWithAdvTuples(Cursor, AdvResultSetMetaData, String) * factorFromCursorWithAdvTuples()} ) for that purpose that is safer, especially with empty cursors * (like from a join creating an empty cursor because the Predicate is never met).<br> * The Cursor will contain Tuples with one column named "value" that containing the elements of <b>cur</b> * @param cur Cursor to be wrapped * @param alias an Alias */ public AdvTupleCursor(Cursor<?> cur, String alias) { this(cur, alias, null); } /** * A constructor that an AdvTupleCursor from a * <b>nonempty</b> Cursor of arbitrary type<br> * <b>Don't</b> use this Constructor for Cursors containing Tuples (e.g. from a join)! * We have a factory Method ({@link AdvTupleCursor#factorFromCursorWithAdvTuples(Cursor, AdvResultSetMetaData, String) * factorFromCursorWithAdvTuples()} ) for that purpose that is safer, especially with empty cursors * (like from a join creating an empty cursor because the Predicate is never met).<br> * The Cursor will contain Tuples with one column named "value" that containing the elements of <b>cur</b> * @param cur Cursor to be wrapped * @param alias an Alias * @param operImpl the {@link OperatorImplementation} to be used in this cursor (or null for default) */ public AdvTupleCursor(Cursor<?> cur, String alias, OperatorImplementation operImpl) { wrappedCursor = cur; wrappedCursor.open(); if(!cur.hasNext()) throw new RuntimeException("Cursor "+alias+" is empty!"); Object example = cur.peek(); // if cur is *not* some kind of Cursor<Tuple> if(!(example instanceof Tuple)) { ColumnMetaData cmd = AdvResultSetMetaData.createColumnMetaData(example.getClass(), "value", alias); metadata = new AdvResultSetMetaData(alias, cmd); objToTuple = new AbstractFunction<Object, Tuple>() { private static final long serialVersionUID = 1L; @Override public Tuple invoke(Object argument) { return new ArrayTuple(argument); } }; } else { // cur *is* some kind of Cursor<Tuple> throw new RuntimeException("You really want to use factorFromCursorWithAdvTuples() instead of this constructor."); } // this cursor has no parents and should probably cache (unless set otherwise later) this.setCachingStrategy(CachingStrategy.NONE, false); if(operImpl != null) this.operatorImpl = operImpl; } /** * This factory creates an {@link AdvTupleCursor} for {@link Iterable}s (Lists, ...) containing * arbitrary types and does an "automagical" mapping: * <ul> * <li> If the elements are Objects of a <b>primitive type</b> (or pseudo-Primitive like * Integer, String, ...) the Tuples in the resulting Cursor will just contain one element * called "value".</li> * <li> If the elements contain a more <b>complex type</b>, the public(!) <b>getters</b> * (get*() and is*()) will be used to obtain values. The columns will be called according * to the getter: The value retrieved from getFoo() will be in the column "foo".</li> * <li> If the elements are <b>Arrays</b> (of equal length!), value i from the array (arr[i]) * will be value i+1 in the Tuple (because tuples start counting at 1) and will be called * {@code"col<i>"} with i >= 1 (e.g. "col1" or "col42")</li> * </ul> * @param it an Iterable providing the elements * @param alias an Alias for the created {@link AdvTupleCursor} * @return an AdvTupleCursor wrapping it */ @SuppressWarnings("unchecked") public static AdvTupleCursor automagicallyCreateTupleCursor(Iterable<?> it, String alias){ return automagicallyCreateTupleCursor(new IterableCursor(it), alias); } /** * This factory creates an {@link AdvTupleCursor} for Iterators/Cursors containing arbitrary * types and does an "automagical" mapping: * <ul> * <li> If the elements are Objects of a <b>primitive type</b> (or pseudo-Primitive like * Integer, String, ...) the Tuples in the resulting Cursor will just contain one element * called "value".</li> * <li> If the elements contain a more <b>complex type</b>, the public(!) <b>getters</b> * (get*() and is*()) will be used to obtain values. The columns will be called according * to the getter: The value retrieved from getFoo() will be in the column "foo".</li> * <li> If the elements are <b>Arrays</b> (of equal length!), value i from the array (arr[i]) * will be value i+1 in the Tuple (because tuples start counting at 1) and will be called * {@code"col<i>"} with i >= 1 (e.g. "col1" or "col42")</li> * </ul> * @param it an Iterator/Cursor providing the elements * @param alias an Alias for the created {@link AdvTupleCursor} * @return an AdvTupleCursor wrapping it */ public static AdvTupleCursor automagicallyCreateTupleCursor(Iterator<?> it, String alias){ Cursor<?> cur = Cursors.wrap(it); cur.open(); if(!cur.hasNext()) throw new RuntimeException("Cursor "+alias+" is empty!"); Object example = cur.peek(); // if cur is *not* some kind of Cursor<Tuple> if(!example.getClass().isArray()){ MapEntry<List<Method>, AdvResultSetMetaData> tmp = createAndSetMetaDataFromClass(example.getClass(), alias); Function<Object, Tuple> mapping = createDefaultMapping(tmp.getKey()); return new AdvTupleCursor(cur, mapping, tmp.getValue(), (AdvTupleCursor[])null); } else { // is Array AdvResultSetMetaData md = createMetadataForArray(example, alias); Function<Object, Tuple> mapping; try { mapping = createArrayMapping(md.getColumnCount()); } catch (SQLException e) { throw new RuntimeException(e); } return new AdvTupleCursor(cur, mapping, md, (AdvTupleCursor[])null); } } /** * A constructor that automagically creates an AdvTupleCursor from a * <b>nonempty</b> Iterator of arbitrary type.<br> * The first element of the cursor will be inspected (vie peek()) to generate the mapping * from the elements to AdvTuple - this will of course fail if there are no elements to peek. * * @param it an Iterator to be wrapped * @param alias an Alias */ public AdvTupleCursor(Iterator<?> it, String alias) { this(it, alias, null); } /** * A constructor that automagically creates an AdvTupleCursor from a * <b>nonempty</b> Iterator of arbitrary type.<br> * The first element of the cursor will be inspected (vie peek()) to generate the mapping * from the elements to AdvTuple - this will of course fail if there are no elements to peek. * * @param it an Iterator to be wrapped * @param alias an Alias * @param operImpl the {@link OperatorImplementation} to be used in this cursor (or null for default) */ public AdvTupleCursor(Iterator<?> it, String alias, OperatorImplementation operImpl) { this(new IteratorCursor<Object>(it), alias, operImpl); } /** * A constructor that automagically creates an AdvTupleCursor from a * <b>nonempty</b> Iterable (almost any Java Container) of arbitrary type * * @param it an Iterable to be wrapped * @param alias an Alias */ public <T> AdvTupleCursor(String alias, T... a) { this(Arrays.asList(a), alias, null); } /** * A constructor that automagically creates an AdvTupleCursor from a * <b>nonempty</b> Iterable (almost any Java Container) of arbitrary type * * @param it an Iterable to be wrapped * @param alias an Alias */ public AdvTupleCursor(Iterable<?> it, String alias) { this(it, alias, null); } /** * A constructor that automagically creates an AdvTupleCursor from a * <b>nonempty</b> Iterable (almost any Java Container) of arbitrary type * * @param it an Iterable to be wrapped * @param alias an Alias * @param operImpl the {@link OperatorImplementation} to be used in this cursor (or null for default) */ public AdvTupleCursor(Iterable<?> it, String alias, OperatorImplementation operImpl) { this(new IterableCursor<Object>(it), alias, operImpl); } /** * Wraps a ResultSet (most probably from JDBC, i.e. from a query to a database) into an * AdvTupleCursor. <br> * By default it does not cache its tuples - its {@link CachingStrategy} is set to <i>NONE</i> * in contrast to most other constructors that set an ONLY_FIRST caching strategy.<br> * That's because ResultSets can contain a lot of elements and creating Tuples from them is * quite easy. You may change this setting via * {@link AdvTupleCursor#setCachingStrategy(CachingStrategy, boolean) setCachingStrategy()}, of course. * * @param resultset the ResultSet to be wrapped * @param alias the alias of the AdvTupleCursor created */ public AdvTupleCursor(ResultSet resultset, String alias) { this(resultset, alias, null); } /** * Wraps a ResultSet (most probably from JDBC, i.e. from a query to a database) into an * AdvTupleCursor. <br> * By default it does not cache its tuples - its {@link CachingStrategy} is set to <i>NONE</i> * in contrast to most other constructors that set an ONLY_FIRST caching strategy.<br> * That's because ResultSets can contain a lot of elements and creating Tuples from them is * quite easy. You may change this setting via * {@link AdvTupleCursor#setCachingStrategy(CachingStrategy, boolean) setCachingStrategy()}, of course. * * @param resultset the ResultSet to be wrapped * @param alias the alias of the AdvTupleCursor created * @param operImpl the {@link OperatorImplementation} to be used in this cursor (or null for default) */ public AdvTupleCursor(ResultSet resultset, String alias, OperatorImplementation operImpl) { try { this.metadata = new AdvResultSetMetaData(resultset.getMetaData(), alias); } catch (SQLException e) { throw new RuntimeException(e); } this.wrappedCursor = new ResultSetMetaDataCursor(resultset); this.objToTuple = new AbstractFunction<Object, Tuple>() { private static final long serialVersionUID = 1L; @Override public Tuple invoke(Object argument) { return (Tuple)argument; } }; // don't cache (unless requested otherwise later), just get the tuples from the DB again // but set to MAYBE_FIRST to indicate that this cursor is resettable this.setCachingStrategy(CachingStrategy.MAYBE_FIRST, false); if(operImpl != null) this.operatorImpl = operImpl; } /** * Creates a Tuple from an Object using the mapping function {@link AdvTupleCursor#objToTuple objToTuple} * @param o an Object * @return a Tuple created from that Object */ protected Tuple createTuple(Object o) { return objToTuple.invoke(o); } /** * Factory Function that wraps a Cursor containing AdvTuples (e.g. from a join, ...) to an AdvTupleCursor.<br> * If you want this to have a specific new alias, make sure your metadata already has got it * (via {@link AdvResultSetMetaData#clone(String newAlias)} or * {@link AdvResultSetMetaData#concat(AdvResultSetMetaData first, AdvResultSetMetaData second, String newAlias)}.<br> * The <b>parents</b> is/are the AdvTupleCursor(s) providing the tuples processed by this cursor.<br> * <b>Example:</b> <code>cur1.join(cur2, col("x").EQ(col("y"), "foo").where(col("z").LEQ(val(4711)))</code> * cur1 and cur2 are the parents of the cursor returned by <i>join</i>, which itself is the parent of the * cursor returned by <i>where</i>. * @param cur the Cursor to be wrapped * @param metadata appropriate Metadata for the AdvTuples * @param parents "parents" of this cursor (if any) * @return an AdvTupleCursor containing the elements of cur */ @SuppressWarnings("serial") public static AdvTupleCursor factorFromCursorWithTuples(Cursor<Tuple> cur, AdvResultSetMetaData metadata, AdvTupleCursor... parents){ Function<Object, Tuple> id = new AbstractFunction<Object, Tuple>() { @Override // no transformation needed, ID-function public Tuple invoke(Object argument) { return (Tuple)argument; } }; return new AdvTupleCursor(cur, id, metadata, parents); } /** * Sorts Cursor this by the given comperator. (useful for sorting and grouping)<br> * <b>The Cursor will be <u>reset</u>!</b> (Wouldn't make much sense not to reset anyway..) * @param comp the Comparator */ protected void internal_sort(Comparator<Tuple> comp){ inUse=true; // so reset does something reset(); // reset to make sure all elements are in List // interne liste sortieren - waere etwas schneller mit ner arraylist, da die elemente // in nen array kopiert, dann sortiert und dann zurueck in die liste kopiert werden... // und das kopieren ist von array zu array schneller. (auf die idee, bei ner arraylist // direkt aufm internen array zu sortieren ist sun - aehhh oracle. - aber noch nicht // gekommen...) Collections.sort(tuples, comp); tupleIt = tuples.iterator(); } @Override protected boolean hasNextObject() { // set inUse to true - the first element has been determined, changing the caching strategy // now would be a bit painful.. (probably not impossible, though.. feel free to improve this :)) inUse = true; // tupleIt is not used with subqueries, because they need to be re-evaluated every time // in case they are correlated if(firstRun || doNotCache) { return wrappedCursor.hasNext(); } else return tupleIt.hasNext(); } @Override protected Tuple nextObject() { Tuple tmp; // do not cache results in subquery: it might be correlated, so it has to be re-evaluated // on every new run. however this could be optimized by finding out it the subquery is really // correlated. also only the cursor first where-clause and later need to be re-evaluated if(doNotCache) { tmp = createTuple(wrappedCursor.next()); } else if(firstRun) { // on the first run copy tuple to tuples-List tmp = createTuple(wrappedCursor.next()); tuples.add(tmp); } else tmp = tupleIt.next(); return tmp; } @Override public void reset() throws UnsupportedOperationException { if(inUse == false) return; if(doNotCache){ wrappedCursor.reset(); } else { if(firstRun) { // if the cursor is reset on first run.. // copy all remaining tuples (if any) to tuples-List while(wrappedCursor.hasNext()){ tuples.add(createTuple(wrappedCursor.next())); } // FIXME: sollte man den schliessen? // wrappedCursor.close(); firstRun = false; } // fetch new iterator, so it starts again from the beginning of the list tupleIt = tuples.iterator(); } super.reset(); } @Override public boolean supportsReset() { if(doNotCache && !wrappedCursor.supportsReset()) return false; else return true; } /** * Returns the AdvResultSetMetaData of this Cursor, which is compatible to * JDBC's ResultSetMetaData * * @return the (Adv)ResultSetMetaData of this Cursor */ public AdvResultSetMetaData getResultSetMetaData() { return metadata; } /** * Get CompositeMetaData<Object, Object> containing ResultSetMetaData for this Cursor. * Especially needed by the Cursors of the relational package. * * @return CompositeMetaData<Object, Object> containing ResultSetMetaData for this Cursor */ @Override public CompositeMetaData<Object, Object> getMetaData() { // solange den quatsch keiner braucht, muessen wir ihn auch nicht initialisieren. if(compositeMetaData == null) { String type = ResultSetMetaDatas.RESULTSET_METADATA_TYPE; compositeMetaData = new CompositeMetaData<Object, Object>(); compositeMetaData.add(type, metadata); } return compositeMetaData; } /** * Returns this Cursor (as it is itself an Iterator containing Tuples).<br> * If it has already been used it will be {@link #reset()} before being returned to make sure * the returned Iterator iterates all Tuples of this Cursor.<br> * It doesn't make to much sense to use this Method directly, but it enables for-each loops * for this class.<br> * <b>Example:</b> * <pre><code> AdvTupleCursors cur = ... ; // some AdvTupleCursor * for(Tuple tup : cur){ * System.out.println(tup); * }</code></pre> * <i><b>Note:</b></i> It is <b>not possible to nest two</b> of this iterators from the <b>same * cursor</b> (or if one cursor is a part of the other cursor). This means, the following will * produce strange behavior: * <pre><code> AdvTupleCursors cur1 = ... ; // some AdvTupleCursor * AdvTupleCursor cur2 = cur1.where(...); // cur2 contains cur1! * for(Tuple t1 : cur1){ * for(Tuple t2 : cur2) { * System.out.println(t1+" "+t2); * } * }</code></pre> * If you want something like this you have to {@link #clone()}! * @return an Iterator containing Tuples, i.e. <b>this</b> */ @Override public Iterator<Tuple> iterator() { if(inUse) reset(); return this; } /** * Returns a clone of this Cursor with given Alias. Will fail if this cursor does not * {@link #supportsReset() support reset()} And <b>it will reset your cursor!</b>.<br> * This is especially useful for self-joins: joining a cursor with itself (or a query that * shares some cursors with this one like <code><i>AdvTupleCursors atc1 = cur1.where(...); * AdvTupleCursor atc2 = cur1.select(...);</i></code>) is impossible (or rather: it is possible, but * you'll get strange results).<br> * But if you clone the cursors before joining it'll work.<br> * <b>Example:</b> * <code><pre> AdvTupleCursor cur1 = ...; // a AdvTupleCursor * AdvTupleCursor atc1 = cur1.where(...); * AdvTupleCursor atc2 = cur1.clone("cur1clone").select(...); * AdvTupleCursor join = atc1.join(atc2, col("cur1.a").LEQ(col("cur1clone.b")));</pre></code> * @param alias an Alias * @return a clone of this Cursor */ public AdvTupleCursor clone(String alias) { if(!this.supportsReset()){ throw new RuntimeException("Can't clone a Cursor that doesn't support reset()!"); // it doesn't support reset() if the wrapped cursor isn't resettable // and this cursor doesn't cache } List<Tuple> newList=null; // list of tuples for the new cloned cursor // reset() this cursor to make sure a cached cursor has it's tuples-list filled and // other cursors are before their first element inUse=true; // so reset() does something reset(); if(doNotCache){ // this cursor doesn't cache so copy each element into list newList = new LinkedList<Tuple>(); while(this.hasNext()){ newList.add(this.next()); } reset(); // reset again so the cursor again is before the first element } else { // this cursor does cache, so just copy the list newList = new ArrayList<Tuple>(tuples); } AdvResultSetMetaData newMD; if(alias == null) newMD = this.metadata; else newMD = this.metadata.clone(alias); return new AdvTupleCursor(newList, newMD, operatorImpl){ @Override public void setCachingStrategy(CachingStrategy strat, boolean recursive) { super.setCachingStrategy(strat, recursive); // make sure caching is not disabled - the cloned cursor relies on its tuples list doNotCache=false; } }; } /** * Add a "correlated Tuple Receiver" to the Cursors corrTuplesReceivers list. * That's a list of Objects implementing {@link CorrTuplesReceiver} (atm only Predicates from WHERE).<br> * In this list all CorrTupleReceivers of this Query (this Cursor and it's parents) are collected * so EXISTS etc can set the correlated Tuples in the Predicates of their (possibly correlated) * subqueries. * * @param ctr A "Corrolated Tuples Receiver", for example an {@link AdvPredicate} * * @see AdvPredicate#setCorrelatedTuples(List) */ // needs to be public so classes from the columns package can use it public void addCorrTuplesRec(CorrTuplesReceiver ctr){ corrTuplesReceivers.add(ctr); } /** * Returns a list of Objects implementing {@link CorrTuplesReceiver} (atm only Predicates from WHERE).<br> * In this list all CorrTupleReceivers of this Query (this Cursor and it's parents) are collected * so EXISTS etc can set the correlated Tuples in the Predicates of their (possibly correlated) * subqueries. * * @return a List of "Corrolated Tuples Receivers", for example {@link AdvPredicate}s * * @see AdvPredicate#setCorrelatedTuples(List) */ public List<CorrTuplesReceiver> getCorrTuplesRec(){ return corrTuplesReceivers; } /** * Sets the {@link CachingStrategy} for this cursor and maybe its parental cursors * (if recursive is <i>true</i>). The CachingStrategy determines whether the cursor saves its tuples * in an intern list in the first run, so it doesn't have to compute them again after reset().<br> * Settings recursive to <i>false</i> doesn't make too much sense for the SMART strategies, however: if * this cursor is a join/difference/intersection, the right parent's caching will be enabled. <br> * See {@link CachingStrategy} for an extensive explanation of the different strategies and * what they're good for. * * @param strat the {@link CachingStrategy} to be set * @param recursive if <i>true</i>, this strategy will also be set to the {@link AdvTupleCursor#parents parental} cursors * * @see CachingStrategy */ public void setCachingStrategy(CachingStrategy strat, boolean recursive){ // TODO: maybe allow setting a new caching strategy directly after reset()? // - that would quite difficult. if(inUse){ throw new RuntimeException("It's currently impossible to change the caching strategy of" +" a cursor that has already delivered an element"); } cacheStrat = strat; switch(strat){ case NONE : doNotCache = true; break; case ALL : doNotCache = false; break; case ONLY_FIRST : if(parents == null) doNotCache = false; else doNotCache = true; break; case MAYBE_FIRST: if(parents == null && !wrappedCursor.supportsReset()) doNotCache = false; else doNotCache = true; break; case ONLY_LAST : doNotCache = false; // pass on NONE to parents strat = CachingStrategy.NONE; break; case SMART : if(children == null) // cache the last cursor doNotCache = false; else // this might be set to false by a child afterwards doNotCache = true; // (if this is an inner join/difference/intersect partner) case SMART_NOTLAST : doNotCache = true; // don't cache last cursor. otherwise like SMART. } if(recursive && parents != null){ for(AdvTupleCursor parent : parents){ parent.setCachingStrategy(strat, recursive); } } if(strat.equals(CachingStrategy.SMART) || strat.equals(CachingStrategy.SMART_NOTLAST)){ if(isExpensive && parents != null && parents.length==2) { // join, difference, intersect // enable caching for inner join/.. partner because it will be reset() really often parents[1].doNotCache = false; } } } /** * Returns the CachingStrategy of this cursor. See {@link CachingStrategy} for an extensive * explanation of the different strategies and what they're good for. * * @return this cursor's caching strategy */ public CachingStrategy getCachingStrategy(){ return cacheStrat; } /** * Sets a custom implementation of operators (join, where, ...) for this query, e.g. for this * cursor, its {@link #parents}, their parents, ...<br> * If you don't want to change the implementation for the whole query but only for this cursor * use {@link #setOperatorImpl(OperatorImplementation, boolean) setOperatorImpl(impl, <b>false</b>)} * @param impl the alternative implementation of operators */ public void setOperatorImpl(OperatorImplementation impl){ setOperatorImpl(impl, true); } /** * Sets a custom implementation of operators (join, where, ...) for this cursor.<br> * If recursive is <i>true</i> it's set for the whole query, if not just for this cursor. * @param impl the alternative implementation of operators * @param recursive if <i>true</i> it will be set in the whole query, else just for this cursor */ public void setOperatorImpl(OperatorImplementation impl, boolean recursive){ this.operatorImpl = impl; if(recursive){ for(AdvTupleCursor parent : parents) parent.setOperatorImpl(impl, recursive); } } /** * Register a "child" at this cursor * <b>Example:</b> <code>cur1.join(cur2, col("x").EQ(col("y"), "foo").where(col("z").LEQ(val(4711)))</code> * The cursor returned by <i>join</i> is a child of both cur1 and cur2 and the cursor returned * by <i>where</i> is a child of the cursor returned by <i>join</i>. * @param child an AdvTupleCursor that is to be registered as a child of this cursor */ void addChild(AdvTupleCursor child){ if(children == null){ // most cursors will have only one child children = new ArrayList<AdvTupleCursor>(1); } children.add(child); } // ########################################################################## // ab hier: Voodoo fuer "automagisches" Mapping beliebiger Klassen auf Tupel # // ########################################################################## protected LinkedList<Method> getters; /** * Automagically sets creates and sets MetaData and a list of getters in this Cursor for Objects * of the given Class.<br> * The mapping itself should be created with {@link AdvTupleCursor#createDefaultMapping() createDefaultMapping()} * and will use the list of getters created by this Method.<br> * If cl is a (pseudo)-<b>primitive</b> type (Float, Integer, ... , Boolean, Byte, Character, String) * the tuples will have only one column named "<b>value</b>".<br> * If the class it is not primitive, it will be scanned for getters and for each getter there * will be a column with the getters suffix, the first character will be lowercase * (i.e. the column for "getFoo()" will be called "foo"). * * @param cl the type of the Objects this Cursor is to wrap * @param alias this Cursor's alias * * @see AdvTupleCursor#createDefaultMapping() */ protected static MapEntry<List<Method>, AdvResultSetMetaData> createAndSetMetaDataFromClass(Class<?> cl, String alias){ AdvResultSetMetaData metadata=null; LinkedList<Method> getters = null; // special cases: primitive Types boolean prim=false; if(cl.isPrimitive()) // it's a "real" primitive type prim=true; // it's derived from Number (covers Float, Integer, ... and similar) else if(java.lang.Number.class.isAssignableFrom(cl)) prim = true; else if(cl.getName().startsWith("java.lang.")) { // check for Boolean, Byte, Character and String if(cl.getSimpleName().equals("Boolean") || cl.getSimpleName().equals("Byte") || cl.getSimpleName().equals("Character") || cl.getSimpleName().equals("String")) prim = true; } // System.out.println("prim="+prim); if(prim){ // it's (more or less) primitive getters = null; metadata = new AdvResultSetMetaData(alias, AdvResultSetMetaData.createColumnMetaData(cl, "value", alias) ); // primitive types have the special column-name "value" // metadata.addMappingAliasNameIndex("value", alias, 1); not needed, is done by constructor } else { // complex type -> traverse the getters // System.out.println("Class: "+cl.getName()); Method[] methods = cl.getMethods(); LinkedList<ColumnMetaData> cmds = new LinkedList<ColumnMetaData>(); // ColumnMetaDatas for each column.. getters = new LinkedList<Method>(); for(Method method : methods){ if(method.getName().equals("getClass")) // we don't want the class.. continue; if(method.getReturnType().equals(void.class)) continue; // we're not interested in methods returning void.. // System.out.println("Method: "+method.getName()); if(method.getName().startsWith("get") && method.getGenericParameterTypes().length == 0) { getters.add(method); // put into getters-list String name = method.getName().substring(3); // remove prefix "get" name = name.substring(0, 1).toLowerCase()+name.substring(1); // make first letter lowercase // create appropriate ColumnMetaData and add it to list.. Class<?> type = getObjectType(method.getReturnType()); cmds.add( AdvResultSetMetaData.createColumnMetaData(type, name, alias) ); } else if(method.getName().startsWith("is") && method.getGenericParameterTypes().length == 0) { getters.add(method); // put into getters-list String name = method.getName().substring(2); // remove prefix "is" name = name.substring(0, 1).toLowerCase()+name.substring(1); // make first letter lowercase // create appropriate ColumnMetaData and add it to list.. Class<?> type = getObjectType(method.getReturnType()); cmds.add( AdvResultSetMetaData.createColumnMetaData(type, name, alias) ); } } if(cmds.size() == 0) throw new RuntimeException("Couldn't get attributes of Class "+cl.getSimpleName() +", because it has no getters and is not (pseudo-)primitive"); ColumnMetaData[] cmdArr = cmds.toArray(new ColumnMetaData[cmds.size()]); metadata = new AdvResultSetMetaData(alias, cmdArr); } // !prim return new MapEntry<List<Method>, AdvResultSetMetaData>(getters, metadata); } /** * Returns the Object-type for a primitive type (or just the given type if it isn't primitive) * @param type a type/class, that might be primitive * @return the according non-primitive type */ @SuppressWarnings("unchecked") protected static Class getObjectType(Class<?> type){ if(!type.isPrimitive()) return type; if(type.equals(byte.class)) return Byte.class; else if(type.equals(short.class)) return Short.class; else if(type.equals(int.class)) return Integer.class; else if(type.equals(long.class)) return Long.class; else if(type.equals(float.class)) return Float.class; else if(type.equals(double.class)) return Double.class; else if(type.equals(boolean.class)) return Boolean.class; else if(type.equals(char.class)) return Character.class; throw new RuntimeException("Unknown primitive type "+type.getName()); } /** * Returns a function that creates a tuple from an Object. Needs MetaData and the getters-list * that may be generated with {@link AdvTupleCursor#createAndSetMetaDataFromClass(Class, String)} */ protected static Function<Object, Tuple> createDefaultMapping(final List<Method> getters) { if(getters != null) { // => complex type return new AbstractFunction<Object, Tuple>() { private static final long serialVersionUID = -5940693227875126138L; @Override public Tuple invoke(Object obj) { Object[] elems = new Object[getters.size()]; int i=0; for(Method m : getters){ // traverse getters and put result into an object array try { elems[i++] = m.invoke(obj, (Object[])null); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("Can't use getter on Object", e); } } // create an arraytuple with that object-array and return it return new ArrayTuple(elems); } }; } else { // primitive type return new AbstractFunction<Object, Tuple>() { private static final long serialVersionUID = 6138923494410694623L; @Override public Tuple invoke(Object obj){ // the tuple contains only one element return new ArrayTuple(obj); } }; } } protected static AdvResultSetMetaData createMetadataForArray(Object arr, String alias){ if(!arr.getClass().isArray()) throw new RuntimeException("This is not an Array!"); int length = Array.getLength(arr); ColumnMetaData[] cmds = new ColumnMetaData[length]; for(int i=0;i<length;i++){ Object tmp = Array.get(arr, i); cmds[i] = AdvResultSetMetaData.createColumnMetaData(tmp.getClass(), "col"+(i+1), alias); } return null; } protected static Function<Object, Tuple> createArrayMapping(final int length){ return new AbstractFunction<Object, Tuple>() { private static final long serialVersionUID = 8401428384322811855L; @Override public Tuple invoke(Object arr){ Object[] ret = new Object[length]; try{ for(int i=0;i<length;i++) ret[i] = Array.get(arr, i); } catch (IndexOutOfBoundsException e) { throw new RuntimeException("Seems like your arrays" +" are not of equal length: "+e.getMessage(), e); } return new ArrayTuple(ret); } }; } // ###################################################################### // ab hier: Methoden fuer LINQ-artige Funktionalitaet (join, select, ...) # // ###################################################################### /** * Returns a Tuple that is a concatenation of the given Tuples. * Useful for join(). * * @param t1 first Tuple (if null, all columns will be null for outer join) * @param t2 second Tuple (if null, all columns will be null for outer join) * @param size1 number of colums in the first tuple * @param size2 number of colums in the second tuple * @return t1++t2 */ public static Tuple concatTuples(Tuple t1, Tuple t2, int size1, int size2){ Object elems[] = new Object[size1+size2]; if(t1 != null){ for(int i=1;i<=size1;i++) { elems[i-1] = t1.getObject(i); } } else { // t1 == null -> null colums from outer join for(int i=1;i<=size1;i++) { elems[i-1] = null; } } if(t2 != null){ for(int i=1;i<=size2;i++) { elems[size1+i-1] = t2.getObject(i); } } else { // t2 == null -> null columns from outer join for(int i=1;i<=size2;i++) { elems[size1+i-1] = null; } } return new ArrayTuple(elems); } /** * Joins this cursor and atc with a standard inner theta-join with given predicate. * @param other the cursor this cursor is to be joined with * @param pred the join-predicate * @return the joined {@link AdvTupleCursor} */ // default theta-join public AdvTupleCursor join(AdvTupleCursor other, AdvPredicate pred){ return join(null, other, pred); } /** * Joins this cursor and another cursor with a standard inner theta-join with given predicate. * @param alias the new alias for the resulting cursor * @param other the cursor this cursor is to be joined with * @param pred the join-predicate * @return the joined {@link AdvTupleCursor} */ public AdvTupleCursor join(String alias, AdvTupleCursor other, AdvPredicate pred){ AdvTupleCursor ret = operatorImpl.join(this, other, alias, pred); ret.isExpensive = true; // set isExpensive, so SMART caching caches atc2 return ret; } /** * Joins this cursor with the other cursor with the given Predicate. The join type * (theta, outer, left/right outer join) may be specified. * @param other the cursor this cursor is to be joined with * @param pred the join-predicate * @param type the {@link JOIN_TYPE} of this cursor * @return joined {@link AdvTupleCursor} */ public AdvTupleCursor join(AdvTupleCursor other, AdvPredicate pred, JOIN_TYPE type){ return join(null, other, pred, type); } /** * Joins this cursor with the other cursor with the given Predicate. The join type * (theta, outer, left/right outer join) may be specified. * @param alias the new alias for the resulting cursor * @param other the cursor this cursor is to be joined with * @param pred the join-predicate * @param type the {@link JOIN_TYPE} of this cursor * @return joined {@link AdvTupleCursor} */ public AdvTupleCursor join(String alias, AdvTupleCursor atc, AdvPredicate pred, JOIN_TYPE type){ AdvTupleCursor ret = operatorImpl.join(this, atc, alias, pred, type); ret.isExpensive = true; // set isExpensive, so SMART caching caches atc2 return ret; } /** * Like the SQL SELECT clause. You can select existing Columns from this cursor * (with {@link ColumnUtils#col(String) col()}) or create new ones by combining existing * Columns (for example with {@link Column#ADD(Column, String)}) or getting values via * reflection from existing Columns (for example with {@link ColumnUtils#colOBJCALL()} * or even insert new static values with {@link ColumnUtils#val(Object, String)}. * @param columns {@link Column}s you want to select/create * @return an {@link AdvTupleCursor} containing the selected/created Columns */ public AdvTupleCursor select(Column... columns ) { return select(null, columns); } /** * Like the SQL SELECT clause. You can select existing Columns from this cursor * (with {@link ColumnUtils#col(String) col()}) or create new ones by combining existing * Columns (for example with {@link Column#ADD(Column, String)}) or getting values via * reflection from existing Columns (for example with {@link ColumnUtils#colOBJCALL()} * or even insert new static values with {@link ColumnUtils#val(Object, String)}. * @param columns {@link Column}s you want to select/create * @return an {@link AdvTupleCursor} containing the selected/created Columns */ public AdvTupleCursor selectIndex(Column... columns ) { return operatorImpl.selectIndex(this, null, columns); } /** * Like the SQL SELECT clause. You can select existing Columns from this cursor * (with {@link ColumnUtils#col(String) col()}) or create new ones by combining existing * Columns (for example with {@link Column#ADD(Column, String)}) or getting values via * reflection from existing Columns (for example with {@link ColumnUtils#colOBJCALL()} * or even insert new static values with {@link ColumnUtils#val(Object, String)}. * * @param alias the new alias for the resulting Cursor * @param columns {@link Column}s you want to select/create * @return an {@link AdvTupleCursor} containing the selected/created Columns */ public AdvTupleCursor select(String alias, Column ...columns ) { return operatorImpl.select(this, alias, columns); } /** * Filters the tuples by the given predicate. If the predicate evaluates to true for a tuple * it's contained in the resulting cursor, if it does not it isn't contained. * @param predicate the predicate of the filter * @return the filtered cursor */ public AdvTupleCursor where(AdvPredicate predicate) { return operatorImpl.where(this, predicate); } /** * Unions this cursor with another cursor. The cursors need to have the same schema, i.e. the * column count needs to be identical and the names and types of each column need to be * identical. * @param other the cursor to be unioned with * @return a cursor containing the tuples of both this cursor and the other cursor */ public AdvTupleCursor union(AdvTupleCursor other) { return union(null, other); } /** * Unions this cursor with another cursor. The cursors need to have the same schema, i.e. the * column count needs to be identical and the names and types of each column need to be * identical. * @param alias the alias for the resulting cursor * @param other the cursor to be unioned with * @return a cursor containing the tuples of both this cursor and the other cursor */ public AdvTupleCursor union(String alias, AdvTupleCursor atc) { return operatorImpl.union(this, atc, alias); } /** * Groups the Cursor by the given Columns and performs the given aggregations. The resulting * cursor's tuples will contain the Columns from group and the new columns created by the * aggregations. <u>The Colums to be grouped need to map to an index within the tuples, i.e. * you may <b>not use val() or reflection Columns</b> here.</u> If you want to group by values * obtained by reflection you have to insert them into the tuple with * {@link #select(Column...) select(cols)} first!<br> * <b>Note:</b> To conveniently specify the Columns to be grouped by you may use * {@link ColumnUtils#PROJ(Column...)} so you don't have to create an Array yourself. * @param group the Columns to be grouped by (use {@link ColumnUtils#PROJ(Column...)}) * @param aggregations aggregations (like count, avg, ...) to be performed on the cursor * @return a cursor containing the Columns from group and the new columns created by the * aggregations. * * @see ColumnUtils#PROJ(Column...) */ public AdvTupleCursor groupBy(Column[] group, AggregateColumn... aggregations) { return groupBy(null, group, aggregations); } /** * Groups the Cursor by the given Columns and performs the given aggregations. The resulting * cursor's tuples will contain the Columns from group and the new columns created by the * aggregations. <u>The Colums to be grouped need to map to an index within the tuples, i.e. * you may <b>not use val() or reflection Columns</b> here.</u> If you want to group by values * obtained by reflection you have to insert them into the tuple with * {@link #select(Column...) select(cols)} first!<br> * <b>Note:</b> To conveniently specify the Columns to be grouped by you may use * {@link ColumnUtils#PROJ(Column...)} so you don't have to create an Array yourself. * @param alias the new alias for the resulting cursor * @param group the Columns to be grouped by (use {@link ColumnUtils#PROJ(Column...)}) * @param aggregations aggregations (like count, avg, ...) to be performed on the cursor * @return a cursor containing the Columns from group and the new columns created by the * aggregations. * * @see ColumnUtils#PROJ(Column...) */ public AdvTupleCursor groupBy(String alias, Column[] group, AggregateColumn... aggregations) { return operatorImpl.groupBy(this, alias, group, aggregations); } /** * Eliminates duplicate Tuples * @return a cursor containing the same tuples as this cursor, but each tuple only once */ public AdvTupleCursor distinct() { return operatorImpl.distinct(this); } /** * Removes tuples from this cursor that are contained in the other cursor.<br> * The cursors need to have the same schema, i.e. the column count needs to be identical and * the names and types of each column need to be identical.<br> * If all is true, all tuples from this cursor that are contained in the other one are removed, * else only as many tuples of each set of duplicate tuples are removed as are returned in the * other cursor.<br> * <b>Example:</b> if this cursor contains 3 tuples ("a", "b", "c") and the other cursor * contains one tuple ("a", "b", "c"), the resulting cursor will contain 2 tuples * ("a", "b", "c") if all is false or no tuple ("a", "b", "c") if all is true. * @param other the cursor that is subtracted from this one * @param all see above * @return a cursor containing only elements not contained in the other cursor (if all is true) * or containing less elements that are also contained in the other cursor (if all is false) */ public AdvTupleCursor difference(AdvTupleCursor other, boolean all) { AdvTupleCursor ret = operatorImpl.difference(this, other, all); ret.isExpensive=true; // set isExpensive, so SMART caching caches atc2 return ret; } /** * Removes all tuples from this cursor that are contained in the other cursor.<br> * The cursors need to have the same schema, i.e. the column count needs to be identical and * the names and types of each column need to be identical.<br> * @param other the cursor that is subtracted from this one * @return a cursor containing elements that are contained in this cursor, but not in the * other cursor */ public AdvTupleCursor difference(AdvTupleCursor other) { AdvTupleCursor ret = operatorImpl.difference(this,other); ret.isExpensive=true; // set isExpensive, so SMART caching caches atc2 return ret; } /** * Calculates an intersection of this cursor and the other cursor, i.e. the resulting cursor * will only contain elements contained in this cursor and the other cursor.<br> * The cursors need to have the same schema, i.e. the column count needs to be identical and * the names and types of each column need to be identical.<br> * @param other the cursor this one is intersected with * @return a cursor containing only elements contained in both this and the other cursor */ public AdvTupleCursor intersect(AdvTupleCursor other){ AdvTupleCursor ret = operatorImpl.intersect(this,other); ret.isExpensive=true; // set isExpensive, so SMART caching caches atc2 return ret; } /** * Sorts the Tuples of this cursor by the given {@link Column}s in ascending order.<br> * It is sorted by the order of the Columns: It's sorted primary by the first Column, * secundarily by the second Column and so on. * @param cols Columns to sort by * @return a sorted Cursor * * @see #orderBy(boolean, Column...) orderBy(asc, cols) if you want to order in descending order */ // default ascending - like in SQL public AdvTupleCursor orderBy(Column... cols){ return orderBy(true, cols); } /** * Sorts the Tuples of this cursor by the given {@link Column}s.<br> * It is sorted by the order of the Columns: It's sorted primary by the first Column, * secundarily by the second Column and so on. * @param asc if false the cursor will be sorted in descending order, else ascending * @param cols Columns to sort by * @return a sorted Cursor */ public AdvTupleCursor orderBy(boolean asc, Column... cols){ return operatorImpl.orderBy(this, asc, cols); } /** * Returns the first count Tuples of the Cursor. Probably only useful with {@link #orderBy}() * @param count how many tuples to be returned * @return an {@link AdvTupleCursor} returning the <i>count</i> first elements of this cursor */ public AdvTupleCursor top(int count){ return operatorImpl.top(this, count); } /** * Returns an {@link Iterable} for one column of this Cursor. This may be used in a for-each * loop.<br> * <b>Example:</b> * <code><pre> List<Foo> l1; // a list of Foos * // a query on that list, for example using reflection, maybe joining * // some other cursor, doesn't matter... * AdvTupleCursor cur = new AdvTupleCursor(l1, "l1") * .where( colOBJCALL(col("l1.value"), "getA").LEQ(val(42)) ) * .join(...); * // now I want the elements from l1 that are left after that query * for( Object o : cur.getIterableForColumn(col("l1.value")) ){ * Foo f = (Foo)o; * f.someMethod(); * }</pre></code> * <i><b>Note:</b></i> It is <b>not possible to <i>nest</i> two</b> of this iterators from the <b>same * cursor</b> (or if one cursor is a part of the other cursor). This means, the following will * produce strange behavior: * <pre><code> AdvTupleCursors cur1 = ... ; // some AdvTupleCursor * AdvTupleCursor cur2 = cur1.where(...); // cur2 contains cur1! * for( Tuple t1 : cur1.getIterableForColumn(col("a")) ){ * for( Tuple t2 : cur2.getIterableForColumn(col("b")) ) { * System.out.println(t1+" "+t2); * } * }</code></pre> * If you want something like this you have to {@link #clone()}! */ public Iterable<Object> getIterableForColumn(Column col){ return operatorImpl.getIterableForColumn(this, col); } public AdvTupleCursor expand(Column col) { return NewLINQFunctions.expand(this,col); } public AdvTupleCursor newSelect(Column... columns ){ Projection prj = new Projection(this.getResultSetMetaData(), "newSelect", columns); return new AdvTupleCursor(this, prj, prj.getMetadata(), this); } }