All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.firebirdsql.jdbc.FBProcedureCall Maven / Gradle / Ivy

There is a newer version: 2.2.15
Show newest version
/*
 * Firebird Open Source JavaEE Connector - JDBC Driver
 *
 * Distributable under LGPL license.
 * You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html
 *
 * This program 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
 * LGPL License for more details.
 *
 * This file was created by members of the firebird development team.
 * All individual contributions remain the Copyright (C) of those
 * individuals.  Contributors to this file are either listed here or
 * can be obtained from a source control history command.
 *
 * All rights reserved.
 */
package org.firebirdsql.jdbc;

import java.sql.SQLException;
import java.util.Collection;
import java.util.List;
import java.util.Vector;

/**
 * Represents procedure call.
 */
public class FBProcedureCall implements Cloneable {

    public Object clone() {
        try {
            FBProcedureCall newProcedureCall = (FBProcedureCall) super.clone();

            //Copy each input and output parameter.
            newProcedureCall.inputParams = cloneParameters(inputParams);
            newProcedureCall.outputParams = cloneParameters(outputParams);

            return newProcedureCall;
        } catch (CloneNotSupportedException e) {
            return null;
        }
    }

    private static Vector cloneParameters(final Vector parameters) {
        final Vector clonedParameters = new Vector(parameters.size());
        for (FBProcedureParam param : parameters) {
            clonedParameters.add(param != null ? (FBProcedureParam) param.clone() : null);
        }
        return clonedParameters;
    }

    /**
     * true if the old callable statement compatibility mode should
     * be used, otherwise - false. Current value - true.
     */
    public static final boolean OLD_CALLABLE_STATEMENT_COMPATIBILITY = true;

    private String name;
    private Vector inputParams = new Vector();
    private Vector outputParams = new Vector();

    /**
     * Get the name of the procedure to be called.
     *
     * @return The procedure name
     */
    public String getName() {
        return name;
    }

    /**
     * Set the name of the procedure to be called.
     *
     * @param name The name of the procedure
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * Get input parameter by the specified index.
     *
     * @param index index for which parameter has to be returned, first
     * index is 1
     *
     * @return instance of {@link FBProcedureParam}.
     */
    public FBProcedureParam getInputParam(int index) {
        FBProcedureParam result = getParam(inputParams, index);

        if (result == null || result == NullParam.NULL_PARAM) {
            result = getParam(outputParams, index);

            // ensure that vector has right size
            // note, index starts with 1
            if (inputParams.size() < index) {
                inputParams.setSize(index);
            }

            inputParams.set(index - 1, result);
        }

        return result;
    }

    /**
     * Get the output parameter at the specified index.
     *
     * @param index The index of the parameter, first index is 1
     * @return The parameter at the given index
     */
    public FBProcedureParam getOutputParam(int index) {
        return getParam(outputParams, index);
    }

    /**
     * Get parameter with the specified index from the specified collection.
     *
     * @param params collection containing parameters.
     * @param index index for which parameter has to be found.
     *
     * @return instance of {@link FBProcedureParam}.
     */
    private FBProcedureParam getParam(Collection params, int index) {
        for (FBProcedureParam param : params) {
            if (param != null && param.getIndex() == index) {
                return param;
            }
        }

        return NullParam.NULL_PARAM;
    }

    /**
     * Map output parameter index to a column number of corresponding result
     * set.
     *
     * @param index index to map.
     *
     * @return mapped column number or index if no output parameter
     * with the specified index found (assuming that {@link #OLD_CALLABLE_STATEMENT_COMPATIBILITY}
     * constant is set to true, otherwise throws exception).
     *
     * @throws FBSQLException if compatibility mode is switched off and no
     * parameter was found (see {@link #OLD_CALLABLE_STATEMENT_COMPATIBILITY}
     * constant).
     */
    public int mapOutParamIndexToPosition(int index) throws FBSQLException {
        return mapOutParamIndexToPosition(index, OLD_CALLABLE_STATEMENT_COMPATIBILITY);
    }

    /**
     * Map output parameter index to a column number of corresponding result
     * set.
     *
     * @param index index to map.
     * @param compatibilityMode true if we should run in old
     * compatibility mode.
     *
     * @return mapped column number or index if no output parameter
     * with the specified index found and compatibilityMode is set.
     *
     * @throws FBSQLException if compatibility mode is switched off and no
     * parameter was found.
     */
    public int mapOutParamIndexToPosition(int index, boolean compatibilityMode) throws FBSQLException {
        int position = -1;

        for (FBProcedureParam param : outputParams) {
            if (param != null && param.isParam()) {
                position++;

                if (param.getIndex() == index) {
                    return position + 1;
                }
            }
        }

        // hack: if we did not find the right parameter we return
        // an index that was asked if we run in compatibility mode
        // 
        // we should switch it off as soon as people convert applications
        if (compatibilityMode) {
            return index;
        } else {
            throw new FBSQLException("Specified parameter does not exist.", FBSQLException.SQL_STATE_INVALID_COLUMN);
        }
    }

    /**
     * Get the list of input parameters for this procedure call.
     *
     * @return A list of all input parameters
     */
    public List getInputParams() {
        return inputParams;
    }

    /**
     * Get a list of output parameters for this procedure call.
     *
     * @return A list of all output parameters
     */
    public List getOutputParams() {
        return outputParams;
    }

    /**
     * Add an input parameter to this procedure call.
     *
     * @param param The parameter to be added
     */
    public void addInputParam(FBProcedureParam param) {
        if (inputParams.size() < param.getPosition() + 1) {
            inputParams.setSize(param.getPosition() + 1);
        }

        inputParams.set(param.getPosition(), param);
    }

    /**
     * Add an output parameter to this procedure call.
     *
     * @param param The parameter to be added
     */
    public void addOutputParam(FBProcedureParam param) {
        if (outputParams.size() < param.getPosition() + 1) {
            outputParams.setSize(param.getPosition() + 1);
        }

        outputParams.set(param.getPosition(), param);
    }

    /**
     * Add call parameter. This method adds new parameter to the procedure call
     * and tries to automatically place the parameter into the right collection
     * if it contains a hint whether it is input or output parameter.
     *
     * @param position position of the parameter in the procedure call.
     * @param param contents of the parameter.
     *
     * @return instance of the {@link FBProcedureParam} that was created to
     * represent this parameter.
     */
    public FBProcedureParam addParam(int position, String param) {
        param = param.trim();

        boolean isInputParam = true;

        if (param.length() > 4) {
            String possibleOutIndicator = param.substring(0, 3);
            if ("OUT".equalsIgnoreCase(possibleOutIndicator) && Character.isSpaceChar(param.charAt(3))) {
                isInputParam = false;
                param = param.substring(4).trim();
            }
        }

        if (isInputParam && param.length() > 3) {
            String possibleInIndicator = param.substring(0, 2);
            if ("IN".equalsIgnoreCase(possibleInIndicator) && Character.isSpaceChar(param.charAt(2))) {
                param = param.substring(3).trim();
            }
        }

        FBProcedureParam callParam = new FBProcedureParam(position, param);
        if (isInputParam) {
            addInputParam(callParam);
        } else {
            addOutputParam(callParam);
        }

        return callParam;
    }

    /**
     * Register output parameter. This method marks parameter with the specified
     * index as output. Parameters marked as output cannot be used as input
     * parameters.
     *
     * @param index index of the parameter to mark as output.
     * @param type SQL type of the parameter.
     *
     * @throws SQLException if something went wrong.
     */
    public void registerOutParam(int index, int type) throws SQLException {
        FBProcedureParam param = getInputParam(index);

        if (param == null || param == NullParam.NULL_PARAM) {
            param = getOutputParam(index);
        } else {
            addOutputParam(param);

            if (!param.isValueSet()) {
                inputParams.set(param.getPosition(), null);
            }
        }

        if (param == null || param == NullParam.NULL_PARAM) {
            throw new FBSQLException("Cannot find parameter with the specified position.",
                    FBSQLException.SQL_STATE_INVALID_COLUMN);
        }

        param.setType(type);
    }

    /**
     * Get native SQL for the specified procedure call.
     *
     * @return native SQL that can be executed by the database server.
     */
    public String getSQL(boolean select) throws FBSQLException {
        StringBuilder sb = new StringBuilder(select
                ? AbstractCallableStatement.NATIVE_SELECT_COMMAND
                : AbstractCallableStatement.NATIVE_CALL_COMMAND);
        sb.append(name);

        boolean firstParam = true;
        sb.append('(');
        for (FBProcedureParam param : inputParams) {
            if (param == null) {
                continue;
            }

            if (!firstParam) {
                sb.append(',');
            } else {
                firstParam = false;
            }

            sb.append(param.getParamValue());
        }

        if (firstParam) {
            sb.setLength(sb.length() - 1);
        } else {
            sb.append(')');
        }

        return sb.toString();
    }

    /**
     * Checks if all parameters have been set.
     *
     * @throws SQLException When some parameters don't have values, and are not registered as an out parameter.
     */
    public void checkParameters() throws SQLException {
        for (FBProcedureParam param : inputParams) {
            if (param == null) {
                continue;
            }

            // if parameter does not have set value, and is not registered
            // as output parameter, throw an exception, otherwise, continue
            // to the next one.
            if (!param.isValueSet()) {
                if (param.isParam()
                        && outputParams.size() > 0
                        && outputParams.get(param.getPosition()) == null)
                    throw new FBSQLException("Value of parameter " + param.getIndex() + " not set and "
                            + "it was not registered as output parameter.", FBSQLException.SQL_STATE_WRONG_PARAM_NUM);
            }
        }
    }

    /**
     * Check if obj is equal to this instance.
     *
     * @return true iff obj is instance of this class
     * representing the same procedure with the same parameters.
     */
    public boolean equals(Object obj) {
        if (obj == this) return true;
        if (!(obj instanceof FBProcedureCall)) return false;

        FBProcedureCall that = (FBProcedureCall) obj;

        boolean result = this.name != null ? this.name.equals(that.name) : that.name == null;

        result &= this.inputParams.equals(that.inputParams);
        result &= this.outputParams.equals(that.outputParams);

        return result;
    }

    public int hashCode() {
        int hashCode = 547;
        hashCode = 37 * hashCode + (name != null ? name.hashCode() : 0);
        hashCode = 37 * hashCode + inputParams.hashCode();
        hashCode = 37 * hashCode + outputParams.hashCode();
        return hashCode;
    }

    /**
     * This class defines a procedure parameter that does not have any value
     * and value of which cannot be set. It is created in order to avoid NPE
     * when {@link FBProcedureCall#getInputParam(int)} does not find correct
     * parameter.
     */
    private static final class NullParam extends FBProcedureParam {

        private static final NullParam NULL_PARAM = new NullParam();

        public void setValue(Object value) throws SQLException {
            throw new FBSQLException("You cannot set value of an non-existing parameter.",
                    FBSQLException.SQL_STATE_INVALID_ARG_VALUE);
        }

    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy