/*
* $Id: AnyResultSet.java,v 1.24 2002/09/16 08:05:03 jkl Exp $
*
* Copyright (c) 2002 Njet Communications Ltd. All Rights Reserved.
*
* Use is subject to license terms, as defined in
* Anvil Sofware License, Version 1.1. See LICENSE
* file, or http://njet.org/license-1.1.txt
*/
package anvil.core.sql;
import anvil.core.Any;
import anvil.core.AnyAbstractClass;
import anvil.core.AnyString;
import anvil.core.AnyList;
import anvil.core.Array;
import anvil.script.Context;
import anvil.util.SQLUtil;
import anvil.java.util.BindingEnumeration;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
///
/// @class ResultSet
/// Class reprsenting result of SQL query as a two dimensional table.
/// @reference columnIndex
/// @synopsis "object <b>ResultSet</b>[<i>int columnIndex</i>]" ;
/// Returns the value of column at given index.
/// @synopsis "object <b>ResultSet</b>[<i>string columnName</i>]" ;
/// Returns the value of column with given name.
/// @param columnIndex Index of column, from 0 (inclusive) to
/// <code>getColumnCount()-1</code>.
/// @param columnName Name of column
///
/// @attribute columnName
/// Returns the value of column with given name.
/// @synopsis "object <b>ResultSet</b>.<i>columnName</i>"
/// @param columnName Name of column
///
/**
* class AnyResultSet
*
* @author: Jani Lehtim�ki
*/
public class AnyResultSet extends AnyMetaData
{
/// @const CONCUR_READ_ONLY
/// The concurrency mode for a ResultSet object that may NOT be updated.
public static final Any CONCUR_READ_ONLY = Any.create(ResultSet.CONCUR_READ_ONLY);
/// @const CONCUR_UPDATABLE
/// The concurrency mode for a ResultSet object that may be updated.
public static final Any CONCUR_UPDATABLE = Any.create(ResultSet.CONCUR_UPDATABLE);
/// @const FETCH_FORWARD
/// The rows in a result set will be processed in a forward direction; first-to-last.
public static final Any FETCH_FORWARD = Any.create(ResultSet.FETCH_FORWARD);
/// @const FETCH_REVERSE
/// The rows in a result set will be processed in a reverse direction; last-to-first.
public static final Any FETCH_REVERSE = Any.create(ResultSet.FETCH_REVERSE);
/// @const FETCH_UNKNOWN
/// The order in which rows in a result set will be processed is unknown.
public static final Any FETCH_UNKNOWN = Any.create(ResultSet.FETCH_UNKNOWN);
/// @const FETCH_FORWARD_ONLY
/// The type for a ResultSet object whose cursor may move only forward.
public static final Any FETCH_FORWARD_ONLY = Any.create(ResultSet.TYPE_FORWARD_ONLY);
/// @const FETCH_SCROLL_INSENSITIVE
/// The type for a ResultSet object that is scrollable and generally not sensitive
/// to changes made by others.
public static final Any FETCH_SCROLL_INSENSITIVE = Any.create(ResultSet.TYPE_SCROLL_INSENSITIVE);
/// @const FETCH_SCROLL_SENSITIVE
/// The type for a ResultSet object that is scrollable and generally sensitive
/// to changes made by others.
public static final Any FETCH_SCROLL_SENSITIVE = Any.create(ResultSet.TYPE_SCROLL_SENSITIVE);
private ResultSet _resultSet;
private Statement _statement = null;
public AnyResultSet(ResultSet resultSet)
{
super(null);
_resultSet = resultSet;
}
public AnyResultSet(ResultSet resultSet, Statement statement)
{
super(null);
_resultSet = resultSet;
_statement = statement;
}
public final anvil.script.ClassType classOf()
{
return __class__;
}
public Object toObject()
{
return _resultSet;
}
protected final ResultSetMetaData getMetaData() throws SQLException
{
if (_metadata == null) {
_metadata = _resultSet.getMetaData();
}
return _metadata;
}
protected final int toColumnIndex(Any index) throws SQLException
{
if (index.isInt()) {
return index.toInt() + 1;
} else {
return _resultSet.findColumn(index.toString());
}
}
private final anvil.core.Array getRow(Context context) throws SQLException
{
ResultSetMetaData metadata = getMetaData();
int n = metadata.getColumnCount();
anvil.core.Array row = new anvil.core.Array(n);
Any value;
String name;
for(int i=1; i<=n; i++) {
value = SQLUtil.getField(_resultSet, metadata, i);
name = metadata.getColumnName(i);
if (name != null) {
row.put(new AnyString(name), value);
}
}
return row;
}
private final AnyList getList(Context context) throws SQLException
{
ResultSetMetaData metadata = getMetaData();
int n = metadata.getColumnCount();
Any[] list = new Any[n];
for(int i=1; i<=n; i++) {
list[i-1] = SQLUtil.getField(_resultSet, metadata, i);
}
return new AnyList(list);
}
public Any getAttribute(Context context, String attribute)
{
try {
return SQLUtil.getField(_resultSet, getMetaData(), _resultSet.findColumn(attribute));
} catch (SQLException e) {
throw context.exception(e);
}
}
public Any checkAttribute(Context context, String attribute)
{
return getAttribute(context, attribute);
}
public Any getReference(Context context, Any index)
{
try {
return SQLUtil.getField(_resultSet, getMetaData(), toColumnIndex(index));
} catch (SQLException e) {
throw context.exception(e);
}
}
public Any checkReference(Context context, Any index)
{
return getReference(context, index);
}
public BindingEnumeration enumeration()
{
try {
return new ResultSetEnumeration();
} catch (SQLException e) {
throw Context.getInstance().exception(e);
}
}
/// @method getConcurrency
/// Returns the concurrency mode of this result set.
/// The concurrency used is determined by the statement
/// that created the result set.
/// @synopsis int getConcurrency()
/// @return the concurrency type, CONCUR_READ_ONLY or CONCUR_UPDATABLE
/// @throws SQLError if an error occured
public Any m_getConcurrency(Context context)
{
try {
return Any.create(_resultSet.getConcurrency());
} catch (SQLException e) {
throw context.exception(e);
}
}
/// @method getFetchDirection
/// Returns the fetch direction for this result set.
/// @synopsis int getFetchDirection()
/// @return the current fetch direction
/// @throws SQLError if an error occured
public Any m_getFetchDirection(Context context)
{
try {
return Any.create(_resultSet.getFetchDirection());
} catch (SQLException e) {
throw context.exception(e);
}
}
/// @method setFetchDirection
/// Gives a hint as to the direction in which the rows in this result
/// set will be processed. The initial value is determined by the
/// statement that produced the result set. The fetch direction may
/// be changed at any time.
/// @synopsis ResultSet setFetchDirection(int dir)
/// @throws SQLError if an error occured
public static final Object[] p_setFetchDirection = { null, "direction" };
public Any m_setFetchDirection(Context context, int dir)
{
try {
_resultSet.setFetchDirection(dir);
return this;
} catch (SQLException e) {
throw context.exception(e);
}
}
/// @method previous
/// Moves the cursor to previous row.
/// @synopsis boolean previous()
/// @return true if cursor was moved to next row
/// @throws SQLError if an error occured
public Any m_previous(Context context)
{
try {
return _resultSet.previous() ? TRUE : FALSE;
} catch (SQLException e) {
throw context.exception(e);
}
}
/// @method next
/// Moves the cursor to next row. Initially cursor is located at
/// row before first.
/// @synopsis boolean next()
/// @return true if cursor was moved to next row
/// @throws SQLError if an error occured
public Any m_next(Context context)
{
try {
return _resultSet.next() ? TRUE : FALSE;
} catch (SQLException e) {
throw context.exception(e);
}
}
/// @method beforeFirst
/// Moves the cursor to front of result set, just before the
/// first row.
/// @synopsis ResultSet beforeFirst()
/// @return this
/// @throws SQLError if an error occured
public Any m_beforeFirst(Context context)
{
try {
_resultSet.beforeFirst();
return this;
} catch (SQLException e) {
throw context.exception(e);
}
}
/// @method afterLast
/// Moves the cursor to end of result set, just after the
/// last row.
/// @synopsis ResultSet afterLast()
/// @return this
/// @throws SQLError if an error occured
public Any m_afterLast(Context context)
{
try {
_resultSet.afterLast();
return this;
} catch (SQLException e) {
throw context.exception(e);
}
}
/// @method first
/// Moves the cursor to first row.
/// @synopsis boolean first()
/// @return true if the cursor is on valid row
/// @throws SQLError if an error occured
public Any m_first(Context context)
{
try {
return _resultSet.first() ? TRUE : FALSE;
} catch (SQLException e) {
throw context.exception(e);
}
}
/// @method last
/// Moves the cursor to last row.
/// @synopsis boolean last()
/// @return true if the cursor is on valid row
/// @throws SQLError if an error occured
public Any m_last(Context context)
{
try {
return _resultSet.last() ? TRUE : FALSE;
} catch (SQLException e) {
throw context.exception(e);
}
}
/// @method row
/// Returns the current row number.
/// @synopsis int row()
/// @return current row number
/// @throws SQLError if an error occured
public static final Object[] p_row = { null, "row" };
public Any m_row(Context context)
{
try {
return Any.create(_resultSet.getRow());
} catch (SQLException e) {
throw context.exception(e);
}
}
/// @method absolute
/// Moves cursor to given row number in result set.
///
/// <p>If the row number is positive, the cursor moves to the given row number
/// with respect to the beginning of the result set. The first row is row 1,
/// the second is row 2, and so on. </p>
///
/// <p>If the given row number is negative, the cursor moves to an absolute
/// row position with respect to the end of the result set. For example,
/// calling absolute(-1) positions the cursor on the last row, absolute(-2) indicates the
/// next-to-last row, and so on. </p>
///
/// <p>An attempt to position the cursor beyond the first/last row in the result set
/// leaves the cursor before/after the first/last row, respectively. </p>
///
/// <p>Note: Calling absolute(1) is the same as calling first(). Calling absolute(-1)
/// is the same as calling last().</p>
/// @synopsis boolean absolute(int row)
/// @return true if the cursor is on result set
/// @throws SQLError if an error occured
public static final Object[] p_absolute = { null, "row" };
public Any m_absolute(Context context, int row)
{
try {
return _resultSet.absolute(row) ? TRUE : FALSE;
} catch (SQLException e) {
throw context.exception(e);
}
}
/// @method relative
/// Moves the cursor a relative number of rows, either positive or negative.
/// 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.
///
/// <p>Note: Calling relative(1) is different from calling next() because is
/// makes sense to call next() when there is no current row, for example,
/// when the cursor is positioned before the first row or after the last
/// row of the result set.</p>
///
/// @synopsis boolean relative(int rows)
/// @return true if the cursor is on result set
/// @throws SQLError if an error occured
public static final Object[] p_relative = { null, "row" };
public Any m_relative(Context context, int row)
{
try {
return _resultSet.relative(row) ? TRUE : FALSE;
} catch (SQLException e) {
throw context.exception(e);
}
}
/// @method close
/// Closes this result set, releasing all the resources associated with it.
/// @synopsis ResultSet close()
/// @throws SQLError if an error occured
public Any m_close(Context context)
{
try {
_resultSet.close();
if (_statement != null) {
_statement.close();
}
return this;
} catch (SQLException e) {
throw context.exception(e);
}
}
/// @method get
/// Moves to next row and retrieves the first value from row.
/// Note: this method closes the result set.
/// @synopsis object get()
/// @return Value of first column in next row
/// @throws SQLError if an error occured
public Any m_get(Context context)
{
try {
if (_resultSet.next()) {
return SQLUtil.getField(_resultSet, getMetaData(), 1);
}
return Any.UNDEFINED;
} catch (SQLException e) {
throw context.exception(e);
} finally {
try {
_resultSet.close();
if (_statement != null) {
_statement.close();
}
} catch (Exception e) {
}
}
}
/// @method getRow
/// Gets next row as an array. Columns can be accessed with their
/// respective names. Method <code>next()</code> must
/// be called before this function.
/// @synopsis array getRow()
/// @return next row as array
/// @throws SQLError if an error occured
public Any m_getRow(Context context)
{
try {
return getRow(context);
} catch (SQLException e) {
throw context.exception(e);
}
}
/// @method getList
/// Gets next row as a list. Columns can be accessed with their
/// indices. Method <code>next()</code> must
/// be called before this function.
/// @synopsis array getList()
/// @return next row as list
/// @throws SQLError if an error occured
public Any m_getList(Context context)
{
try {
return getList(context);
} catch (SQLException e) {
throw context.exception(e);
}
}
/// @method getRows
/// Gets next rows as an array of arrays. This method
/// will calls first <code>next()</code>.
/// @synopsis array getRows()
/// @synopsis array getRows(int max)
/// @param max max number of rows
/// @return array of arrays
/// @throws SQLError if an error occured
public static final Object[] p_getRows = { null, "*maxRows", null };
public Any m_getRows(Context context, Any max_)
{
int max = 0;
if (max_ != null) {
max = max_.toInt();
}
try {
if (max <= 0) {
anvil.core.Array rows = new anvil.core.Array();
while(_resultSet.next()) {
rows.append(getRow(context));
}
return rows;
} else {
anvil.core.Array rows = new anvil.core.Array(max);
while(max-->0 && _resultSet.next()) {
rows.append(getRow(context));
}
return rows;
}
} catch (SQLException e) {
throw context.exception(e);
}
}
public class ResultSetEnumeration implements BindingEnumeration
{
private int _index = 1;
private int _size;
public ResultSetEnumeration() throws SQLException
{
_size = getMetaData().getColumnCount();
}
public boolean hasMoreElements()
{
return _index <= _size;
}
public Object nextKey()
{
if (_index <= _size) {
try {
return Any.create(getMetaData().getColumnName(_index));
} catch (SQLException e) {
throw Context.getInstance().exception(e);
}
}
return UNDEFINED;
}
public Object nextElement()
{
if (_index <= _size) {
Context context = Context.getInstance();
try {
return SQLUtil.getField(_resultSet, getMetaData(), _index++);
} catch (SQLException e) {
throw Context.getInstance().exception(e);
}
}
return UNDEFINED;
}
}
public static final anvil.script.compiler.NativeClass __class__ =
new anvil.script.compiler.NativeClass("ResultSet", AnyResultSet.class, AnyMetaData.__class__,
//DOC{{
""+
"\n" +
" @class ResultSet\n" +
" Class reprsenting result of SQL query as a two dimensional table.\n" +
" @reference columnIndex\n" +
" @synopsis \"object <b>ResultSet</b>[<i>int columnIndex</i>]\" ;\n" +
" Returns the value of column at given index.\n" +
" @synopsis \"object <b>ResultSet</b>[<i>string columnName</i>]\" ;\n" +
" Returns the value of column with given name.\n" +
" @param columnIndex Index of column, from 0 (inclusive) to\n" +
" <code>getColumnCount()-1</code>.\n" +
" @param columnName Name of column\n" +
"\n" +
" @attribute columnName\n" +
" Returns the value of column with given name.\n" +
" @synopsis \"object <b>ResultSet</b>.<i>columnName</i>\"\n" +
" @param columnName Name of column\n" +
"\n" +
" @const CONCUR_READ_ONLY\n" +
" The concurrency mode for a ResultSet object that may NOT be updated.\n" +
" @const CONCUR_UPDATABLE\n" +
" The concurrency mode for a ResultSet object that may be updated.\n" +
" @const FETCH_FORWARD\n" +
" The rows in a result set will be processed in a forward direction; first-to-last.\n" +
" @const FETCH_REVERSE\n" +
" The rows in a result set will be processed in a reverse direction; last-to-first.\n" +
" @const FETCH_UNKNOWN\n" +
" The order in which rows in a result set will be processed is unknown.\n" +
" @const FETCH_FORWARD_ONLY\n" +
" The type for a ResultSet object whose cursor may move only forward.\n" +
" @const FETCH_SCROLL_INSENSITIVE\n" +
" The type for a ResultSet object that is scrollable and generally not sensitive \n" +
" to changes made by others.\n" +
" @const FETCH_SCROLL_SENSITIVE\n" +
" The type for a ResultSet object that is scrollable and generally sensitive \n" +
" to changes made by others.\n" +
" @method getConcurrency \n" +
" Returns the concurrency mode of this result set. \n" +
" The concurrency used is determined by the statement\n" +
" that created the result set.\n" +
" @synopsis int getConcurrency()\n" +
" @return the concurrency type, CONCUR_READ_ONLY or CONCUR_UPDATABLE\n" +
" @throws SQLError if an error occured\n" +
" @method getFetchDirection \n" +
" Returns the fetch direction for this result set.\n" +
" @synopsis int getFetchDirection()\n" +
" @return the current fetch direction\n" +
" @throws SQLError if an error occured\n" +
" @method setFetchDirection \n" +
" Gives a hint as to the direction in which the rows in this result \n" +
" set will be processed. The initial value is determined by the \n" +
" statement that produced the result set. The fetch direction may \n" +
" be changed at any time.\n" +
" @synopsis ResultSet setFetchDirection(int dir)\n" +
" @throws SQLError if an error occured\n" +
" @method previous \n" +
" Moves the cursor to previous row. \n" +
" @synopsis boolean previous()\n" +
" @return true if cursor was moved to next row\n" +
" @throws SQLError if an error occured\n" +
" @method next \n" +
" Moves the cursor to next row. Initially cursor is located at\n" +
" row before first.\n" +
" @synopsis boolean next()\n" +
" @return true if cursor was moved to next row\n" +
" @throws SQLError if an error occured\n" +
" @method beforeFirst \n" +
" Moves the cursor to front of result set, just before the\n" +
" first row. \n" +
" @synopsis ResultSet beforeFirst()\n" +
" @return this\n" +
" @throws SQLError if an error occured\n" +
" @method afterLast \n" +
" Moves the cursor to end of result set, just after the\n" +
" last row. \n" +
" @synopsis ResultSet afterLast()\n" +
" @return this\n" +
" @throws SQLError if an error occured\n" +
" @method first \n" +
" Moves the cursor to first row. \n" +
" @synopsis boolean first()\n" +
" @return true if the cursor is on valid row\n" +
" @throws SQLError if an error occured\n" +
" @method last \n" +
" Moves the cursor to last row. \n" +
" @synopsis boolean last()\n" +
" @return true if the cursor is on valid row\n" +
" @throws SQLError if an error occured\n" +
" @method row \n" +
" Returns the current row number.\n" +
" @synopsis int row()\n" +
" @return current row number\n" +
" @throws SQLError if an error occured\n" +
" @method absolute \n" +
" Moves cursor to given row number in result set.\n" +
"\n" +
" <p>If the row number is positive, the cursor moves to the given row number\n" +
" with respect to the beginning of the result set. The first row is row 1,\n" +
" the second is row 2, and so on. </p>\n" +
"\n" +
" <p>If the given row number is negative, the cursor moves to an absolute \n" +
" row position with respect to the end of the result set. For example, \n" +
" calling absolute(-1) positions the cursor on the last row, absolute(-2) indicates the\n" +
" next-to-last row, and so on. </p>\n" +
"\n" +
" <p>An attempt to position the cursor beyond the first/last row in the result set \n" +
" leaves the cursor before/after the first/last row, respectively. </p>\n" +
"\n" +
" <p>Note: Calling absolute(1) is the same as calling first(). Calling absolute(-1) \n" +
" is the same as calling last().</p>\n" +
" @synopsis boolean absolute(int row)\n" +
" @return true if the cursor is on result set\n" +
" @throws SQLError if an error occured\n" +
" @method relative\n" +
" Moves the cursor a relative number of rows, either positive or negative. \n" +
" Attempting to move beyond the first/last row in the result set positions \n" +
" the cursor before/after the the first/last row. Calling relative(0) \n" +
" is valid, but does not change the cursor position.\n" +
"\n" +
" <p>Note: Calling relative(1) is different from calling next() because is \n" +
" makes sense to call next() when there is no current row, for example, \n" +
" when the cursor is positioned before the first row or after the last \n" +
" row of the result set.</p>\n" +
"\n" +
" @synopsis boolean relative(int rows)\n" +
" @return true if the cursor is on result set\n" +
" @throws SQLError if an error occured\n" +
" @method close \n" +
" Closes this result set, releasing all the resources associated with it. \n" +
" @synopsis ResultSet close()\n" +
" @throws SQLError if an error occured\n" +
" @method get \n" +
" Moves to next row and retrieves the first value from row. \n" +
" Note: this method closes the result set.\n" +
" @synopsis object get()\n" +
" @return Value of first column in next row\n" +
" @throws SQLError if an error occured\n" +
" @method getRow \n" +
" Gets next row as an array. Columns can be accessed with their\n" +
" respective names. Method <code>next()</code> must\n" +
" be called before this function.\n" +
" @synopsis array getRow()\n" +
" @return next row as array\n" +
" @throws SQLError if an error occured\n" +
" @method getList \n" +
" Gets next row as a list. Columns can be accessed with their\n" +
" indices. Method <code>next()</code> must\n" +
" be called before this function.\n" +
" @synopsis array getList()\n" +
" @return next row as list\n" +
" @throws SQLError if an error occured\n" +
" @method getRows \n" +
" Gets next rows as an array of arrays. This method\n" +
" will calls first <code>next()</code>.\n" +
" @synopsis array getRows()\n" +
" @synopsis array getRows(int max)\n" +
" @param max max number of rows\n" +
" @return array of arrays\n" +
" @throws SQLError if an error occured\n"
//}}DOC
);
static {
SQLModule.class.getName();
}
}