/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999-2004 Bull S.A.
 * Contact: jonas-team@ow2.org
 *
 * 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.1 of the License, or 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.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */


package org.ow2.jonas.generators.genic;


import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import java.util.Vector;

import org.ow2.jonas.deployment.common.DeploymentDescException;
import org.ow2.jonas.deployment.ejb.BeanDesc;
import org.ow2.jonas.deployment.ejb.EntityJdbcCmp1Desc;
import org.ow2.jonas.deployment.ejb.EntityJdbcCmp2Desc;
import org.ow2.jonas.deployment.ejb.FieldJdbcDesc;
import org.ow2.jonas.deployment.ejb.MethodCmp2Desc;
import org.ow2.jonas.deployment.ejb.MethodDesc;
import org.ow2.jonas.deployment.ejb.MethodJdbcCmp1Desc;
import org.ow2.jonas.lib.ejb21.JavaType;
import org.ow2.jonas.lib.ejb21.sql.EjbqlQueryTreeHolder;
import org.ow2.jonas.lib.util.BeanNaming;




import org.objectweb.jorm.metainfo.api.Manager;
import org.objectweb.jorm.type.api.PType;
import org.objectweb.util.monolog.api.BasicLevel;


/**
 * This class is the "Velocity context" for a interface method or a bean method used in the Velocity Templates.
 * @author Helene Joanin (Bull) : Initial developer
 * @author Santiago Gala (sgala@hisitech.com) - 00/09/14 - Parameters can be followed by a number in the WHERE clause
 * @author Joe Gittings has proposed to code method signature for security
 *          in order to avoid same signature for inherited methods.
 */

public class VcMethod {

    /**
     * home.create method
     */
    static final byte METHOD_CREATE = 0;
    /**
     * bean.ebjCreate method
     */
    static final byte METHOD_EJB_CREATE = 1;
    /**
     * bean.ejbPostCreate method
     */
    static final byte METHOD_EJB_POST_CREATE = 2;
    /**
     * EJBObject.remove() method
     */
    static final byte METHOD_REMOVE_THIS = 10;
    /**
     * home.remove(primarykey) method
     */
    static final byte METHOD_REMOVE_PK = 11;
    /**
     * home.remove(handle) method
     */
    static final byte METHOD_REMOVE_HANDLE = 12;
    /**
     * bean.ejbRemove() method
     */
    static final byte METHOD_EJB_REMOVE = 13;
    /**
     * home.findByPrimaryKey(pk) method
     */
    static final byte METHOD_FINDER_BY_PK = 20;
    /**
     * home.findXxx() method
     */
    static final byte METHOD_FINDER_ALL = 21;
    /**
     * home.findXxx() method that returns a simple object
     */
    static final byte METHOD_FINDER_SIMPLE = 22;
    /**
     * home.findXxx() method that returns an Enumeration
     */
    static final byte METHOD_FINDER_ENUM = 23;
    /**
     * home.findXxx() method that returns a Collection
     */
    static final byte METHOD_FINDER_COL = 24;
    /**
     * bean.ejbSelectXxx() method that returns a simple object
     */
    static final byte METHOD_EJB_SELECT_SIMPLE = 30;
    /**
     * bean.ejbSelectXxx() method that returns a Collection
     */
    static final byte METHOD_EJB_SELECT_COL = 31;
    /**
     * bean.ejbSelectXxx() method that returns a Set
     */
    static final byte METHOD_EJB_SELECT_SET = 32;
    /**
     * bean.ejbLoad() method
     */
    static final byte METHOD_EJB_LOAD = 40;
    /**
     * bean.ejbStore() method
     */
    static final byte METHOD_EJB_STORE = 41;
    /**
     * bean.setEntityContext() method
     */
    static final byte METHOD_EJB_SET_ENTITY_CONTEXT = 42;
    /**
     * bean.ejbActivate() method
     */
    static final byte METHOD_EJB_ACTIVATE = 43;

    /**
     * kind of the method ie home.create() or bean.ejbCreate, or ...
     */
    private byte mKind = -1;

    /**
     * name of the method
     */
    private String  mName = null;

    /**
     * name of the method with the firt letter capitalized
     */
    private String  mCapName = null;

    /**
     * string representation of the formal parameters of the method (for ex: "int p1, String p2");
     */
    private StringBuffer mFormalParameters = null;

    /**
     * string representation of the actual parameters of the method (for ex: "p1, p2")
     */
    private StringBuffer mActualParameters = null;

    /**
     * Array of objects of the parameters
     */
    private StringBuffer mArrayObjectParameters = null;

    /**
     * string representation of the exception list
     */
    private StringBuffer mExceptionList = null;

    /**
     * string representation of the security signature method
     */
    private String mSecuritySignature;

    /**
     * transactional attribute of the method
     */
    private int mTxAttribute = MethodDesc.TX_NULL;

    /**
     * name of the return type of the method
     */
    private String mReturnType;

    /**
     * default return value
     */
    private String mDefaultValue;

    /**
     * default wrapper type for the return type
     */
    private String mWrapperType;

    /**
     * java.lang.reflect.Method, just for explicit error message
     */
    private Method mMethod = null;

    /**
     * index of the method. Each method is identify with an unuique index.
     */
    private int methodIndex = -1;

    /**
     * BeanDesc of the associated bean to the method
     */
    private BeanDesc beanDesc = null;

    private Manager manager = null;
    
    /*
    * For CMP1 only
    */

    /**
     * string representation of the SQL statement associated to the method
     */
    private String mSqlStmt = null;

    /**
     * VcParamWhere list of the finder method
     */
    private ArrayList  mParamWhereList = null;

    /*
     * For CMP2 only
     */

    /**
     * true is this is a finder/ejbSelect method that return a remote bean
     * (Only used in CMP2 for finder and ejbSelect methods)
     */

    private boolean isEjbqlReturnRemoteBean = false;
    /**
     * true is this is a finder/ejbSelect method that return a local bean
     * (Only used in CMP2 for finder and ejbSelect methods)
     */

    private boolean isEjbqlReturnLocalBean = false;
    /**
     * Medor Field representation of the result of the finder/ejbSelect method
     * (Only used in CMP2 for finder and ejbSelect methods)
     */

    private org.objectweb.medor.api.Field resFieldOfEjbql = null;
    /**
     * VcParam list of the finder/ejbSelect method
     * (Only used in CMP2 for finder and ejbSelect methods)
     */
    private ArrayList  mParamList = null;

    /**
     * Class list of the parameters of the method
     */
    private Class[] parameterTypes = null;
    /**
     * true if the method throws javax.ejb.CreateException
     */
    private boolean canThrowCreate = false;

    /**
     * VcMethod constructor
     * @param method java.lang.reflect.Method method description
     * @param methodDesc method description
     * @param dd bean description of the associated bean
     */
    VcMethod(Method method, MethodDesc methodDesc, BeanDesc dd, Manager mgr) {

        if (methodDesc != null) {
            methodIndex = methodDesc.getIndex();
        }
        beanDesc = dd;
        manager = mgr;
        
        /*
         * The 'method' may be
         * - a home interface's method
         * - a local home interface's method
         * - a remote interface's method
         * - a local interface's method
         * - a bean's method as
         * - a beans's ejbSelect() method,
         *   setEntityContext(), ejbActivate(), ejbCreate(), ejbLoad(), ejbStore(), ejbRemove().
         * - a beans's ejbSelect() method,
         * In the last case, the 'methodDesc' is null in case of
         * setEntityContext(), ejbActivate(), ejbLoad() and ejbStore().
         */

        Class declClass = method.getDeclaringClass();
        Class [] params = method.getParameterTypes();
        parameterTypes = params;
        Class [] exceptions = method.getExceptionTypes();
        mMethod = method;

        // Name and CapName
        mName = method.getName();
        mCapName = BeanNaming.firstToUpperCase(mName);

        // FormalParameters and ActualParameters
        mFormalParameters = new StringBuffer();
        mActualParameters = new StringBuffer();
        mArrayObjectParameters = new StringBuffer();
        String var = null;
        for (int p = 1; p <= params.length; p++) {
            var =  "p" + p;
            mFormalParameters.append(JavaType.getName(params[p - 1]) + " " + var);
            mActualParameters.append(var);
            mArrayObjectParameters.append(JavaType.toStringObject(var, params[p - 1]));
            if (p < params.length) {
                mFormalParameters.append(", ");
                mActualParameters.append(", ");
                mArrayObjectParameters.append(", ");
            }
        }

        // ExceptionList
        mExceptionList = new StringBuffer();
        for (int e = 0; e < exceptions.length; e++) {
            Class ecl = exceptions[e];
            if (javax.ejb.CreateException.class.isAssignableFrom(ecl)) {
                canThrowCreate = true;
            }
            mExceptionList.append(JavaType.getName(ecl));
            if (e < exceptions.length - 1) {
                mExceptionList.append(", ");
            }
        }

        // SecuritySignature
        // No methodDesc for bean's methods as ejbLoad(), ejbStore(), ...
        if (methodDesc != null) {
            mSecuritySignature = "";
            if (methodDesc.getRoleName().length > 0 || methodDesc.isExcluded()) {
                mSecuritySignature = BeanNaming.getSignature(dd.getEjbName(), method);
            }
        }

        // TxAttribute
        // No methodDesc for bean's methods as ejbLoad(), ejbStore(), ...
        if (methodDesc != null) {
            mTxAttribute = methodDesc.getTxAttribute();
        }

        // ReturnType
        mReturnType = JavaType.getName(method.getReturnType());
        if (! mReturnType.equals("void")) {
            mDefaultValue = JavaType.getDefaultValue(method.getReturnType());
            mWrapperType = JavaType.getWrapperType(method.getReturnType());
        }

        // isFinderXXX
        boolean isFinder = false;

        if (mName.startsWith("find")
            && (javax.ejb.EJBHome.class.isAssignableFrom(declClass)
                || javax.ejb.EJBLocalHome.class.isAssignableFrom(declClass))
            ) {
            isFinder = true;
        }
        if (isFinder) {
            if ("findByPrimaryKey".equals(mName)) {
                mKind = METHOD_FINDER_BY_PK;
            } else {
                if (java.util.Enumeration.class.equals(method.getReturnType())) {
                    mKind = METHOD_FINDER_ENUM;
                } else if (java.util.Collection.class.equals(method.getReturnType())) {
                    mKind = METHOD_FINDER_COL;
                } else {
                    mKind = METHOD_FINDER_SIMPLE;
                }
            }
        }

        // isCreate
        if (mName.startsWith("create")
            && (javax.ejb.EJBHome.class.isAssignableFrom(declClass)
                || javax.ejb.EJBLocalHome.class.isAssignableFrom(declClass))
            ) {
            mKind = METHOD_CREATE;
        }

        // isEjbCreate
        if (mName.startsWith("ejbCreate")
            && javax.ejb.EnterpriseBean.class.isAssignableFrom(declClass)) {
            mKind = METHOD_EJB_CREATE;
        }

        // isEjbPostCreate
        if (mName.startsWith("ejbPostCreate")
            && javax.ejb.EnterpriseBean.class.isAssignableFrom(declClass)) {
            mKind = METHOD_EJB_POST_CREATE;
        }

        // isRemoveXXX
        boolean isRemove = "remove".equals(mName);
        if (isRemove) {
            if (declClass.equals(javax.ejb.EJBObject.class)
                || declClass.equals(javax.ejb.EJBLocalObject.class)) {
                mKind = METHOD_REMOVE_THIS;
            } else if (declClass.equals(javax.ejb.EJBHome.class)
                       || declClass.equals(javax.ejb.EJBLocalHome.class)) {
                if (params[0].equals(javax.ejb.Handle.class)) {
                    mKind = METHOD_REMOVE_HANDLE;
                } else {
                    mKind = METHOD_REMOVE_PK;
                }
            }
        }

        // isEjbRemove, isEjbLoad, isEjbStore
        if (javax.ejb.EnterpriseBean.class.isAssignableFrom(declClass)
            && (params.length == 0)) {
            if ("ejbRemove".equals(mName)) {
                mKind = METHOD_EJB_REMOVE;
            } else if ("ejbLoad".equals(mName)) {
                mKind = METHOD_EJB_LOAD;
            } else if ("ejbStore".equals(mName)) {
                mKind = METHOD_EJB_STORE;
            }
        }

        // isEjbSetEntityContext
        if (javax.ejb.EnterpriseBean.class.isAssignableFrom(declClass)) {
            if ("setEntityContext".equals(mName)
                && (params.length == 1)
                && javax.ejb.EntityContext.class.equals(params[0])) {
                mKind = METHOD_EJB_SET_ENTITY_CONTEXT;
            }
        }

        // isEjbActivate
        if (javax.ejb.EnterpriseBean.class.isAssignableFrom(declClass)) {
            if ("ejbActivate".equals(mName)
                && (params.length == 0)) {
                mKind = METHOD_EJB_ACTIVATE;
            }
        }

        if (dd instanceof EntityJdbcCmp1Desc) {
            // For Entity CMP 1
            // SqlStmt, and ParamsWhere in case of FinderSimple, FinderEnum or FinderCol.
            EntityJdbcCmp1Desc edd = (EntityJdbcCmp1Desc) dd;
            StringBuffer lf = new StringBuffer();
            StringBuffer lv = new StringBuffer();
            boolean firstLf = true;
            boolean firstLv = true;
            if (mKind == METHOD_EJB_CREATE) {
                for (Iterator i = edd.getCmpFieldDescIterator(); i.hasNext();) {
                    FieldJdbcDesc fd = (FieldJdbcDesc) i.next();
                    if (firstLv) {
                        firstLv = false;
                    } else {
                        lf.append(", ");
                        lv.append(", ");
                    }
                    lf.append(fd.getJdbcFieldName());
                    lv.append("?");
                }
                mSqlStmt =
                    "insert into " + edd.getJdbcTableName()
                    + " (" + lf + ") values (" + lv + ")";
            } else if (mKind == METHOD_FINDER_BY_PK) {
                for (Iterator i = edd.getCmpFieldDescIterator(); i.hasNext();) {
                    FieldJdbcDesc fd = (FieldJdbcDesc) i.next();
                    if (fd.isPrimaryKey()) {
                        if (firstLv) {
                            firstLv = false;
                        } else {
                            lf.append(", ");
                            lv.append(" and ");
                        }
                        lf.append(fd.getJdbcFieldName());
                        lv.append(fd.getJdbcFieldName() + "=?");
                    }
                }

                mSqlStmt =
                    "select " + lf + " from " + edd.getJdbcTableName()
                    + " where " + lv;
            } else if ((mKind == METHOD_FINDER_SIMPLE)
                       || (mKind == METHOD_FINDER_ENUM)
                       || (mKind == METHOD_FINDER_COL)) {
                if (!((MethodJdbcCmp1Desc) methodDesc).hasWhereClause()) {
                    throw new Error("No WHERE clause defined for the finder method '"
                                    + method.toString() + "'");
                }
                ArrayList posParams = new ArrayList();
                String clauseWhere = parseWhere (((MethodJdbcCmp1Desc) methodDesc).getWhereClause(), posParams);
                for (Iterator i = edd.getCmpFieldDescIterator(); i.hasNext();) {
                    FieldJdbcDesc fd = (FieldJdbcDesc) i.next();
                    if (fd.isPrimaryKey()) {
                        if (firstLf) {
                            firstLf = false;
                        } else {
                            lf.append(", ");
                        }
                        lf.append(fd.getJdbcFieldName());
                    }
                }

                mSqlStmt =
                    "select " + lf + " from " + edd.getJdbcTableName()
                    + " " + clauseWhere;
                mParamWhereList = new ArrayList();
                for (int i = 0; i < posParams.size(); i++) {
                    int pos = ((Integer) posParams.get(i)).intValue();
                    if (pos >= params.length) {
                        throw new Error("Wrong parameters number between the method definition and the WHERE clause of the method '" + method + "'");
                    }
                    VcParamWhere vpw = new VcParamWhere(params[pos], pos + 1);
                    mParamWhereList.add(vpw);
                }
            } else if (mKind == METHOD_EJB_REMOVE) {
                for (Iterator i = edd.getCmpFieldDescIterator(); i.hasNext();) {
                    FieldJdbcDesc fd = (FieldJdbcDesc) i.next();
                    if (fd.isPrimaryKey()) {
                        if (firstLv) {
                           firstLv = false;
                        } else {
                           lv.append(" and ");
                        }
                        lv.append(fd.getJdbcFieldName() + "=?");
                    }
                }

                mSqlStmt =
                    "delete from " + edd.getJdbcTableName()
                    + " where " + lv;
            } else if (mKind == METHOD_EJB_LOAD) {
                for (Iterator i = edd.getCmpFieldDescIterator(); i.hasNext();) {
                    FieldJdbcDesc fd = (FieldJdbcDesc) i.next();
                    if (firstLf) {
                        firstLf = false;
                    } else {
                        lf.append(", ");
                    }
                    lf.append(fd.getJdbcFieldName());
                    if (fd.isPrimaryKey()) {
                        if (firstLv) {
                            firstLv = false;
                        } else {
                            lv.append(" and ");
                        }
                        lv.append(fd.getJdbcFieldName() + "=?");
                    }
                }

                mSqlStmt =
                    "select " + lf + " from " + edd.getJdbcTableName()
                    + " where " + lv;
            } else if (mKind == METHOD_EJB_STORE) {
                for (Iterator i = edd.getCmpFieldDescIterator(); i.hasNext();) {
                    FieldJdbcDesc fd = (FieldJdbcDesc) i.next();
                    if (fd.isPrimaryKey()) {
                        if (firstLv) {
                            firstLv = false;
                        } else {
                            lv.append(" and ");
                        }
                        lv.append(fd.getJdbcFieldName() + "=?");
                    } else {
                        if (firstLf) {
                           firstLf = false;
                        } else {
                            lf.append(", ");
                        }
                        lf.append(fd.getJdbcFieldName() + "=?");
                    }
                }
                mSqlStmt =
                    "update " + edd.getJdbcTableName()
                    + " set " + lf + " where " + lv;
            }
        } else if (dd instanceof EntityJdbcCmp2Desc) {
            // ejbSelect() methods
            boolean isEjbSelect = false;
            if (mName.startsWith("ejbSelect")
                && javax.ejb.EntityBean.class.isAssignableFrom(declClass)) {
                isEjbSelect = true;
            }
            if (isEjbSelect) {
                if (java.util.Collection.class.equals(method.getReturnType())) {
                    mKind = METHOD_EJB_SELECT_COL;
                } else if (java.util.Set.class.equals(method.getReturnType())) {
                    mKind = METHOD_EJB_SELECT_SET;
                } else {
                    mKind = METHOD_EJB_SELECT_SIMPLE;
                }
            }
            if (isFinder || isEjbSelect) {
                mParamList = new ArrayList();
                for (int p = 0; p < params.length; p++) {
                    mParamList.add(new VcParam(params[p], dd));
                }
                if (isEjbSelect) {
                    try {
                        resFieldOfEjbql = getResField((MethodCmp2Desc) methodDesc);
                        if (resFieldOfEjbql.getType().getTypeCode() == PType.TYPECODE_REFERENCE) {
                            if (((MethodCmp2Desc) methodDesc).isResultTypeMappingRemote()) {
                                isEjbqlReturnRemoteBean = true;
                            } else {
                                isEjbqlReturnLocalBean = true;
                            }
                        }
                    } catch (DeploymentDescException e) {
                        throw new Error("Error while the EJB-QL parsing of '" + method.toString()
                                        + "': " + e.getMessage());
                    }
                } else if (!isFinderByPk()) {
                    // finder method other than findByPrimaryKey: parse the EJBQL query to check the query
                    try {
                        getResField((MethodCmp2Desc) methodDesc);
                    } catch (DeploymentDescException ignore) {
                        GenIC.logger.log(BasicLevel.WARN, "Warning: Error while the EJB-QL parsing of '" + method.toString()
                                        + "': " + ignore.getMessage());
                    }
                }
            }

        }

        // System.out.print("VcMethod: "+method.toString());
        // System.out.println(toString());
    }

    protected EjbqlQueryTreeHolder queryTreeHolder = null;
    
    /**
     * Check the query, and return the resfield.
     */
    private org.objectweb.medor.api.Field getResField(MethodCmp2Desc md) throws DeploymentDescException {
        if (queryTreeHolder == null) {
            try {
                queryTreeHolder = new EjbqlQueryTreeHolder(md, md.getQueryNode(), null, manager);
            } catch (Exception e) {
                throw new DeploymentDescException("ejbql query does not map to the persistent schema",e);
            }
        }
        return queryTreeHolder.getResField();
    }
    
    /**
     * @return the string representation of the java default value for the cmp field (ie "0 "for int, "null" for object ...)
     */
    public String getDefaultValue() {
        return mDefaultValue;
    }

    /**
     * Parses Strings with syntax "WHERE summary like ?<nnn>"
     * where <nnn> is a decimal integer that indexes (starting by 0)
     * the parameters in the findByXXX method for CMP1.
     * @param fragment
     * @param pos
     * @return
     * @author: Santiago Gala (sgala@hisitech.com)
     */
    private String parseWhere(String fragment, List pos) {
        StringTokenizer parseWhere = new StringTokenizer(fragment, "?\n", true);
        int numpos = pos.size();
        String newWhere = "";
        String tok;
        while (parseWhere.hasMoreTokens()) {
            tok = parseWhere.nextToken();
            String numVar = "";
            if (Character.isDigit(tok.charAt(0))) {
                for (int i = 0; tok.length() > i && Character.isDigit(tok.charAt(i)); i++) {
                    numVar += tok.charAt(i);
                }
                if (numVar.length() > 0) {
                    newWhere += tok.substring(numVar.length(), tok.length());
                    pos.set(numpos - 1, (Object) new Integer(Integer.parseInt(numVar) - 1));
                }
            } else {
                newWhere += tok;
                if (tok.charAt(0) == '?') {
                    pos.add(new Integer(numpos++));
                }
            }
        }
        return newWhere;
    }

    /**
     * @return Return the name of the method
     */
    public String getName() {
        return mName;
    }

    /**
     * @return Return the name of the method with the first letter capitalized
     */
    public String getCapName() {
        return mCapName;
    }

    /**
     * @return Return the string representation of the formal parameters list (ie "int p1, String p2")
     */
    public String getFormalParameters() {
        return mFormalParameters.toString();
    }

    /**
     * @return Return the string representation of the actual parameters list (ie "p1, p2")
     */
    public String getActualParameters() {
        return mActualParameters.toString();
    }

    /**
     * @return Return the string representation of the exception list of the method
     */
    public String getExceptionList() {
        return mExceptionList.toString();
    }

    /**
     * @return Return true if the method throws the javax.ejb.createException exception
     */
    public boolean canThrowCreate() {
        return canThrowCreate;
    }

    /**
     * @return Return the string representation of the security signature of the method
     */
    public String getSecuritySignature() {
        if (mSecuritySignature == null) {
            throw new Error("VcMethod.getSecuritySignature() not available for the method '"
                            + mMethod.toString() + "'");
        }
        return mSecuritySignature;
    }

    /**
     * @return Return the transactional attribute associated to the method
     */
    public int getTxAttribute() {
        if (mTxAttribute == MethodDesc.TX_NULL) {
            throw new Error("VcMethod.getTxAttribute() not available for the method '"
                            + mMethod.toString() + "'");
        }
        return mTxAttribute;
    }

    /**
     * @return Return the name of the return type of the method
     */
    public String getReturnType() {
        return mReturnType;
    }

    /**
     * @return the string representation of the wrapper type for the field
     */
    public String getWrapperType() {
        return mWrapperType;
    }
    

    /**
     * @return Return false because we are not able to know if a finder method is a FinderAll method !!
     */
    public boolean isFinderAll() {
        // How to known a finder is a finderAll !?
        return false;
    }

    /**
     * @return Return true if the method is the findByPrimaryKey() method
     */
    public boolean isFinderByPk() {
        return (mKind == METHOD_FINDER_BY_PK);
    }

    /**
     * @return Return true if the method is a finder method that returns a unique object
     */
    public boolean isFinderSimple() {
        return (mKind == METHOD_FINDER_SIMPLE);
    }

    /**
     * @return Return true if the method is a finder method that returns a Enumeration
     */
    public boolean isFinderEnum() {
        return (mKind == METHOD_FINDER_ENUM);
    }

    /**
     * @return Return true if the method is a finder method that returns a Collection
     */
    public boolean isFinderCol() {
        return (mKind == METHOD_FINDER_COL);
    }

    /**
     * @return Return true if the method is an ejbSelect method that returns a unique object
     */
    public boolean isEjbSelectSimple() {
        return (mKind == METHOD_EJB_SELECT_SIMPLE);
    }

    /**
     * @return Return true if the method is an ejbSelect method that returns a Set
     */
    public boolean isEjbSelectSet() {
        return (mKind == METHOD_EJB_SELECT_SET);
    }

    /**
     * @return Return true if the method is an ejbSelect method that returns a Collection
     */
    public boolean isEjbSelectCol() {
        return (mKind == METHOD_EJB_SELECT_COL);
    }

    /**
     * @return Return true if the method is a finder/select method that return a remote bean
     */
    public boolean isEjbqlReturnRemoteBean() {
        return isEjbqlReturnRemoteBean;
    }

    /**
     * @return Return true if the method is a finder/select method that return a local bean
     */
    public boolean isEjbqlReturnLocalBean() {
        return isEjbqlReturnLocalBean;
    }

    /**
     * @return Return true if the method is home.create()
     */
    public boolean isCreate() {
        return (mKind == METHOD_CREATE);
    }

    /**
     * @return Return true if the method is bean.ejbCreate()
     */
    public boolean isEjbCreate() {
        return (mKind == METHOD_EJB_CREATE);
    }

    /**
     * @return Return true if the method is bean.ejbPostCreate()
     */
    public boolean isEjbPostCreate() {
        return (mKind == METHOD_EJB_POST_CREATE);
    }

    /**
     * @return Return true if the method is EJBObject.remove() method
     */
    public boolean isRemoveThis() {
        return (mKind == METHOD_REMOVE_THIS);
    }

    /**
     * @return Return true if the method is home.remove(primarykey)
     */
    public boolean isRemovePk() {
        return (mKind == METHOD_REMOVE_PK);
    }

    /**
     * @return Return true if the method is home.remove(handle);
     */
    public boolean isRemoveHandle() {
        return (mKind == METHOD_REMOVE_HANDLE);
    }

    /**
     * @return Return true if the method is bean.ejbRemove()
     */
    public boolean isEjbRemove() {
        return (mKind == METHOD_EJB_REMOVE);
    }

    /**
     * @return Return true if the method is bean.ejbLoad()
     */
    public boolean isEjbLoad() {
        return (mKind == METHOD_EJB_LOAD);
    }

    /**
     * @return Return true if the method is bean.ejbStore()
     */
    public boolean isEjbStore() {
        return (mKind == METHOD_EJB_STORE);
    }

    /**
     * @return Return true if the method is bean.setEntityContext()
     */
    public boolean isEjbSetEntityContext() {
        return (mKind == METHOD_EJB_SET_ENTITY_CONTEXT);
    }

    /**
     * @return Return true if the method is bean.ejbActivate()
     */
    public boolean isEjbActivate() {
        return (mKind == METHOD_EJB_ACTIVATE);
    }

    /**
     * Only for CMP1 finder methods
     * @return Return the string representation of the SQL statement associated to the method
     */
    public String getSqlStmt() {
        if (mSqlStmt == null) {
            throw new Error("VcMethod.getSqlStmt() not available for the method '"
                            + mMethod.toString() + "'");
        }
        return mSqlStmt;
    }


    /**
     * @return Return the Class array of the parameters of the method
     */
    public Class[] getParameterTypes() {
        return parameterTypes;
    }

    /**
     * @return Return the number of the parameters of the method
     */
    public int getParametersNumber() {
        return parameterTypes.length;
    }


    /**
     * Only for CMP1 finder methods
     * @return Return the VcParamWhere list of the method
     */
    public Vector getParamWhereList() {
        if (mParamWhereList == null) {
            throw new Error("VcMethod.getParamWhereList() not available for the method '"
                            + mMethod.toString() + "'");
        }
        return (new Vector(mParamWhereList));
    }


    /**
     * @return Return the VcParam list of the method
     */
    public Vector getParamList() {
        if (mParamList == null) {
            throw new Error("VcMethod.getParamList() not available for the method '"
                            + mMethod.toString() + "'");
        }
        return (new Vector(mParamList));
    }


    /**
     * @return Return the index of the method. This is a unique index for each method of a bean.
     */
    public int getMethodIndex() {
        return methodIndex;
    }

    /**
     * Only for CMP2 finder/select methods
     * @return Return the Medor field of tuple result of an Finder/Select method
     */
    public org.objectweb.medor.api.Field getResFieldOfEjbql() {
        return resFieldOfEjbql;
    }

    /**
     * Only for CMP2 finder/select methods
     * @return Return the Medor get method name of the tuple result of an ejbSelect method
     */
    public String getTupleGetter() {
        PType type = resFieldOfEjbql.getType();
        switch(type.getTypeCode()) {
        case PType.TYPECODE_BOOLEAN:
            return "getBoolean";
        case PType.TYPECODE_BYTE:
            return "getByte";
        case PType.TYPECODE_CHAR:
            return "getChar";
        case PType.TYPECODE_DATE:
            return "getDate";
        case PType.TYPECODE_DOUBLE:
            return "getDouble";
        case PType.TYPECODE_FLOAT:
            return "getFloat";
        case PType.TYPECODE_INT:
            return "getInt";
        case PType.TYPECODE_LONG:
            return "getLong";
        case PType.TYPECODE_SERIALIZED:
            return "getObject";
        case PType.TYPECODE_SHORT:
            return "getShort";
        case PType.TYPECODE_STRING:
            return "getString";
        case PType.TYPECODE_BIGDECIMAL:
        case PType.TYPECODE_REFERENCE:
        case PType.TYPECODE_BYTEARRAY:
        case PType.TYPECODE_CHARARRAY:
        case PType.TYPECODE_OBJBOOLEAN:
        case PType.TYPECODE_OBJBYTE:
        case PType.TYPECODE_OBJCHAR:
        case PType.TYPECODE_OBJDOUBLE:
        case PType.TYPECODE_OBJFLOAT:
        case PType.TYPECODE_OBJINT:
        case PType.TYPECODE_OBJLONG:
        case PType.TYPECODE_OBJSHORT:
        default:
            return "getObject";
        }
    }

    /**
     * Only for CMP2 finder/select methods
     * @return Return true is the return type of the Medor getter method of the tuple result of an ejbSelect method is a primitive type
     */
    public boolean isTupleGetterPrimitive() {
        PType type = resFieldOfEjbql.getType();
        switch(type.getTypeCode()) {
        case PType.TYPECODE_BOOLEAN:
        case PType.TYPECODE_BYTE:
        case PType.TYPECODE_CHAR:
        case PType.TYPECODE_DOUBLE:
        case PType.TYPECODE_FLOAT:
        case PType.TYPECODE_INT:
        case PType.TYPECODE_LONG:
        case PType.TYPECODE_SHORT:
            return true;
        default:
            return false;
        }
    }

    /**
     * Only for CMP2 finder/select methos
     * @return Return the associated class of the Medor getter method of the tuple result of an ejbSelect method when it's a primtive type
     */
    public String getTupleGetterObjectClass() {
        PType type = resFieldOfEjbql.getType();
        switch(type.getTypeCode()) {
        case PType.TYPECODE_BOOLEAN:
            return "Boolean";
        case PType.TYPECODE_BYTE:
            return "Byte";
        case PType.TYPECODE_CHAR:
            return "Char";
        case PType.TYPECODE_DOUBLE:
            return "Double";
        case PType.TYPECODE_FLOAT:
            return "Float";
        case PType.TYPECODE_INT:
            return "Integer";
        case PType.TYPECODE_LONG:
            return "Long";
        case PType.TYPECODE_SHORT:
            return "Short";
        default:
            return "Object";
        }
    }

    /**
     * Split a dot separated path into tokens
     * @param path input path to split
     * @return Return the string array of the splited token
     */
    String[] splitPath(String path) {
        StringTokenizer st = new StringTokenizer(path, ".");
        String[] ret = new String[st.countTokens()];
        for (int i = 0; i < ret.length; i++) {
            ret[i] = st.nextToken();
        }
        return ret;
    }

    /**
     * Gets an empty array if no parameters and return an array of objects of the parameters if any
     * @return array of objects
     */
    public String getArrayObjectParameters() {
        if (mArrayObjectParameters.length() == 0) {
            return "new Object[0]";
        } else {
            return "new Object[] {" + mArrayObjectParameters.toString() + "}";
        }
    }


    /**
     * @return Return a string representation of the VcMethod object for debug use
     */
    public String toString() {
        StringBuffer ret = new StringBuffer();
        ret.append("\n    Name                  = " + getName());
        ret.append("\n    CapName               = " + getCapName());
        ret.append("\n    FormalParameters      = " + getFormalParameters());
        ret.append("\n    ActualParameters      = " + getActualParameters());
        ret.append("\n    ExceptionList         = " + getExceptionList());
        if (mSecuritySignature != null) {
            ret.append("\n    SecuritySignature     = " + getSecuritySignature());
        }
        if (mTxAttribute != MethodDesc.TX_NULL) {
            ret.append("\n    TxAttribute           = " + getTxAttribute());
        }
        ret.append("\n    ReturnType            = " + getReturnType());
        ret.append("\n    isFinderByPk          = " + isFinderByPk());
        ret.append("\n    isFinderSimple        = " + isFinderSimple());
        ret.append("\n    isFinderEnum          = " + isFinderEnum());
        ret.append("\n    isFinderCol           = " + isFinderCol());
        ret.append("\n    isCreate              = " + isCreate());
        ret.append("\n    isEjbCreate           = " + isEjbCreate());
        ret.append("\n    isRemoveThis          = " + isRemoveThis());
        ret.append("\n    isRemovePk            = " + isRemovePk());
        ret.append("\n    isRemoveHandle        = " + isRemoveHandle());
        ret.append("\n    isEjbRemove           = " + isEjbRemove());
        ret.append("\n    isEjbLoad             = " + isEjbLoad());
        ret.append("\n    isEjbStore            = " + isEjbStore());
        ret.append("\n    isEjbSetEntityContext = " + isEjbSetEntityContext());
        ret.append("\n    isEjbActivate         = " + isEjbActivate());
        ret.append("\n    isEjbSelectSimple     = " + isEjbSelectSimple());
        ret.append("\n    isEjbSelectSet        = " + isEjbSelectSet());
        ret.append("\n    isEjbSelectCol        = " + isEjbSelectCol());
        if (mSqlStmt != null) {
            ret.append("\n    SqlStmt               = " + getSqlStmt());
        }
        if (mParamWhereList != null) {
            ret.append("\n    ParamWhereList        = " + getParamWhereList());
        }
        return (ret.toString());
    }

}
