/* 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.DataInput; import java.io.DataOutput; import java.io.IOException; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.util.Arrays; import java.util.List; import xxl.core.collections.Lists; import xxl.core.functions.Function; import xxl.core.io.converters.BooleanConverter; import xxl.core.io.converters.Converter; import xxl.core.io.converters.Converters; import xxl.core.relational.Types; import xxl.core.util.WrappingRuntimeException; /** * This class is a converter for tuples which converts a tuple into a byte * value in order to read or write a tuple. */ public class TupleConverter extends Converter<Tuple> { /** * An array of converters for each column of a Tuple. */ protected List<Converter<Object>> converters; /** * A converter for boolean values is used to save, if the given column of * the tuple is a <code>null</code> value or not. */ protected BooleanConverter nullConverter; /** * A factory that is used to construct a new tuple. */ protected Function<Object, ? extends Tuple> createResTuple; /** * Creates a new converter for tuples using the given converters to read * and write the column values of tuples and the specified function for * creating tuples by using these column values. * * @param createResTuple a function that maps a list of objects (column * values) to a new result tuple. The factory methods of the tuple * default implementations {@link ArrayTuple} and {@link ListTuple} * can be used for this task. If <code>null</code> is passed, the * constructor will try to determine the type of tuple that is used * in the cursor. If it is possible, the appropriate factory method * is used. If it is not possible, {@link ArrayTuple#FACTORY_METHOD} * is used. * @param considerNullValues determines whether the converter should * consider <code>null</code> values or not. If it is set to * <code>true</code> a boolean converter is used to convert the * information whether the value is <code>null</code> or not. When * the value is not <code>null</code> the converted value follows. * If it is set to <code>false</code> the specified converters must * deal with <code>null</code> values. * @param converters an array of converters used for converting the column * values of the tuple to be converted. */ public TupleConverter(Function<Object, ? extends Tuple> createResTuple, boolean considerNullValues, Converter<Object>... converters) { this.createResTuple = createResTuple; this.converters = Arrays.asList(converters); nullConverter = considerNullValues ? BooleanConverter.DEFAULT_INSTANCE : new BooleanConverter() { @Override public Boolean read(DataInput dataInput, Boolean object) { return Boolean.FALSE; } @Override public void write(DataOutput dataOutput, Boolean object) { // nothing to be written } }; } /** * Creates a new converter for tuples using the given result set metadata * for retrieving converters to read and write the column values of tuples * and the specified function for creating tuples by using these column * values. * * @param createResTuple a function that maps a list of objects (column * values) to a new result tuple. The factory methods of the tuple * default implementations {@link ArrayTuple} and {@link ListTuple} * can be used for this task. If <code>null</code> is passed, the * constructor will try to determine the type of tuple that is used * in the cursor. If it is possible, the appropriate factory method * is used. If it is not possible, {@link ArrayTuple#FACTORY_METHOD} * is used. * @param considerNullValues determines whether the converter should * consider <code>null</code> values or not. If it is set to * <code>true</code> a boolean converter is used to convert the * information whether the value is <code>null</code> or not. When * the value is not <code>null</code> the converted value follows. * If it is set to <code>false</code> the specified converters must * deal with <code>null</code> values. * @param metadata the result set metadata is used to retrieve converters * for reading and writing the column values of tuples. */ public TupleConverter(Function<Object, ? extends Tuple> createResTuple, boolean considerNullValues, ResultSetMetaData metadata) { this.createResTuple = createResTuple; try { int size = metadata.getColumnCount(); converters = Lists.initializedList(null, size); // Construction of the Converter array for (int i = 1; i <= size; i++) setConverter(i, Converters.getConverterForJavaType(Types.getJavaTypeName(Types.getJavaType(metadata.getColumnType(i))))); nullConverter = considerNullValues ? BooleanConverter.DEFAULT_INSTANCE : new BooleanConverter() { @Override public Boolean read(DataInput dataInput, Boolean object) { return Boolean.FALSE; } @Override public void write(DataOutput dataOutput, Boolean object) { // nothing to be written } }; } catch(SQLException e) { throw new WrappingRuntimeException(e); } } /** * Creates a new converter for tuples using the given converters to read * and write the column values of tuples. The converter's <code>read</code> * method returns array tuples generated by the * {@link ArrayTuple#FACTORY_METHOD factory method} located in the class * <code>ArrayTuple</code>. * * @param considerNullValues determines whether the converter should * consider <code>null</code> values or not. If it is set to * <code>true</code> a boolean converter is used to convert the * information whether the value is <code>null</code> or not. When * the value is not <code>null</code> the converted value follows. * If it is set to <code>false</code> the specified converters must * deal with <code>null</code> values. * @param converters an array of converters used for converting the column * values of the tuple to be converted. */ public TupleConverter(boolean considerNullValues, Converter<Object>... converters) { this(ArrayTuple.FACTORY_METHOD, considerNullValues, converters); } /** * Creates a new converter for tuples using the given result set metadata * for retrieving converters to read and write the column values of tuples. * The converter's <code>read</code> method returns array tuples generated * by the {@link ArrayTuple#FACTORY_METHOD factory method} located in the * class <code>ArrayTuple</code>. * * @param considerNullValues determines whether the converter should * consider <code>null</code> values or not. If it is set to * <code>true</code> a boolean converter is used to convert the * information whether the value is <code>null</code> or not. When * the value is not <code>null</code> the converted value follows. * If it is set to <code>false</code> the specified converters must * deal with <code>null</code> values. * @param metadata the result set metadata is used to retrieve converters * for reading and writing the column values of tuples. */ public TupleConverter(boolean considerNullValues, ResultSetMetaData metadata) { this(ArrayTuple.FACTORY_METHOD, considerNullValues, metadata); } /** * Creates a new converter for tuples using the given converters to read * and write the column values of tuples. The converter's <code>read</code> * method returns array tuples generated by the * {@link ArrayTuple#FACTORY_METHOD factory method} located in the class * <code>ArrayTuple</code>. * * @param converters an array of converters used for converting the column * values of the tuple to be converted. */ public TupleConverter(Converter<Object>... converters) { this(ArrayTuple.FACTORY_METHOD, true, converters); } /** * Creates a new converter for tuples using the given result set metadata * for retrieving converters to read and write the column values of tuples. * The converter's <code>read</code> method returns array tuples generated * by the {@link ArrayTuple#FACTORY_METHOD factory method} located in the * class <code>ArrayTuple</code>. * * @param metadata the result set metadata is used to retrieve converters * for reading and writing the column values of tuples. */ public TupleConverter(ResultSetMetaData metadata) { this(ArrayTuple.FACTORY_METHOD, true, metadata); } /** * Sets a converter for the specified column of the tuple. * * @param index the number of the column whose values should be converter * by the given converter. * @param converter the converter used for reading and writing the values * of the specified column. * @throws IndexOutOfBoundsException if the given column number less than * <code>1</code>. */ public void setConverter(int index, Converter<Object> converter) throws IndexOutOfBoundsException{ for (int i = index - converters.size() - 1; i > 0; i--) converters.add(null); converters.set(index-1, converter); } /** * Returns the converter used for converting the values of the specified * column. * * @param index the number of the column whose data is converter by the * given converter. * @return the converter used for converting the values of the specified * column. * @throws IndexOutOfBoundsException if the given column number less than * <code>1</code> or greater than the number of registered * converters. */ public Converter<Object> getConverter(int index) throws IndexOutOfBoundsException{ return converters.get(index-1); } /** * Writes the byte value of a tuple to the specified data output stream. * * @param output the output stream the byte value of the tuple is written * to. * @param tuple the tuple to be written on the output stream. * @throws IOException if an I/O error occurs. */ @Override public void write(DataOutput output, Tuple tuple) throws IOException { boolean isNull; for (int i = 1; i <= tuple.getColumnCount(); i++) { nullConverter.writeBoolean(output, isNull = tuple.isNull(i)); if (!isNull) converters.get(i-1).write(output, tuple.getObject(i)); } } /** * Reads the tuple from the data input stream. * * @param input the input stream containing the contents of a tuple. * @param tuple the tuple to be restored. This implementation ignores the * given tuple. * @return the tuple read from the data input stream. * @throws IOException if an I/O error occurs. */ @Override public Tuple read(DataInput input, Tuple tuple) throws IOException { Object[] columns = new Object[tuple != null ? tuple.getColumnCount() : converters.size()]; for (int i = 0; i < columns.length; i++) columns[i] = nullConverter.readBoolean(input) ? null : converters.get(i).read(input, tuple != null ? tuple.getObject(i+1) : columns[i]); return createResTuple.invoke(Arrays.asList(columns)); } }