/*
* PoolMan Java Object Pooling and Caching Library
* Copyright (C) 1999-2001 The Code Studio
*
* 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 2 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.
*
* The full license is located at the root of this distribution
* in the LICENSE file.
*/
package com.codestudio.sql;
// Code Studio
import com.codestudio.util.JDBCPool;
import java.io.InputStream;
import java.io.Reader;
import java.math.BigDecimal;
import java.sql.*;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Map;
/**
* This JDBC 2.0 ResultSet implementation transforms
* underlying ResultSets so they can support more advanced
* JDBC 2.0 features (scrolling, updates, etc.).
* <p>
* It is implemented as an ArrayList representing the rows
* in a ResultSet. Each element in this ArrayList is a "row"
* that is in turn represented as an ArrayList of
* com.codestudio.sql.Result objects.
* <p>
* A cursor, implemented as a simple int index, indicates the
* current row by indicating the index of the element in the
* top-level ArrayList of rows.
* <p>
* Yet another ArrayList represents a virtual row, one that is
* based on the current row but is being updated. This structure
* is used to permanently alter the original row when <code>
* updateRow()</code> is invoked. If that method is never invoked,
* then the changes are discarded via the clearUpdates() method,
* which is invoked whenever the cursor moves.
*
* @author PS Neville
*/
public class PoolManResultSet implements ResultSet {
/** The PoolManStatement that generated this ResultSet. */
private Statement statement;
/** The array of Row objects in this ResultSet. */
private ArrayList rowlist;
/** The current cursor position. */
private int pos;
/** The index of the last read column. */
private int lastColIndex;
/** The scrollable type of this ResultSet, TYPE_FORWARD_ONLY by default. */
private int scrollableType = TYPE_FORWARD_ONLY;
/** The concurrency type, CONCUR_READ_ONLY by default. */
private int concurType = CONCUR_READ_ONLY;
/** The fetch direction of this ResultSet, FETCH_UNKNOWN by default. */
private int fetchDirection = FETCH_UNKNOWN;
private int fetchSize = 1;
/** Metadata returned by the underlying driver */
private ResultSetMetaData metaData;
/**
* The list of new Results with which to update the current row
* once updateRow() is called.
*/
private ArrayList updatedResults;
private ArrayList updatedIndexes;
/**
* The insert row implementation.
*/
private ArrayList insertRow;
private boolean onInsertRow = false;
private ArrayList insertedIndexes;
private boolean closed = false;
public PoolManResultSet() {
}
public PoolManResultSet(Statement s, ArrayList rowlist, ResultSetMetaData meta, int rScrollableType, int rConcurType) throws java.sql.SQLException {
// start cursor before first row
this.pos = 0;
this.statement = s;
this.rowlist = rowlist;
this.metaData = PoolManResultSetMetaData.getCopy(meta);
this.scrollableType = rScrollableType;
this.concurType = rConcurType;
this.updatedResults = new ArrayList();
// set up the insert row
composeInsertRow();
this.updatedIndexes = new ArrayList();
this.insertedIndexes = new ArrayList();
}
public void setScrollableType(int n) {
this.scrollableType = n;
}
public void setConcurrencyType(int n) {
this.concurType = n;
}
public ResultSet cloneSet() {
try {
ResultSet set = new PoolManResultSet(this.statement, this.rowlist, this.metaData, this.scrollableType, this.concurType);
return set;
} catch (java.sql.SQLException e) {
return null;
}
}
/* JDBC Methods */
/**
* Move the cursor to a specific row number.
* NOTE: row 1 and pos 1 == rowlist index of 0.
*/
public boolean absolute(int row) throws SQLException {
clearUpdates();
if (row == 0)
throw new SQLException("Invalid Row Number 0");
if (scrollableType == TYPE_FORWARD_ONLY)
throw new SQLException("Invalid method call for this ResultSet type");
// move the cursor backward 'row' steps from the last row
// row -1 == rowlist index of rowlist.size()-1
if (row < 0) {
// could be moved backwards before first
if (Math.abs(row) > rowlist.size()) {
beforeFirst();
return false;
}
else {
pos = rowlist.size() + row + 1;
return true;
}
}
// move the cursor forward 'row' steps from the first row
if (row > rowlist.size()) {
afterLast();
return false;
}
pos = row;
return true;
}
/** Move the cursor one step beyond the final row. */
public void afterLast() throws SQLException {
if (this.scrollableType == TYPE_FORWARD_ONLY) {
throw new SQLException("Illegal Operation when ResultSet is TYPE_FORWARD_ONLY");
}
clearUpdates();
this.pos = rowlist.size() + 1;
}
/** Move the cursor one step before the first row. */
public void beforeFirst() throws SQLException {
if (this.scrollableType == TYPE_FORWARD_ONLY) {
throw new SQLException("Illegal Operation when ResultSet is TYPE_FORWARD_ONLY");
}
clearUpdates();
this.pos = 0;
}
/** Clear the updates made to the current row. */
public void cancelRowUpdates() throws SQLException {
if (this.concurType == CONCUR_READ_ONLY) {
throw new SQLException("Illegal Operation when ResultSet is CONCUR_READ_ONLY");
}
clearUpdates();
}
public void clearWarnings() throws SQLException {
// this method cannot be called if currently on the insert row
assertNotInserting();
}
public void close() throws SQLException {
// this method cannot be called if currently on the insert row
assertNotInserting();
this.updatedResults = null;
this.insertRow = null;
this.rowlist = null;
((PoolManStatement) statement).removeOpenResultSet(this);
closed = true;
}
public boolean isClosed() {
return closed;
}
/** Delete the current row from this ResultSet AND from the database. */
public void deleteRow() throws SQLException {
if (pos > rowlist.size())
throw new SQLException("Illegal Operation, cursor is after the last row");
if (pos < 1)
throw new SQLException("Illegal Operation, cursor is before the first row");
// this method cannot be called if currently on the insert row
assertNotInserting();
ArrayList row;
synchronized (rowlist) {
row = (ArrayList) rowlist.get(pos - 1);
}
// this row isn't necessarily a true row in the db, it
// could be created via a join or somesuch, so look at
// each element and keep track of the tableNames touched.
ArrayList touchedTables = new ArrayList(1);
for (int i = 0; i < row.size(); i++) {
com.codestudio.sql.Result result = (com.codestudio.sql.Result) row.get(i);
// if the row has already been deleted from this tab