blob: 89520e31b554044293b05ce8c1d8941ed96d09a3 [file] [log] [blame]
/*
* Copyright 1999-2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cocoon.components.flow.javascript;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.JavaScriptException;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.Undefined;
import org.mozilla.javascript.Wrapper;
/**
* Wraps a JDBC connection and provides an API similar to JSTL
* A ScriptableConnection provides two methods:
*
* <UL>
* <LI>query([String] stmt, [Array] parameters, [Number] startRow, [Number] maxRows, [Function] fun)</LI>
* <LI>update([String] stmt, [Array] parameters)</LI>
* </UL>
* If the <code>fun</code> argument is provided to <code>query</code> it
* will be called for each row returned (the row object will be passed as its
* argument). For example:
* <pre>
* var db = Database.getConnection(...);
* var queryVal = ...;
* var startRow = 0;
* var maxRows = 100;
*
* db.query("select * from table where column = ?",
* [queryVal],
* startRow,
* maxRows,
* function(row) {
* print("column = " + row.column);
* });
*
* </pre>
* If <code>fun</code> is undefined, an object containing the following
* properties will be returned instead:
* <UL>
* <LI>[Array] rows - an array of row objects</LI>
* <LI>[Array] rowsByIndex - An array with an array per row of column values</LI>
* <LI>[Array] columnNames - An array of column names</LI>
* <LI>[Number] rowCount - Number of rows returned</LI>
* <LI>[Boolean] limitedByMaxRows - true if not all rows are included due to matching a maximum value </LI>
* </UL>
*
* A ScriptableConnection is also a wrapper around a real JDBC Connection and thus
* provides all of methods of Connection as well
*
* @version CVS $Id: ScriptableConnection.java,v 1.6 2004/03/05 13:02:03 bdelacretaz Exp $
*/
public class ScriptableConnection extends ScriptableObject {
Connection connection;
Scriptable wrapper;
static Object wrap(final Scriptable wrapper,
final Scriptable wrapped,
Object obj) {
if (obj instanceof Function) {
return wrap(wrapper, wrapped, (Function)obj);
}
return obj;
}
static Function wrap(final Scriptable wrapper,
final Scriptable wrapped,
final Function fun) {
return new Function() {
public Object call(Context cx, Scriptable scope, Scriptable thisObj,
Object[] args) throws JavaScriptException {
if (thisObj == wrapper) {
thisObj = wrapped;
}
return fun.call(cx, scope, thisObj, args);
}
public Scriptable construct(Context cx, Scriptable scope,
Object[] args)
throws JavaScriptException {
return fun.construct(cx, scope, args);
}
public String getClassName() {
return fun.getClassName();
}
public Object get(String name, Scriptable start) {
return fun.get(name, fun);
}
public Object get(int index, Scriptable start) {
return fun.get(index, fun);
}
public boolean has(String name, Scriptable start) {
return fun.has(name, start);
}
public boolean has(int index, Scriptable start) {
return fun.has(index, start);
}
public void put(String name, Scriptable start, Object value) {
fun.put(name, start, value);
}
public void put(int index, Scriptable start, Object value) {
fun.put(index, start, value);
}
public void delete(String name) {
fun.delete(name);
}
public void delete(int index) {
fun.delete(index);
}
public Scriptable getPrototype() {
return fun.getPrototype();
}
public void setPrototype(Scriptable prototype) {
}
public Scriptable getParentScope() {
return fun.getParentScope();
}
public void setParentScope(Scriptable parent) {
}
public Object[] getIds() {
return fun.getIds();
}
public Object getDefaultValue(Class hint) {
return fun.getDefaultValue(hint);
}
public boolean hasInstance(Scriptable instance) {
return fun.hasInstance(instance);
}
};
}
public String getClassName() {
return "Database";
}
public ScriptableConnection() {
}
public static void finishInit(Scriptable proto) {
}
public static Scriptable jsConstructor(Context cx, Object[] args,
Function ctorObj,
boolean inNewExpr)
throws Exception {
Connection conn = null;
if (args.length > 0) {
Object arg = args[0];
if (arg instanceof Wrapper) {
arg = ((Wrapper)arg).unwrap();
}
if (arg instanceof Connection) {
conn = (Connection)arg;
}
}
if (conn == null) {
throw new JavaScriptException("expected an instance of java.sql.Connection");
}
ScriptableConnection result = new ScriptableConnection(ctorObj, conn);
return result;
}
public ScriptableConnection(Scriptable parent, Connection conn) {
this.connection = conn;
this.wrapper = Context.toObject(connection, parent);
}
public Object jsFunction_query(String sql, Object params,
int startRow, int maxRows,
Object funObj)
throws JavaScriptException {
try {
PreparedStatement stmt = connection.prepareStatement(sql);
Scriptable array = (Scriptable)params;
if (array != Undefined.instance) {
int len = (int)
Context.toNumber(ScriptableObject.getProperty(array, "length"));
for (int i = 0; i < len; i++) {
Object val = ScriptableObject.getProperty(array, i);
if (val instanceof Wrapper) {
val = ((Wrapper)val).unwrap();
}
if (val == Scriptable.NOT_FOUND) {
val = null;
}
stmt.setObject(i + 1, val);
}
}
ResultSet rs = stmt.executeQuery();
if (maxRows == 0) {
maxRows = -1;
}
if (funObj instanceof Function) {
Context cx = Context.getCurrentContext();
Function fun = (Function)funObj;
ResultSetMetaData rsmd = rs.getMetaData();
int noOfColumns = rsmd.getColumnCount();
// Throw away all rows upto startRow
for (int i = 0; i < startRow; i++) {
rs.next();
}
// Process the remaining rows upto maxRows
int processedRows = 0;
Scriptable scope = getTopLevelScope(this);
Scriptable proto = getObjectPrototype(scope);
Object[] args;
while (rs.next()) {
if ((maxRows != -1) && (processedRows == maxRows)) {
break;
}
Scriptable row = new ScriptableResult.Row();
row.setParentScope(scope);
row.setPrototype(proto);
for (int i = 1; i <= noOfColumns; i++) {
Object value = rs.getObject(i);
if (rs.wasNull()) {
value = null;
}
row.put(rsmd.getColumnName(i), row, value);
}
args = new Object[1];
args[0] = row;
fun.call(cx, scope, scope, args);
}
return Undefined.instance;
} else {
ScriptableResult s = new ScriptableResult(this, rs,
startRow, maxRows);
s.setParentScope(getTopLevelScope(this));
s.setPrototype(getClassPrototype(this, s.getClassName()));
return s;
}
} catch (JavaScriptException e) {
throw e;
} catch (Exception e) {
throw new JavaScriptException(e);
}
}
public int jsFunction_update(String sql, Object params)
throws JavaScriptException {
try {
PreparedStatement stmt = connection.prepareStatement(sql);
Scriptable array = (Scriptable)params;
if (array != Undefined.instance) {
int len = (int)
Context.toNumber(ScriptableObject.getProperty(array, "length"));
for (int i = 0; i < len; i++) {
Object val = ScriptableObject.getProperty(array, i);
if (val instanceof Wrapper) {
val = ((Wrapper)val).unwrap();
}
if (val == Scriptable.NOT_FOUND) {
val = null;
}
stmt.setObject(i + 1, val);
}
}
stmt.execute();
return stmt.getUpdateCount();
} catch (Exception e) {
throw new JavaScriptException(e);
}
}
public Object get(String name, Scriptable start) {
if (wrapper != null) {
Object result = wrapper.get(name, wrapper);
if (result != NOT_FOUND) {
return wrap(this, wrapper, result);
}
}
return super.get(name, start);
}
public boolean has(String name, Scriptable start) {
if (wrapper != null) {
if (wrapper.has(name, wrapper)) {
return true;
}
}
return super.has(name, start);
}
public boolean has(int index, Scriptable start) {
if (wrapper != null) {
if (wrapper.has(index, start)) {
return true;
}
}
return super.has(index, start);
}
public Object get(int index, Scriptable start) {
if (wrapper != null) {
Object result = wrapper.get(index, start);
if (result != NOT_FOUND) {
return wrap(this, wrapper, result);
}
}
return super.get(index, start);
}
public void put(String name, Scriptable start, Object value) {
if (wrapper != null) {
wrapper.put(name, wrapper, value);
return;
}
super.put(name, start, value);
}
}