/* * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is the Kowari Metadata Store. * * The Initial Developer of the Original Code is Plugged In Software Pty * Ltd (http://www.pisoftware.com, mailto:info@pisoftware.com). Portions * created by Plugged In Software Pty Ltd are Copyright (C) 2001,2002 * Plugged In Software Pty Ltd. All Rights Reserved. * * Contributor(s): N/A. * * [NOTE: The text of this Exhibit A may differ slightly from the text * of the notices in the Source Code files of the Original Code. You * should use the text of this Exhibit A rather than the text found in the * Original Code Source Code for Your Modifications.] * */ package org.mulgara.util; // Java 2 standard packages import java.io.InputStream; import java.io.Reader; import java.io.Serializable; import java.sql.*; import java.util.*; // third party packages import org.apache.log4j.Logger; // Log4J /** * Implementation of {@link ResultSet} in memory. This is particularly suitable * for generating test cases, though its use is more widespread. It's * not a correct {@link ResultSet} in many respects, the foremost being an * absence of column typing. * * @created 2001-07-12 * * @author <a href="http://staff.pisoftware.com/raboczi">Simon Raboczi</a> * * @version $Revision: 1.9 $ * * @modified $Date: 2005/01/05 04:59:29 $ * * @maintenanceAuthor $Author: newmana $ * * @company <A href="mailto:info@PIsoftware.com">Plugged In Software</A> * * @copyright © 2001-2003 <A href="http://www.PIsoftware.com/">Plugged In * Software Pty Ltd</A> * * @licence <a href="{@docRoot}/../../LICENCE">Mozilla Public License v1.1</a> */ public class MemoryResultSet extends AbstractMulgaraResultSet implements Serializable { /** * Allow newer compiled version of the stub to operate when changes * have not occurred with the class. * NOTE : update this serialVersionUID when a method or a public member is * deleted. */ static final long serialVersionUID = 447953576074487320L; /** * Logger. */ private static final Logger logger = Logger.getLogger(MemoryResultSet.class); /** * The metadata for this result set. */ private ResultSetMetaData metaData; /** * The rows of the result set. */ private List<ResultSetRow> rows = new RowList<ResultSetRow>(); /** * The current row index. */ private int currentRowIndex = 0; /** * The current row. */ private ResultSetRow currentRow = null; // // Constructors // /** * Create a result set with named columns and no rows. * * @param newColumnNames the new column names for this result set. * @throws SQLException on failure */ public MemoryResultSet(String[] newColumnNames) throws SQLException { // FIXME: should validate columnNames, checking for duplicate names, etc // initialize fields columnNames = newColumnNames; metaData = new ResultSetMetaDataImpl(columnNames); // move the cursor before the first row beforeFirst(); } /** * Create a result set with specified metadata and no rows. * * @param metaData PARAMETER TO DO * @throws SQLException on failure */ public MemoryResultSet(ResultSetMetaData metaData) throws SQLException { initialiseMetaData(metaData); // move the cursor before the first row beforeFirst(); } /** * Create a result set with content copied from an existing result set. * * @param resultSet PARAMETER TO DO * @throws SQLException on failure */ public MemoryResultSet(ResultSet resultSet) throws SQLException { // Validate "resultSet" parameter if (resultSet == null) { throw new IllegalArgumentException("Null \"resultSet\" parameter"); } // Copy columns initialiseMetaData(resultSet.getMetaData()); // Copy rows if (resultSet.getClass() == MemoryResultSet.class) { rows.addAll( ( (MemoryResultSet) resultSet).rows); } else { // Don't assume that it hasn't already been read. resultSet.beforeFirst(); // Repeat until we get all of the items from the result sets. while (resultSet.next()) { ResultSetRow row = new ResultSetRow(resultSet); for (int i = 0; i < columnNames.length; i++) { row.setObject(columnNames[i], resultSet.getObject(columnNames[i])); } addRow(row); } } } /** * Overwrites the existing set of rows available. * * @param newRows the new rows to set. * @throws SQLException EXCEPTION TO DO */ public void setAllRows(List<ResultSetRow> newRows) throws SQLException { rows = newRows; } // next() /** * Return the object at the given index. * * @param columnIndex the index of the object to retrieve * @return the value at the given index * @throws SQLException on failure */ public Object getObject(int columnIndex) throws SQLException { // throw an error if the current row is invalid if (this.currentRow == null) throw new SQLException("Not on a row."); // get the value out of the current row return this.currentRow.getObject(columnIndex); } // getObject() /** * Return the string at the given index. * * @param columnIndex the index of the string to retrieve * @return the value at the given index (possibly <code>null</code>) * @throws SQLException on failure */ public String getString(int columnIndex) throws SQLException { // throw an error if the current row is invalid if (this.currentRow == null) throw new SQLException("Not on a row."); // get the value out of the current row Object object = this.currentRow.getObject(columnIndex); return (object == null) ? null : object.toString(); } // getString() /** * Returns the metadata for this result set. * * @return the metadata for this result set * @throws SQLException on failure */ public ResultSetMetaData getMetaData() throws SQLException { return metaData; } /** * Returns whether the cursor is before the first row. * * @return true if the cursor is before the first row in the result set, false * otherwise * @throws SQLException on failure */ public boolean isBeforeFirst() throws SQLException { return this.currentRowIndex == 0; } /** * Returns true if the cursor is after the last row. * * @return true if the cursor is after the first row in the result set, false * otherwise * @throws SQLException on failure */ public boolean isAfterLast() throws SQLException { return this.currentRowIndex > this.rows.size(); } /** * Returns true if the cursor is on the first row. * * @return true if the cursor is on the first row, false otherwise * @throws SQLException on failure */ public boolean isFirst() throws SQLException { return this.currentRowIndex == 1; } /** * Returns true if the cursor is on the last row. * * @return true if the cursor is on the las row, false otherwise * @throws SQLException on failure */ public boolean isLast() throws SQLException { return this.currentRowIndex == this.rows.size(); } /** * Returns the entire rows underlying the result set. * * @return the entire rows underlying the result set. * @throws SQLException EXCEPTION TO DO */ public List<ResultSetRow> getAllRows() throws SQLException { return rows; } /** * Returns the index of the row the cursor is on. * * @return the index of the row the cursor is on * @throws SQLException on failure */ public int getRow() throws SQLException { return this.currentRowIndex; } /** * Returns the total size of the number of rows. * * @return the total size of the number of rows available. */ public int getTotalRowSize() { return rows.size(); } /** * Gets the CurrentRow attribute of the MemoryResultSet object * * @return The CurrentRow value */ public ResultSetRow getCurrentRow() { return currentRow; } // // Methods implementing the ResultSet interface // /** * Moves the cursor down one row from its current position. * * @return true if the new current row is valid; false if there are no more * rows * @throws SQLException on failure */ public boolean next() throws SQLException { boolean returnState = false; // advance the cursor if we can if (!this.isLast()) { this.currentRowIndex++; this.currentRow = (ResultSetRow)this.rows.get(this.currentRowIndex - 1); returnState = true; } else { this.currentRow = null; returnState = false; } // end if // return return returnState; } /** * Moves the cursor to before the first row in the result set. * * @throws SQLException on failure */ public void beforeFirst() throws SQLException { // return members to their default state this.currentRowIndex = 0; this.currentRow = null; } /** * Moves the cursor to after the last row in the result set. * * @throws SQLException on failure */ public void afterLast() throws SQLException { this.currentRowIndex = this.rows.size() + 1; this.currentRow = null; } /** * Moves the cursor to the first row in this ResultSet object. * * @return true if the cursor is on a valid row; false if there are no rows in * the result set * @throws SQLException always */ public boolean first() throws SQLException { boolean returnState = false; if (this.rows.size() > 0) { this.beforeFirst(); this.next(); returnState = true; } return returnState; } /** * Moves the cursor to the last row in the result set. * * @return RETURNED VALUE TO DO * @throws SQLException always */ public boolean last() throws SQLException { boolean returnState = false; if ( (this.rows != null) && (this.rows.size() > 0)) { this.currentRowIndex = this.rows.size(); this.currentRow = (ResultSetRow)this.rows.get(this.currentRowIndex - 1); returnState = true; } return returnState; } /** * Adds a new row to the current set of rows. * * @param row new row to add to the end of the queue. */ public void addRow(ResultSetRow row) { rows.add(row); } /** * Moves the cursor to the given row number (takes obsolute value) in this * ResultSet object. An attempt to position the cursor beyond the first/last * row in the result set leaves the cursor before the first row or after the * last row. * * @param row the number of the row to which the cursor should move. A * positive number indicates the row number counting from the beginning * of the result set; a negative number indicates the row number counting * from the end of the result set * @return RETURNED VALUE TO DO * @throws SQLException on failure */ public boolean absolute(int row) throws SQLException { boolean returnState = false; // Work forward from the start if (row >= 0) { if (row == 0) { beforeFirst(); } else if (row <= this.rows.size()) { this.currentRowIndex = row; this.currentRow = this.rows.get(this.currentRowIndex - 1); returnState = true; } else { afterLast(); } } else { // Work back from the end if ( (this.rows.size() + row) >= 0) { this.currentRowIndex = (this.rows.size() + 1) + row; this.currentRow = this.rows.get(this.currentRowIndex - 1); returnState = true; } else { beforeFirst(); } } return returnState; } /** * Moves the cursor a relative number of rows, either positive or negative * from its current position. Attempting to move beyond the first/last row in * the result set positions the cursor before/after the the first/last row. * Calling relative(0) is valid, but does not change the cursor position. * * @param numRows an int specifying the number of rows to move from the * current row; a positive number moves the cursor forward; a negative * number moves the cursor backward * @return RETURNED VALUE TO DO * @throws SQLException on failure */ public boolean relative(int numRows) throws SQLException { boolean returnState = false; // Work forward from the start if (numRows >= 0) { if (numRows <= (this.rows.size() - this.currentRowIndex)) { this.currentRowIndex += numRows; this.currentRow = (ResultSetRow)this.rows.get(this.currentRowIndex - 1); returnState = true; } else { afterLast(); } } else { // Work back from the end if ( (this.currentRowIndex + numRows) > 0) { // Add 1 to size to go to end of list then add the negative row number this.currentRowIndex += numRows; this.currentRow = this.rows.get(this.currentRowIndex - 1); returnState = true; } else { beforeFirst(); } } return returnState; } // // Relational algebra methods // /** * Result sets are equal if their rows are equal. Both row and column ordering * is signicant. * * @param object PARAMETER TO DO * @return RETURNED VALUE TO DO */ public boolean equals(Object object) { if (! (object instanceof ResultSet)) return false; try { // Convert the other result set into a comparable form MemoryResultSet testResultSet = (object instanceof MemoryResultSet) ? (MemoryResultSet) object : new MemoryResultSet( (ResultSet) object); // Compare the rows return rows.equals(testResultSet.rows); } catch (SQLException e) { return false; } } public int hashCode() { return rows.hashCode(); } /** * Produce a string version of the result set. Displaying the available * columns and rows. * * @return the string version of the result set. */ public String toString() { try { StringBuffer buffer = new StringBuffer(getColumnNames().length + " columns:"); // Save the current state of the result set. int tmpCurrentRow = getRow(); // Get the names of the columns for (int i = 0; i < columnNames.length; i++) { buffer.append(" ").append(columnNames[i]); } buffer.append(" (").append(rows.size()).append(" rows)"); // Start at the start beforeFirst(); while (next()) { buffer.append("\n").append(getCurrentRow()); } // Restore the state of the result set. absolute(tmpCurrentRow); return buffer.toString(); } catch (SQLException se) { logger.error("Failed to convert object to string", se); return ""; } } /** * Initialises the column names and metadata from the given metadata object. * * @param newMetaData PARAMETER TO DO * @throws SQLException if there was an error getting the metadata attributes. */ private void initialiseMetaData(ResultSetMetaData newMetaData) throws SQLException { int columnNameCount = newMetaData.getColumnCount(); columnNames = new String[columnNameCount]; for (int i = 0; i < columnNameCount; i++) { columnNames[i] = newMetaData.getColumnName(i + 1); } // initialise the metadata field metaData = new ResultSetMetaDataImpl(columnNames); } public int getHoldability() throws SQLException { throw new SQLException(NOT_IMPLEMENTED); } public Reader getNCharacterStream(int columnIndex) throws SQLException { throw new SQLException(NOT_IMPLEMENTED); } public Reader getNCharacterStream(String columnLabel) throws SQLException { throw new SQLException(NOT_IMPLEMENTED); } public NClob getNClob(int columnIndex) throws SQLException { throw new SQLException(NOT_IMPLEMENTED); } public NClob getNClob(String columnLabel) throws SQLException { throw new SQLException(NOT_IMPLEMENTED); } public String getNString(int columnIndex) throws SQLException { throw new SQLException(NOT_IMPLEMENTED); } public String getNString(String columnLabel) throws SQLException { throw new SQLException(NOT_IMPLEMENTED); } public RowId getRowId(int columnIndex) throws SQLException { throw new SQLException(NOT_IMPLEMENTED); } public RowId getRowId(String columnLabel) throws SQLException { throw new SQLException(NOT_IMPLEMENTED); } public SQLXML getSQLXML(int columnIndex) throws SQLException { throw new SQLException(NOT_IMPLEMENTED); } public SQLXML getSQLXML(String columnLabel) throws SQLException { throw new SQLException(NOT_IMPLEMENTED); } public <T> T getObject(int columnIndex, Class<T> type) throws SQLException { throw new SQLException(NOT_IMPLEMENTED); } public <T> T getObject(String columnLabel, Class<T> type) throws SQLException { throw new SQLException(NOT_IMPLEMENTED); } public boolean isClosed() throws SQLException { throw new SQLException(NOT_IMPLEMENTED); } public void updateAsciiStream(int columnIndex, InputStream x, long length) throws SQLException { throw new SQLException(NOT_IMPLEMENTED); } public void updateAsciiStream(String columnLabel, InputStream x, long length) throws SQLException { throw new SQLException(NOT_IMPLEMENTED); } public void updateBinaryStream(int columnIndex, InputStream x, long length) throws SQLException { throw new SQLException(NOT_IMPLEMENTED); } public void updateBinaryStream(String columnLabel, InputStream x, long length) throws SQLException { throw new SQLException(NOT_IMPLEMENTED); } public void updateBlob(int columnIndex, InputStream inputStream, long length) throws SQLException { throw new SQLException(NOT_IMPLEMENTED); } public void updateBlob(String columnLabel, InputStream inputStream, long length) throws SQLException { throw new SQLException(NOT_IMPLEMENTED); } public void updateCharacterStream(int columnIndex, Reader x, long length) throws SQLException { throw new SQLException(NOT_IMPLEMENTED); } public void updateCharacterStream(String columnLabel, Reader reader, long length) throws SQLException { throw new SQLException(NOT_IMPLEMENTED); } public void updateClob(int columnIndex, Reader reader, long length) throws SQLException { throw new SQLException(NOT_IMPLEMENTED); } public void updateClob(String columnLabel, Reader reader, long length) throws SQLException { throw new SQLException(NOT_IMPLEMENTED); } public void updateNCharacterStream(int columnIndex, Reader x, long length) throws SQLException { throw new SQLException(NOT_IMPLEMENTED); } public void updateNCharacterStream(String columnLabel, Reader reader, long length) throws SQLException { throw new SQLException(NOT_IMPLEMENTED); } public void updateNClob(int columnIndex, NClob clob) throws SQLException { throw new SQLException(NOT_IMPLEMENTED); } public void updateNClob(String columnLabel, NClob clob) throws SQLException { throw new SQLException(NOT_IMPLEMENTED); } public void updateNClob(int columnIndex, Reader reader, long length) throws SQLException { throw new SQLException(NOT_IMPLEMENTED); } public void updateNClob(String columnLabel, Reader reader, long length) throws SQLException { throw new SQLException(NOT_IMPLEMENTED); } public void updateNString(int columnIndex, String string) throws SQLException { throw new SQLException(NOT_IMPLEMENTED); } public void updateNString(String columnLabel, String string) throws SQLException { throw new SQLException(NOT_IMPLEMENTED); } public void updateRowId(int columnIndex, RowId x) throws SQLException { throw new SQLException(NOT_IMPLEMENTED); } public void updateRowId(String columnLabel, RowId x) throws SQLException { throw new SQLException(NOT_IMPLEMENTED); } public void updateSQLXML(int columnIndex, SQLXML xmlObject) throws SQLException { throw new SQLException(NOT_IMPLEMENTED); } public void updateSQLXML(String columnLabel, SQLXML xmlObject) throws SQLException { throw new SQLException(NOT_IMPLEMENTED); } public void updateNCharacterStream(int columnIndex, Reader reader) throws SQLException { // Empty stub } public void updateNCharacterStream(String columnLabel, Reader reader) throws SQLException { // Empty stub } public void updateAsciiStream(int columnIndex, InputStream inputStream) throws SQLException { // Empty stub } public void updateBinaryStream(int columnIndex, InputStream inputStream) throws SQLException { // Empty stub } public void updateCharacterStream(int columnIndex, Reader reader) throws SQLException { // Empty stub } public void updateAsciiStream(String columnLabel, InputStream inputStream) throws SQLException { // Empty stub } public void updateBinaryStream(String columnLabel, InputStream inputStream) throws SQLException { // Empty stub } public void updateCharacterStream(String columnLabel, Reader reader) throws SQLException { // Empty stub } public void updateBlob(int columnIndex, InputStream inputStream) throws SQLException { // Empty stub } public void updateBlob(String columnLabel, InputStream inputStream) throws SQLException { // Empty stub } public void updateClob(int columnIndex, Reader reader) throws SQLException { // Empty stub } public void updateClob(String columnLabel, Reader reader) throws SQLException { // Empty stub } public void updateNClob(int columnIndex, Reader reader) throws SQLException { // Empty stub } public void updateNClob(String columnLabel, Reader reader) throws SQLException { // Empty stub } public boolean isWrapperFor(Class<?> iface) throws SQLException { throw new SQLException(NOT_IMPLEMENTED); } public <T> T unwrap(Class<T> iface) throws SQLException { throw new SQLException(NOT_IMPLEMENTED); } }