devdaily home | apple | java | perl | unix | directory | blog

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/* Copyrights and Licenses
 *
 * This product includes Hypersonic SQL.
 * Originally developed by Thomas Mueller and the Hypersonic SQL Group. 
 *
 * Copyright (c) 1995-2000 by the Hypersonic SQL Group. All rights reserved. 
 * Redistribution and use in source and binary forms, with or without modification, are permitted
 * provided that the following conditions are met: 
 *     -  Redistributions of source code must retain the above copyright notice, this list of conditions
 *         and the following disclaimer. 
 *     -  Redistributions in binary form must reproduce the above copyright notice, this list of
 *         conditions and the following disclaimer in the documentation and/or other materials
 *         provided with the distribution. 
 *     -  All advertising materials mentioning features or use of this software must display the
 *        following acknowledgment: "This product includes Hypersonic SQL." 
 *     -  Products derived from this software may not be called "Hypersonic SQL" nor may
 *        "Hypersonic SQL" appear in their names without prior written permission of the
 *         Hypersonic SQL Group. 
 *     -  Redistributions of any form whatsoever must retain the following acknowledgment: "This
 *          product includes Hypersonic SQL." 
 * This software is provided "as is" and any expressed or implied warranties, including, but
 * not limited to, the implied warranties of merchantability and fitness for a particular purpose are
 * disclaimed. In no event shall the Hypersonic SQL Group or its contributors be liable for any
 * direct, indirect, incidental, special, exemplary, or consequential damages (including, but
 * not limited to, procurement of substitute goods or services; loss of use, data, or profits;
 * or business interruption). However caused any on any theory of liability, whether in contract,
 * strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this
 * software, even if advised of the possibility of such damage. 
 * This software consists of voluntary contributions made by many individuals on behalf of the
 * Hypersonic SQL Group.
 *
 *
 * For work added by the HSQL Development Group:
 *
 * Copyright (c) 2001-2004, The HSQL Development Group
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer, including earlier
 * license statements (above) and comply with all above license conditions.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution, including earlier
 * license statements (above) and comply with all above license conditions.
 *
 * Neither the name of the HSQL Development Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG, 
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


package org.hsqldb;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import org.hsqldb.lib.HashMap;
import org.hsqldb.lib.HsqlArrayList;
import org.hsqldb.lib.StringConverter;

// fredt@users 20020912 - patch 1.7.1 - shortcut treatment of identity() call
// fredt@users 20020912 - patch 1.7.1 - cache java.lang.reflect.Method objects
// fredt@users 20021013 - patch 1.7.1 - ignore non-static methods
// boucherb@users 20030201 - patch 1.7.2 - direct calls for org.hsqldb.Library
// fredt@users 20030621 - patch 1.7.2 - shortcut treatment of session calls
// boucherb@users 200404xx - doc 1.7.2 - updates toward 1.7.2 final

/**
 * Provides services to evaluate SQL function and stored procedure calls,
 * by invoking Java methods.
 *
 * @version 1.7.2
 */
class Function {

    private String         sFunction;
    Method                 mMethod;
    private Class          cReturnClass;
    private Class[]        aArgClasses;
    private int            iReturnType;
    private int            iArgCount;
    private int            iSqlArgCount;
    private int            iSqlArgStart;
    private int            iArgType[];
    private boolean        bArgNullable[];
    Expression[]           eArg;
    private boolean        bConnection;
    private static HashMap methodCache = new HashMap();
    private int            fID;
    String                 name;        // name used to call function
    boolean                isSimple;    //CURRENT_TIME, NOW etc.
    boolean                hasAggregate;

    /**
     * Constructs a new Function object with the given function call name
     * and using the specified Session context. 

* * The call name is the fully qualified name of a static Java method, in * the form "package.class.method." This implies that Java * methods with the same fully qualified name but different signatures * cannot be used properly as HSQLDB SQL functions or stored procedures. * For instance, it is impossible to call both System.getProperty(String) * and System.getProperty(String,String) under this arrangement, because * the HSQLDB Function object is unable to differentiate between the two; * it simply chooses the first method matching the FQN in the array of * methods obtained from calling getMethods() on an instance of the * Class indicated in the FQN, hiding all other methods with the same * FQN.

* * The function FQN must match at least one static Java method FQN in the * specified class or construction cannot procede and an HsqlException is * thrown.

* * The isSimple parameter is true when certain SQL standard functions * that are used without brackets are invokded. * * @param name this Function object's call name * @param fqn the fully qualified name of a Java method * @param isSimple if true, used to evalate CURRENT_TIME, NOW etc. * evaluate * @throws HsqlException if the specified function FQN corresponds to no * Java method */ Function(String name, String fqn, boolean isSimple) throws HsqlException { this.name = name; this.isSimple = isSimple; // cSession = session; sFunction = fqn; fID = Library.functionID(fqn); int i = fqn.lastIndexOf('.'); Trace.check(i != -1, Trace.UNEXPECTED_TOKEN, fqn); String classname = fqn.substring(0, i); mMethod = (Method) methodCache.get(fqn); if (mMethod == null) { String methodname = fqn.substring(i + 1); Class classinstance = null; try { classinstance = Class.forName(classname); } catch (Exception e) { throw Trace.error(Trace.FUNCTION_NOT_FOUND, Trace.Message_Pair, new Object[] { classname, e }); } // public only, but includes those inherited from // superclasses and superinterfaces. List is unordered. Method methods[] = classinstance.getMethods(); for (i = 0; i < methods.length; i++) { Method m = methods[i]; if (m.getName().equals(methodname) && Modifier.isStatic(m.getModifiers())) { mMethod = m; break; } } Trace.check(mMethod != null, Trace.UNKNOWN_FUNCTION, methodname); methodCache.put(fqn, mMethod); } cReturnClass = mMethod.getReturnType(); if (cReturnClass.equals(org.hsqldb.Result.class)) { // For now, we can write stored procedures whose // descriptor explicitly specifies the above return type. // Later, this will be modified or replaced to provide proper // support for jdbcCallableStatement OUT mode return parameter, // multiple results (Result.MULTI etc.) iReturnType = Types.OTHER; } else { // Now we support the following construction-time return type // Classes, as specified by the method descriptor: // // 1.) any primitive or primitive wrapper type, except Byte(.TYPE), // Short(.TYPE) and Float(.TYPE) (TBD; narrow if no truncation) // // 2.) any primitive array type // // 3.) any non-primitive array whose base component implements // java.io.Serializable // // 4.) any class implementing java.io.Serializable, except those // described in 1.) as currently unsupported // // 5.) java.lang.Object // // For java.lang.Object, checking is deferred from the construction // stage to the evaluation stage. In general, for the evaluation // to succeed, the runtime class of the retrieved Object must be // // 1.) any primitive or primitive wrapper type, except Byte(.TYPE), // Short(.TYPE) and Float(.TYPE) (TBD; narrow if no trunction) // // 2.) any primitive array type // 3.) any non-primitive array whose base component implements // java.io.Serializable // // 4.) any class implementing java.io.Serializable, except those // described in 1.) as currently unsupported // // Additionally, it is possible for the evaluation to succeed under // an SQL CALL if the runtime Class of the returned Object is not // from the list above but is from the list below: // // 1.) is org.hsqldb.Result // 2.) is org.hsqldb.jdbc.jdbcResultSet // // In these special cases, the statement executor notices the // types and presents the client with a view the underlying result // rather than with a view of the object as an opaque scalar value // iReturnType = Types.getParameterTypeNr(cReturnClass); } aArgClasses = mMethod.getParameterTypes(); iArgCount = aArgClasses.length; iArgType = new int[iArgCount]; bArgNullable = new boolean[iArgCount]; for (i = 0; i < aArgClasses.length; i++) { Class a = aArgClasses[i]; String type = a.getName(); if ((i == 0) && a.equals(java.sql.Connection.class)) { // TODO: provide jdbc:default:connection url functionality // // only the first parameter can be a Connection bConnection = true; } else { // see discussion above for iReturnType iArgType[i] = Types.getParameterTypeNr(a); bArgNullable[i] = !a.isPrimitive(); } } iSqlArgCount = iArgCount; if (bConnection) { iSqlArgCount--; iSqlArgStart = 1; } else { iSqlArgStart = 0; } eArg = new Expression[iArgCount]; } /** * Evaluates and returns this Function in the context of the session.

*/ Object getValue(Session session) throws HsqlException { switch (fID) { case Library.curtime : return session.getCurrentTime(); case Library.curdate : return session.getCurrentDate(); case Library.database : return session.getDatabase().getPath(); case Library.getAutoCommit : return session.isAutoCommit() ? Boolean.TRUE : Boolean.FALSE; case Library.identity : return session.getLastIdentity(); case Library.isReadOnlyDatabase : return session.getDatabase().databaseReadOnly ? Boolean.TRUE : Boolean.FALSE; case Library.isReadOnlyConnection : return session.isReadOnly() ? Boolean.TRUE : Boolean.FALSE; case Library.isReadOnlyDatabaseFiles : return session.getDatabase().filesReadOnly ? Boolean.TRUE : Boolean.FALSE; case Library.now : return session.getCurrentTimestamp(); case Library.user : return session.getUser().getName(); } Object[] oArg = getArguments(session); if (oArg == null) { return null; } return getValue(session, oArg); } /** * Evaluates the Function with the given arguments in the session context. */ Object getValue(Session session, Object[] arguments) throws HsqlException { if (bConnection) { arguments[0] = session.getInternalConnection(); } try { Object ret = (fID >= 0) ? Library.invoke(fID, arguments) : mMethod.invoke(null, arguments); return Column.convertObject(ret, iReturnType); } catch (InvocationTargetException e1) { // thrown by user functions Throwable t = e1.getTargetException(); String s = sFunction + " : " + t.toString(); throw Trace.error(Trace.FUNCTION_CALL_ERROR, s); } catch (IllegalAccessException e3) { // never thrown in this method throw Trace.error(Trace.FUNCTION_CALL_ERROR); } // Library function throw HsqlException } private Object[] getArguments(Session session) throws HsqlException { int i = bConnection ? 1 : 0; Object[] oArg = new Object[iArgCount]; for (; i < iArgCount; i++) { Expression e = eArg[i]; Object o = null; if (e != null) { // no argument: null o = e.getValue(session, iArgType[i]); } if ((o == null) &&!bArgNullable[i]) { // null argument for primitive datatype: don't call return null; } if (o instanceof JavaObject) { o = ((JavaObject) o).getObject(); } else if (o instanceof Binary) { o = ((Binary) o).getBytes(); } oArg[i] = o; } return oArg; } /** * returns null if any non-nullable element of values is null */ private Object[] getNotNull(Object[] values) throws HsqlException { int i = bConnection ? 1 : 0; for (; i < iArgCount; i++) { Object o = values[i]; if (o == null &&!bArgNullable[i]) { // null argument for primitive datatype: don't call return null; } } return values; } Object getAggregatedValue(Session session, Object currValue) throws HsqlException { Object[] valueArray = (Object[]) currValue; if (valueArray == null) { valueArray = new Object[iArgCount]; } for (int i = 0; i < iArgCount; i++) { Expression e = eArg[i]; if (eArg[i] != null) { valueArray[i] = eArg[i].isAggregate() ? e.getAggregatedValue(session, valueArray[i]) : e.getValue(session); } } valueArray = getNotNull(valueArray); if (valueArray == null) { return null; } return getValue(session, valueArray); } Object updateAggregatingValue(Session session, Object currValue) throws HsqlException { Object[] valueArray = (Object[]) currValue; if (valueArray == null) { valueArray = new Object[iArgCount]; } for (int i = 0; i < iArgCount; i++) { Expression e = eArg[i]; if (eArg[i] != null) { valueArray[i] = e.updateAggregatingValue(session, valueArray[i]); } } return valueArray; } /** * Returns the number of parameters that must be supplied to evaluate * this Function object from SQL.

* * This value may be different than the number of parameters of the * underlying Java method. This is because HSQLDB automatically detects * if the first parameter is of type java.sql.Connection, and supplies a * live Connection object constructed from the evaluating session context * if so. */ int getArgCount() { return iSqlArgCount; } /** * Remnoves the Table filters from Expression parameters to this Function. * * @throws HsqlException if there is a problem resolving a parameter * against the specified TableFilter */ /* void removeFilters() throws HsqlException { Expression e; for (int i = iSqlArgStart; i < iArgCount; i++) { e = eArg[i]; if (e != null) { e.removeFilters(); } } } */ /** * Checks the Expresion parameters to this Function object against the * set of TableFilter. */ void checkTables(HsqlArrayList fa) throws HsqlException { Expression e; for (int i = iSqlArgStart; i < iArgCount; i++) { e = eArg[i]; if (e != null) { e.checkTables(fa); } } } /** * Resolves the Expression parameters to this Function object against the * specified TableFilter. */ void resolveTables(TableFilter f) throws HsqlException { Expression e; for (int i = iSqlArgStart; i < iArgCount; i++) { e = eArg[i]; if (e != null) { e.resolveTables(f); } } } /** * Resolves the type of this expression and performs certain * transformations and optimisations of the expression tree. */ void resolveType() throws HsqlException { Expression e; for (int i = iSqlArgStart; i < iArgCount; i++) { e = eArg[i]; if (e != null) { if (e.isParam()) { e.setDataType(iArgType[i]); e.nullability = getArgNullability(i); e.valueClassName = getArgClass(i).getName(); } else { e.resolveTypes(); } } } } /** * Checks each of this object's arguments for resolution, throwing an * HsqlException if any arguments have not yet been resolved.

* The check boolean argument is passed on to further check calls.

*/ boolean checkResolved(boolean check) throws HsqlException { boolean result = true; for (int i = iSqlArgStart; i < iArgCount; i++) { if (eArg[i] != null) { result = result && eArg[i].checkResolved(check); } } return result; } /** * Returns the java.sql.Types type of the argument at the specified * offset in this Function object's paramter list.

*/ int getArgType(int i) { return iArgType[i]; } /** * Returns the java.sql.Types type of this Function * object's return type.

*/ int getReturnType() { return iReturnType; } /** * Binds the specified expression to the specified position in this * Function object's parameter list.

*/ void setArgument(int i, Expression e) { if (bConnection) { i++; } eArg[i] = e; hasAggregate = hasAggregate || (e != null && e.isAggregate()); } /** * Returns a DDL representation of this object.

*/ String getDLL() throws HsqlException { StringBuffer sb = new StringBuffer(); // get the name as used by the CHECK statement String ddlName = name; if (isSimple) { return name; } else if (Token.T_TRIM.equals(name)) { // special case for TRIM sb.append(name).append('('); boolean leading = eArg[2].test(null); boolean trailing = eArg[3].test(null); if (leading && trailing) { sb.append(Token.T_BOTH); } else { sb.append(leading ? Token.T_LEADING : Token.T_TRAILING); } // to do change to string sb.append(' '); String charval = (String) eArg[1].getValue(null); sb.append(Column.createSQLString(charval)).append(' '); sb.append(Token.T_FROM).append(' '); sb.append(eArg[0].getDDL()).append(')'); return sb.toString(); } if (sFunction.equals(name)) { ddlName = StringConverter.toQuotedString(name, '"', true); } sb.append(ddlName).append('('); for (int i = iSqlArgStart; i < eArg.length; i++) { sb.append(eArg[i].getDDL()); if (i < eArg.length - 1) { sb.append(','); } } sb.append(')'); return sb.toString(); } /** * Returns a String representation of this object.

*/ public String toString() { StringBuffer sb = new StringBuffer(); sb.append(super.toString()).append("=[\n"); sb.append(sFunction).append("("); for (int i = iSqlArgStart; i < eArg.length; i++) { sb.append("[").append(eArg[i]).append("]"); } sb.append(") returns ").append(Types.getTypeString(getReturnType())); sb.append("]\n"); return sb.toString(); } /** * Returns the Java Class of the object returned by getValue().

*/ Class getReturnClass() { return cReturnClass; } /** * Returns the Java Class of the i'th argument.

*/ Class getArgClass(int i) { return aArgClasses[i]; } /** * Returns the SQL nullability code of the i'th argument.

*/ int getArgNullability(int i) { return bArgNullable[i] ? Expression.NULLABLE : Expression.NO_NULLS; } }




Copyright 1998-2008 Alvin Alexander
All Rights Reserved.
 
devdaily.com is based in louisville, kentucky, and this web site is hosted by godaddy.com