/* 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.resultSets; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.sql.ResultSetMetaData; import java.sql.SQLException; import xxl.core.cursors.MetaDataCursor; import xxl.core.io.converters.Converter; import xxl.core.io.converters.Converters; import xxl.core.io.converters.SerializableConverter; import xxl.core.relational.metaData.ResultSetMetaDatas; import xxl.core.relational.tuples.Tuple; import xxl.core.util.metaData.CompositeMetaData; /** * This class wraps a metadata cursor to a result set. * * <p>Result sets and metadata cursors are equivalent concepts. Both handle * sets of objects with metadata. The different direction is done by the class * {@link xxl.core.relational.cursors.ResultSetMetaDataCursor}.</p> */ public class MetaDataCursorResultSet extends AbstractResultSet { /** * The metadata cursor that is wrapped into a result set. */ protected MetaDataCursor<? extends Tuple, CompositeMetaData<Object, Object>> metaDataCursor; /** * The result set's metadata provided by the metadata cursor. */ protected ResultSetMetaData metaData; /** * The next tuple of the metadata cursor. */ protected Tuple next; /** * The index of the last column that became accessed. */ protected int lastColumnIndex = 0; /** * Constructs a new result set based on the given metadata cursor. * * @param metaDataCursor the metadata cursor that should be wrapped to a * result set. */ public MetaDataCursorResultSet(MetaDataCursor<? extends Tuple, CompositeMetaData<Object, Object>> metaDataCursor) { this.metaDataCursor = metaDataCursor; this.metaData = ResultSetMetaDatas.getResultSetMetaData(metaDataCursor); } /** * Moves the cursor down one row from its current position. A * <code>ResultSet</code> cursor is initially positioned before the first * row; the first call to the method <code>next</code> makes the first row * the current row; the second call makes the second row the current row, * and so on. * * <p>If an input stream is open for the current row, a call to the method * <code>next</code> will implicitly close it. A <code>ResultSet</code> * object's warning chain is cleared when a new row is read.</p> * * @return <code>true</code> if the new current row is valid; * <code>false</code> if there are no more rows. * @throws SQLException if a database access error occurs. */ @Override public boolean next() throws SQLException { if (metaDataCursor.hasNext()) { next = metaDataCursor.next(); return true; } return false; } /** * Releases this <code>ResultSet</code> object's database and JDBC * resources immediately instead of waiting for this to happen when it is * automatically closed. * * <p><b>Note:</b> A <code>ResultSet</code> object is automatically closed * by the <code>Statement</code> object that generated it when that * <code>Statement</code> object is closed, re-executed, or is used to * retrieve the next result from a sequence of multiple results. A * <code>ResultSet</code> object is also automatically closed when it is * garbage collected.</p> * * @throws SQLException if a database access error occurs. */ @Override public void close() throws SQLException { metaDataCursor.close(); } /** * Gets the value of the designated column in the current row of this * <code>ResultSet</code> object as an <code>Object</code> in the Java * programming language. * * <p>This method will return the value of the given column as a Java * object. The type of the Java object will be the default Java object type * corresponding to the column's SQL type, following the mapping for * built-in types specified in the JDBC specification. If the value is an * SQL <code>NULL</code>, the driver returns a Java <code>null</code>.</p> * * <p>This method may also be used to read database-specific abstract data * types. In the JDBC 2.0 API, the behavior of method * <code>getObject</code> is extended to materialize data of SQL * user-defined types. When a column contains a structured or distinct * value, the behavior of this method is as if it were a call to: * <code>getObject(columnIndex, this.getStatement().getConnection().getTypeMap())</code>. * * @param columnIndex the first column is 1, the second is 2, ... * @return a <code>java.lang.Object</code> holding the column value. * @throws SQLException if a database access error occurs. */ @Override public Object getObject(int columnIndex) throws SQLException { lastColumnIndex = columnIndex; return next.getObject(columnIndex); } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a <code>byte</code> array in the Java * programming language. The bytes represent the raw values returned by the * driver. * * @param columnIndex the first column is 1, the second is 2, ... * @return the column value; if the value is SQL <code>NULL</code>, the * value returned is <code>null</code>. * @throws SQLException if a database access error occurs. */ @Override public byte[] getBytes(int columnIndex) throws SQLException { return getBytes(columnIndex, Converters.getObjectConverter(SerializableConverter.DEFAULT_INSTANCE)); } /** * Retrieves the value of the designated column in the current row of this * <code>ResultSet</code> object as a <code>byte</code> array in the Java * programming language. The byte representation of the column is generated * using the specified converter. * * @param columnIndex the first column is 1, the second is 2, ... * @param converter the converter used for transforming the column into a * byte representation. * @return the column value; if the value is SQL <code>NULL</code>, the * value returned is <code>null</code>. * @throws SQLException if a database access error occurs. */ public byte[] getBytes(int columnIndex, Converter<Object> converter) throws SQLException { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); converter.write(oos, getObject(columnIndex)); byte[] result = baos.toByteArray(); oos.close(); return result; } catch (IOException ioe) { throw new SQLException("I/O exception occured during convertion : " + ioe.getMessage()); } } /** * Reports whether the last column read had a value of SQL * <code>NULL</code>. Note that you must first call one of the getter * methods on a column to try to read its value and then call the method * <code>wasNull</code> to see if the value read was SQL <code>NULL</code>. * * @return <code>true</code> if last column read was SQL <code>NULL</code> * and <code>false</code> otherwise. * @throws SQLException if a database access error occurs. */ @Override public boolean wasNull() throws SQLException { return next.getObject(lastColumnIndex) == null ? true : false; } /** * Retrieves the number, types and properties of this * <code>ResultSet</code> object's columns. * * @return the description of this <code>ResultSet</code> object's columns. * @throws SQLException if a database access error occurs. */ @Override public ResultSetMetaData getMetaData() throws SQLException { return metaData; } /** * Maps the given <code>ResultSet</code> column name to its * <code>ResultSet</code> column index. * * @param columnName the name of the column. * @return the column index of the given column name. * @throws SQLException if the <code>ResultSet</code> object does not * contain <code>columnName</code> or a database access error * occurs. */ @Override public int findColumn(String columnName) throws SQLException { for (int index = 1; index <= metaData.getColumnCount(); index++) if (metaData.getColumnName(index).equals(columnName)) return index; throw new SQLException("a column with vthe specified name cannot be found"); } /** * Updates the designated column with an <code>Object</code> value. The * updater methods are used to update column values in the current row or * the insert row. The updater methods do not update the underlying * database; instead the <code>updateRow</code> or <code>insertRow</code> * methods are called to update the database. * * @param columnName the name of the column. * @param x the new column value. * @throws SQLException if a database access error occurs. */ @Override public void updateObject(String columnName, Object x) throws SQLException { updateObject(findColumn(columnName), x); } /** * Deletes the current row from this <code>ResultSet</code> object and * from the underlying database. This method cannot be called when the * cursor is on the insert row. * * @throws SQLException if a database access error occurs or if this method * is called when the cursor is on the insert row. */ @Override public void deleteRow() throws SQLException { metaDataCursor.remove(); } }