/*
* 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.io.PrintStream;
import java.math.BigInteger;
import java.sql.Date;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.ArrayList;
import xxl.core.io.converters.meta.ExtendedResultSetMetaData;
import xxl.core.relational.metaData.ExtendedColumnMetaData;
import xxl.core.relational.metaData.TupleMetaData;
import xxl.core.util.Arrays;
/**
* The <code>ComparableTuples</code> class provides various algebraic or useful methods for dealing with
* {@link Tuple tuple objects} .
*
* @author Marcus Pinnecke (pinnecke@mathematik.uni-marburg.de)
*
*/
public class ComparableTuples {
/*
* Check if an IndexOutOfBoundException would be occur and throw an exception if it is so
*/
private static void checkIndexIsInBounds(int[] destProjectionIndizies,
int srcColumnCount) throws IllegalArgumentException {
for (int column : destProjectionIndizies) {
if (column < 1 || column > srcColumnCount)
throw new IllegalArgumentException(
"Projection contains invalid column index. Column index is smaller than 1 or out of bounds (value is \""
+ column + "\")");
}
}
/*
* Check if the array of column indices is not empty or throw an exception
*/
private static void checkProjectionIsNotEmpty(int[] destProjectionIndizies)
throws IllegalArgumentException {
if (destProjectionIndizies.length == 0)
throw new IllegalArgumentException(
"Projection indizies array have to contain at least one column index.");
}
/*
* Check if tuple dimension of source is smaller than of the destination or throw an exception
*/
private static void checkProjectionLength(int src, int dest)
throws IndexOutOfBoundsException {
if (src < dest)
throw new IndexOutOfBoundsException(
"Projection for tuple contains more columns than the source (#col src: "
+ src + ", #col dest: " + dest + ").");
}
/*
* Prevent users to double columns by projection twice to the same column. If the column indices
* are not unique throw an exception
*/
private static ArrayList<ExtendedColumnMetaData> checkUniqueIndexAndProject(
int[] destProjectionIndizies, ExtendedResultSetMetaData srcMetaData) {
ArrayList<ExtendedColumnMetaData> subetColumnMetaData = new ArrayList<>();
ArrayList<Integer> accPreviousProjectionIndizies = new ArrayList<>();
for (int column : destProjectionIndizies) {
if (!accPreviousProjectionIndizies.contains(column)) {
subetColumnMetaData.add(srcMetaData.getColumnMetaData(column));
accPreviousProjectionIndizies.add(column);
} else
throw new IllegalArgumentException(
"Projection for meta data contains at least one column index twice (requested column is "
+ column
+ "). A column index have to be unique for projection operation.");
}
return subetColumnMetaData;
}
/*
* Prevent users to double columns by projection twice to the same column. If the column indices
* are not unique throw an exception
*/
private static ArrayList<Object> checkUniqueIndexAndProject(
int[] destProjectionIndizies, Tuple tuple) {
ArrayList<Object> content = new ArrayList<Object>();
ArrayList<Integer> accPreviousProjectionIndizies = new ArrayList<>();
for (int column : destProjectionIndizies) {
if (!accPreviousProjectionIndizies.contains(column)) {
content.add(tuple.getObject(column));
accPreviousProjectionIndizies.add(column);
} else
throw new IllegalArgumentException(
"Projection for tuple contains at least one column index twice (requested column is "
+ column
+ "). A column index have to be unique for projection operation.");
}
return content;
}
/**
* Creates a deep and hard copy of a tuple object <i>t</i>. Neither the pointer to the copy of
* <i>t</i> nor the pointer of <i>t</i>'s components are identical to the original object
* <i>t</i>. This ensures modifications of <i>t</i> will <b>not</b> effect it's copies.
*
* @param t The tuple to copy
* @return A tuple identical to <i>t</i> which is independent of <i>t</i> with respect to
* <i>t</i>'s references.
*/
public static ColumnComparableTuple clone(ColumnComparableTuple t) {
Object[] content = t.toArray();
Object[] clone = new Object[content.length];
for (int i = 0; i < clone.length; i++) {
Object element = content[i];
if (element instanceof String)
clone[i] = new String(String.valueOf(element));
else if (element instanceof BigInteger)
clone[i] = new BigInteger(((BigInteger) element).toByteArray());
else if (element instanceof Boolean)
clone[i] =
new Boolean(Boolean.valueOf(((Boolean) element).booleanValue()));
else if (element instanceof Byte)
clone[i] = new Byte(Byte.valueOf(((Byte) element).byteValue()));
else if (element instanceof Date)
clone[i] = new Date(((Date) element).getTime());
else if (element instanceof Double)
clone[i] = new Double(Double.valueOf(((Double) element).doubleValue()));
else if (element instanceof Float)
clone[i] = new Float(Float.valueOf(((Float) element).floatValue()));
else if (element instanceof Integer)
clone[i] = new Integer(Integer.valueOf(((Integer) element).intValue()));
else if (element instanceof Long)
clone[i] = new Long(Long.valueOf(((Long) element).longValue()));
else if (element instanceof Short)
clone[i] = new Short(Short.valueOf(((Short) element).shortValue()));
else if (element instanceof Time)
clone[i] = new Time(((Time) element).getTime());
else if (element instanceof Timestamp)
clone[i] = new Timestamp(((Timestamp) element).getTime());
else
throw new UnsupportedOperationException(
"Not implemented yet for given class. ("
+ element.getClass().getName() + ")");
}
return new ColumnComparableArrayTuple(clone);
}
/**
* Creates a deep and hard copy of a tuple object <i>t</i>. Neither the pointer to the copy of
* <i>t</i> nor the pointer of <i>t</i>'s components are identical to the original object
* <i>t</i>. This ensures modifications of <i>t</i> will <b>not</b> effect it's copies.
*
* @param t The tuple to copy
* @return A tuple identical to <i>t</i> which is independent of <i>t</i> with respect to
* <i>t</i>'s references.
*/
public static Tuple clone(Tuple t) {
Object[] content = t.toArray();
Object[] clone = new Object[content.length];
for (int i = 0; i < clone.length; i++) {
Object element = content[i];
if (element instanceof String)
clone[i] = new String(String.valueOf(element));
else if (element instanceof BigInteger)
clone[i] = new BigInteger(((BigInteger) element).toByteArray());
else if (element instanceof Boolean)
clone[i] =
new Boolean(Boolean.valueOf(((Boolean) element).booleanValue()));
else if (element instanceof Byte)
clone[i] = new Byte(Byte.valueOf(((Byte) element).byteValue()));
else if (element instanceof Date)
clone[i] = new Date(((Date) element).getTime());
else if (element instanceof Double)
clone[i] = new Double(Double.valueOf(((Double) element).doubleValue()));
else if (element instanceof Float)
clone[i] = new Float(Float.valueOf(((Float) element).floatValue()));
else if (element instanceof Integer)
clone[i] = new Integer(Integer.valueOf(((Integer) element).intValue()));
else if (element instanceof Long)
clone[i] = new Long(Long.valueOf(((Long) element).longValue()));
else if (element instanceof Short)
clone[i] = new Short(Short.valueOf(((Short) element).shortValue()));
else if (element instanceof Time)
clone[i] = new Time(((Time) element).getTime());
else if (element instanceof Timestamp)
clone[i] = new Timestamp(((Timestamp) element).getTime());
else
throw new UnsupportedOperationException(
"Not implemented yet for given class. ("
+ element.getClass().getName() + ")");
}
return new ArrayTuple(clone);
}
/**
* Prints a given tuple to the <code>PrintStream</code> and adds a new line.
*
* @param s PrintStream
* @param tuple Tuple to be printed
*/
public static void println(PrintStream s, Tuple tuple) {
Object[] content = tuple.toArray();
Arrays.println(content, s);
}
/**
* An unary operation where <b>columns</b> is an array of column indices. The result of this
* operation is a subset of <b>metaData</b> in which other attributes (column indices) than
* <b>columns</b> are discarded (or excluded). <br/>
* <br/>
*
* <b>Note:</b> The first column index is <b>1</b> not <i>0</i>.
*
* @param metaData The source meta data
* @param columns Array of indices which represents attribute columns and which should be keep in
* the result.
* @return A subset of <b>metaData</b> which is clipped to the given columns.
*
* <b>Note: </b> This operation does not change the original object. Instead it returns a
* new object which is a subset of the original object.
*
* @throws IllegalArgumentException If <b>columns</b> is empty or at least one components is
* lesser than 1. This exception will be also thrown if a projection column is set twice
* or more in <b>columns</b>.
*
* @throws IndexOutOfBoundsException If <b>columns</b> contains more (unique) indices than the
* dimension of this tuple is or if at least one component of <b>columns</b> is out of
* bounds.
*
* @throws SQLException If it fails to work with the underlying {@link java.sql.ResultSetMetaData}
*/
public static ExtendedResultSetMetaData project(
final ExtendedResultSetMetaData metaData, final int[] columns)
throws SQLException, IllegalArgumentException, IndexOutOfBoundsException {
int srcTupleDimension = metaData.getColumnCount();
int desTupleDimension = columns.length;
checkProjectionIsNotEmpty(columns);
checkProjectionLength(srcTupleDimension, desTupleDimension);
checkIndexIsInBounds(columns, srcTupleDimension);
ArrayList<ExtendedColumnMetaData> columnSubsetMetaData =
checkUniqueIndexAndProject(columns, metaData);
return new TupleMetaData(metaData.getTableName(0),
columnSubsetMetaData
.toArray(new ExtendedColumnMetaData[columnSubsetMetaData.size()]));
}
/**
* An unary operation where <b>columns</b> is an array of column indices. The result of this
* operation is a subset of <b>tuple</b> in which other attributes (column indices) than
* <b>columns</b> are discarded (or excluded). <br/>
* <br/>
*
* <b>Note:</b> The first column index is <b>1</b> not <i>0</i>.
*
* @param tuple The source tuple
* @param columns Array of indices which represents attribute columns and which should be keep in
* the result.
* @return A subset of <b>tuple</b> which is clipped to the given columns.
*
* <b>Note: </b> This operation does not change the original object. Instead it returns a
* new object which is a subset of the original object.
*
* @throws IllegalArgumentException If <b>columns</b> is empty or at least one components is
* lesser than 1. This exception will be also thrown if a projection column is set twice
* or more in <b>columns</b>.
*
* @throws IndexOutOfBoundsException If <b>columns</b> contains more (unique) indices than the
* dimension of this tuple is or if at least one component of <b>columns</b> is out of
* bounds.
*/
public static Tuple project(final Tuple tuple, final int[] columns)
throws IllegalArgumentException, IndexOutOfBoundsException {
int srcTupleDimension = tuple.getColumnCount();
int desTupleDimension = columns.length;
checkProjectionIsNotEmpty(columns);
checkProjectionLength(srcTupleDimension, columns.length);
checkIndexIsInBounds(columns, srcTupleDimension);
ArrayList<Object> content = checkUniqueIndexAndProject(columns, tuple);
return new ArrayTuple(content.toArray(new Object[desTupleDimension]));
}
}