/* 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.tuples; import java.sql.ResultSetMetaData; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import xxl.core.comparators.ComparableComparator; import xxl.core.comparators.Comparators; import xxl.core.comparators.FeatureComparator; import xxl.core.comparators.InverseComparator; import xxl.core.comparators.LexicographicalComparator; import xxl.core.cursors.Cursor; import xxl.core.cursors.mappers.Mapper; import xxl.core.functions.AbstractFunction; import xxl.core.functions.AbstractMetaDataFunction; import xxl.core.functions.Function; import xxl.core.functions.MetaDataFunction; import xxl.core.relational.metaData.ColumnMetaData; import xxl.core.relational.metaData.ColumnMetaDataResultSetMetaData; /** * This class contains various useful <tt>static</tt> methods for Tuples. * * <p<This includes the methods for comparing tuples, accessing columns, * unpacking into an object array and calculating the size.</p> * * <p>This class cannot be instantiated.</p> */ public class Tuples { /** * The default constructor has private access in order to ensure * non-instantiability. */ private Tuples() { // private access in order to ensure non-instantiability } /** * Returns a metadata function with the given metadata that takes lists of * objects and returns tuples. * * @param metadata the relational metadata of the constructed tuples. * @return a metadata function with the given metadata that takes lists of * objects and returns tuples. */ public MetaDataFunction<Object, Tuple, ResultSetMetaData> getTupleFactory(final ColumnMetaData... metadata) { return new AbstractMetaDataFunction<Object, Tuple, ResultSetMetaData>() { protected ResultSetMetaData resultSetMetaData = new ColumnMetaDataResultSetMetaData(metadata); @Override public Tuple invoke(List<? extends Object> args) { if (args.size() != metadata.length) throw new IllegalArgumentException("incorrect number of objects"); return new ArrayTuple(args.toArray()); } @Override public ResultSetMetaData getMetaData() { return resultSetMetaData; } }; } /** * Returns a comparator that compares two tuples of different type * according to a subset of column indices. * * <p>The comparison uses a lexicographical ordering of the column objects. * The objects themselves have to be comparable, otherwise a * <code>ClassCastException</code> will be thrown. The comparison can be * processed in ascending or descending order.</p> * * @param columns1 an array of column indices of the first type of tuple: * the first column is 1, the second is 2, ... * @param columns2 an array of column indices of the second type of tuple: * the first column is 1, the second is 2, ... * @param ascending an array of <code>boolean</code> values that determines * the order (<tt>ascending=true</tt>/<tt>descending=false</tt>) for * each dimension. * @param caseInsensiveStringorderingColumns determines string columns that * are compared using a case insensitive ordering. These indices * have to be chosen according to the first type of tuple. If a * string column is not listed here, the comparison is case * sensitive. It is convenient to use the method * {@link xxl.core.relational.metaData.ResultSetMetaDatas#getStringColumns(java.sql.ResultSetMetaData)}. * @return a comparator that compares two tuples of different type * according to a subset of column indices. */ public static Comparator<Tuple> getTupleComparator(int[] columns1, int[] columns2, boolean[] ascending, int[] caseInsensiveStringorderingColumns) { if (columns1.length == 0) throw new IllegalArgumentException("no columns specified"); if (columns1.length != columns2.length || columns1.length != ascending.length) throw new IllegalArgumentException("length of specified parameter arrays does not match"); java.util.Arrays.sort(caseInsensiveStringorderingColumns); List<Comparator<Tuple>> tupleComparators = new ArrayList<Comparator<Tuple>>(columns1.length); Comparator<Tuple> tupleComparator; for (int i = 0; i < columns1.length; i++) { tupleComparator = new FeatureComparator<Object, Tuple>( Comparators.newNullSensitiveComparator( java.util.Arrays.binarySearch(caseInsensiveStringorderingColumns, columns1[i]) < 0 ? Comparators.getObjectComparator(new ComparableComparator()) : Comparators.getObjectComparator(String.CASE_INSENSITIVE_ORDER) ), getObjectFunction(columns1[i]), getObjectFunction(columns2[i]) ); tupleComparators.add(ascending[i] ? tupleComparator : new InverseComparator<Tuple>(tupleComparator)); } return new LexicographicalComparator<Tuple>(tupleComparators.toArray(new Comparator[tupleComparators.size()])); } /** * Returns a comparator that compares two tuples of different type * according to a subset of column indices. * * <p>The comparison uses a lexicographical ordering of the column objects. * The objects themselves have to be comparable, otherwise a * <code>ClassCastException</code> will be thrown. The comparison can be * processed in ascending or descending order.</p> * * @param columns1 an array of column indices of the first type of tuple: * the first column is 1, the second is 2, ... * @param columns2 an array of column indices of the second type of tuple: * the first column is 1, the second is 2, ... * @param ascending an array of <code>boolean</code> values that determines * the order (<tt>ascending=true</tt>/<tt>descending=false</tt>) for * each dimension. * @return a comparator that compares two tuples of different type * according to a subset of column indices. */ public static Comparator<Tuple> getTupleComparator(int[] columns1, int[] columns2, boolean[] ascending) { return getTupleComparator( columns1, columns2, ascending, new int[0] ); } /** * Returns a comparator that compares two tuples of different type * according to a subset of column indices. * * <p>The comparison uses a lexicographical ordering of the column objects. * The objects themselves have to be comparable, otherwise a * <code>ClassCastException</code> will be thrown.</p> * * @param columns1 an array of column indices of the first type of tuple: * the first column is 1, the second is 2, ... * @param columns2 an array of column indices of the second type of tuple: * the first column is 1, the second is 2, ... * @return a comparator that compares two tuples of different type * according to a subset of column indices. */ public static Comparator<Tuple> getTupleComparator(int[] columns1, int[] columns2) { return getTupleComparator( columns1, columns2, xxl.core.util.Arrays.newBooleanArray(columns1.length, true) ); } /** * Returns a comparator that compares two tuples of the same type according * to a subset of column indices. * * <p>The comparison uses a lexicographical ordering of the column objects. * The objects themselves have to be comparable, otherwise a * <code>ClassCastException</code> will be thrown. The comparison can be * processed in ascending or descending order.</p> * * @param columns an array of column indices of the tuple: the first column * is 1, the second is 2, ... * @param ascending an array of <code>boolean</code> values that determines * the order (<tt>ascending=true</tt>/<tt>descending=false</tt>) for * each dimension. * @param caseInsensiveStringorderingColumns determines string columns that * are compared using a case insensitive ordering. These indices * have to be chosen according to the first type of tuple. If a * string column is not listed here, the comparison is case * sensitive. It is convenient to use the method * {@link xxl.core.relational.metaData.ResultSetMetaDatas#getStringColumns(java.sql.ResultSetMetaData)}. * @return a comparator that compares two tuples of different type * according to a subset of column indices. */ public static Comparator<Tuple> getTupleComparator(int[] columns, boolean[] ascending, int[] caseInsensiveStringorderingColumns) { return getTupleComparator(columns, columns, ascending, caseInsensiveStringorderingColumns); } /** * Returns a comparator that compares two tuples of the same type according * to a subset of column indices. * * <p>The comparison uses a lexicographical ordering of the column objects. * The objects themselves have to be comparable, otherwise a * <code>ClassCastException</code> will be thrown. The comparison can be * processed in ascending or descending order.</p> * * @param columns an array of column indices of the tuple: the first column * is 1, the second is 2, ... * @param ascending an array of <code>boolean</code> values that determines * the order (<tt>ascending=true</tt>/<tt>descending=false</tt>) for * each dimension. * @return a comparator that compares two tuples of different type * according to a subset of column indices. */ public static Comparator<Tuple> getTupleComparator(int[] columns, boolean[] ascending) { return getTupleComparator( columns, ascending, new int[0] ); } /** * Returns a comparator that compares two tuples of the same type according * to a subset of column indices. * * <p>The comparison uses a lexicographical ordering of the column objects. * The objects themselves have to be comparable, otherwise a * <code>ClassCastException</code> will be thrown.</p> * * @param columns an array of column indices of the tuple: the first column * is 1, the second is 2, ... * @return a comparator that compares two tuples of different type * according to a subset of column indices. */ public static Comparator<Tuple> getTupleComparator(int... columns) { return getTupleComparator( columns, xxl.core.util.Arrays.newBooleanArray(columns.length, true) ); } /** * Returns a function that accesses a column of a tuple parameter. * * <p>The function domains: <pre>Tuple --> Object (column)</pre></p> * * @param columnIndex index of the column: the first column is 1, the * second is 2, ... * @return the function that accesses the columns. */ public static Function<Tuple, Object> getObjectFunction(final int columnIndex) { return new AbstractFunction<Tuple, Object>() { @Override public Object invoke(Tuple tuple) { try { return tuple.getObject(columnIndex); } catch (IndexOutOfBoundsException e) { throw new IllegalStateException("required column index is invalid: " + e.getMessage()); } } }; } /** * Maps the elements of the input cursor to array-tuples. * * @param cursor the input cursor delivering the objects. * @return a cursor containing the tuples. */ public static Cursor<Tuple> mapObjectsToTuples(Cursor<? extends Object> cursor) { return new Mapper<Object, Tuple>( new AbstractFunction<Object, ArrayTuple>() { @Override public ArrayTuple invoke(List<? extends Object> arguments) { return ArrayTuple.FACTORY_METHOD.invoke(arguments); } }, cursor ); } }