Package org.jpox.store.mapped.expression

Source Code of org.jpox.store.mapped.expression.ScalarExpression

/**********************************************************************
Copyright (c) 2002 Mike Martin (TJDO) and others. All rights reserved.
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.

Contributors:
2003 Andy Jefferson - commented
    ...
**********************************************************************/
package org.jpox.store.mapped.expression;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

import org.jpox.ObjectManagerFactoryImpl;
import org.jpox.exceptions.JPOXException;
import org.jpox.exceptions.JPOXUserException;
import org.jpox.store.mapped.DatastoreField;
import org.jpox.store.mapped.mapping.JavaTypeMapping;
import org.jpox.store.mapped.query.StatementText;
import org.jpox.store.query.JPOXQueryInvalidParametersException;
import org.jpox.util.Localiser;

/**
* A Scalar expression in a Query. Used to compute values with a resulting type
*
* @version $Revision: 1.34 $
**/
public abstract class ScalarExpression
{
    /** Localiser for messages */
    protected static final Localiser LOCALISER = Localiser.getInstance("org.jpox.store.Localisation",
        ObjectManagerFactoryImpl.class.getClassLoader());

    /**
     * when translated to StatementText, the expression is generated to
     * be projected as result of executing a statement
     **/
    public static int PROJECTION = 0;

    /**
     * when translated to StatementText, the expression is generated to
     * be used as filter to a executing statement
     **/   
    public static int FILTER = 1;

    /**
     * Inner class representing an Operator
     **/
    protected static class Operator
    {
        protected final String symbol;
        protected final int precedence;

        /**
         * Operator
         * @param symbol the source text or symbol of an operator. e.g  =, ==, +, /, >, <, etc
         * @param precedence the order of precedence where the expression is compiled
         */
        public Operator(String symbol, int precedence)
        {
            this.symbol = symbol;
            this.precedence = precedence;
         }

        public String toString()
        {
            return symbol;
        }
    }

    /**
     * "Monadic" operator performs a function on one operand.
     * It can be in either postfix or prefix notation.
     * <ul>
     * <li>
     * Prefix notation means that the operator appears before its operand. <i>operator operand</i>
     * </li>
     * <li>
     * Postfix notation means that the operator appears after its operand. <i>operand operator</i>
     * </li>
     * </ul>
     **/
    protected static class MonadicOperator extends Operator
    {
        /**
         * Monodiac operator
         * @param symbol the source text or symbol of an operator. e.g  =, ==, +, /, >, <, etc
         * @param precedence the order of precedence where the expression is compiled
         */
        public MonadicOperator(String symbol, int precedence)
        {
            super(symbol, precedence);
        }

        /**
         * Check if this operator has higher precedence than <code>op</code>
         * @param op the operator
         * @return true if this operation is higher
         */
        public boolean isHigherThan(Operator op)
        {
            if (op == null)
            {
                return false;
            }
            else
            {
                return precedence > op.precedence;
            }
        }
    }

    /**
     * "Dyadic" operator performs operation on one or two operands
     **/
    protected static class DyadicOperator extends Operator
    {
        /**
         * An associative operator is one for which parentheses can be inserted and removed without changing the meaning of the expression
         */
        private final boolean isAssociative;

        /**
         * Dyadic operator
         * @param symbol the source text or symbol of an operator. e.g  =, ==, +, /, >, <, etc
         * @param precedence the order of precedence where the expression is compiled
         * @param isAssociative true if associative operator. An associative operator is one for which parentheses can be inserted and removed without changing the meaning of the expression
         */
        public DyadicOperator(String symbol, int precedence, boolean isAssociative)
        {
            super(" " + symbol + " ", precedence);
            this.isAssociative = isAssociative;
        }

        /**
         * Checks if this operation is higher than operator <code>op</code> in left side of the expression
         * @param op the operator in the left side of the expression
         * @return true if this operation is higher than operator <code>op</code> in left side of the expression
         */
        public boolean isHigherThanLeftSide(Operator op)
        {
            if (op == null)
            {
                return false;
            }
            else
            {
                return precedence > op.precedence;
            }
        }
        /**
         * Checks if this operation is higher than operator <code>op</code> in right side of the expression
         * @param op the operator in the right side of the expression
         * @return true if this operation is higher than operator <code>op</code> in right side of the expression
         */
        public boolean isHigherThanRightSide(Operator op)
        {
            if (op == null)
            {
                return false;
            }
            else if (precedence == op.precedence)
            {
                return !isAssociative;
            }
            else
            {
                return precedence > op.precedence;
            }
        }
    }

    /** OR **/
    public static final DyadicOperator OP_OR = new DyadicOperator("OR", 0, true);
    /** AND **/
    public static final DyadicOperator OP_AND = new DyadicOperator("AND", 1, true);
    /** NOT **/
    public static final MonadicOperator OP_NOT = new MonadicOperator("NOT ", 2);
    /** EQ **/
    public static final DyadicOperator OP_EQ = new DyadicOperator("=", 3, false);
    /** NOTEQ **/
    public static final DyadicOperator OP_NOTEQ = new DyadicOperator("<>", 3, false);
    /** LT **/
    public static final DyadicOperator OP_LT = new DyadicOperator("<", 3, false);
    /** LTEQ **/
    public static final DyadicOperator OP_LTEQ = new DyadicOperator("<=", 3, false);
    /** GT **/
    public static final DyadicOperator OP_GT = new DyadicOperator(">", 3, false);
    /** GTEQ **/
    public static final DyadicOperator OP_GTEQ = new DyadicOperator(">=", 3, false);
    /** LIKE **/
    public static final DyadicOperator OP_LIKE = new DyadicOperator("LIKE", 3, false);
    /** NOT LIKE **/
    public static final DyadicOperator OP_NOTLIKE = new DyadicOperator("NOT LIKE", 3, false);
    /** BETWEEN **/
    public static final DyadicOperator OP_BETWEEN = new DyadicOperator("BETWEEN", 3, false);
    /** IS **/
    public static final DyadicOperator OP_IS = new DyadicOperator("IS", 3, false);
    /** ISNOT **/
    public static final DyadicOperator OP_ISNOT = new DyadicOperator("IS NOT", 3, false);
    /** IN **/
    public static final DyadicOperator OP_IN = new DyadicOperator("IN", 3, false);
    /** NOT IN **/
    public static final DyadicOperator OP_NOTIN = new DyadicOperator("NOT IN", 3, false);
    /** ADD **/
    public static final DyadicOperator OP_ADD = new DyadicOperator("+", 4, true);
    /** SUB **/
    public static final DyadicOperator OP_SUB = new DyadicOperator("-", 4, false);
    /** CONCAT **/
    public static final DyadicOperator OP_CONCAT = new DyadicOperator("||", 4, true);
    /** MUL **/
    public static final DyadicOperator OP_MUL = new DyadicOperator("*", 5, true);
    /** DIV **/
    public static final DyadicOperator OP_DIV = new DyadicOperator("/", 5, false);
    /** MOD **/
    public static final DyadicOperator OP_MOD = new DyadicOperator("%", 5, false);
    /** NEG **/
    public static final MonadicOperator OP_NEG = new MonadicOperator("-", 6);
    /** COM **/
    public static final MonadicOperator OP_COM = new MonadicOperator("~", 6);

    /** The Query Expression. */
    protected final QueryExpression qs;

    /** The table expression being used. */
    protected LogicSetExpression te;

    /** The Statement Text for this expression. */
    protected final StatementText st = new StatementText();

    protected Operator lowestOperator = null;

    /** alias identifier for this expression **/
    protected String aliasIdentifier;
   
    /** The expression string without any alias. */
    private String nonAliasExpression;

    /** Mapping for the field. */
    protected JavaTypeMapping mapping;

    /** List of sub-expressions. Used where we have a field that has multiple datastore mappings (one subexpression for each). */
    protected ExpressionList expressionList = new ExpressionList();

    /** Name of a parameter that this expression/literal represents (if any). */
    protected String parameterName = null;

    /** Flag whether we should make checks for type assignability (currently for JPA). */
    protected boolean checkForTypeAssignability = false;

    /**
     * Constructor.
     * @param qs The Query Expression
     */
    protected ScalarExpression(QueryExpression qs)
    {
        this.qs = qs;
    }

    /**
     * Constructor for an expression of the mapping in the specified table.
     * Creates a list of (field) expressions for the datastore mappings of the specified field.
     * @param qs The Query Expression
     * @param mapping The field mapping
     * @param te The table
     */
    protected ScalarExpression(QueryExpression qs, JavaTypeMapping mapping, LogicSetExpression te)
    {
        this(qs);
        this.mapping = mapping;
        this.te = te;
        for (int i=0; i<mapping.getNumberOfDatastoreFields(); i++)
        {
            DatastoreFieldExpression fieldExpr = new DatastoreFieldExpression(qs, mapping.getDataStoreMapping(i).getDatastoreField(), te);
            expressionList.addExpression(fieldExpr);
        }
        st.append(expressionList.toString());
    }

    /**
     * Generates statement as e.g. FUNCTION_NAME(arg[,argN])
     * @param functionName Name of the function
     * @param args ScalarExpression list
     */
    protected ScalarExpression(String functionName, List args)
    {
        st.append(functionName).append('(');
        ScalarExpression arg = null;
        ListIterator i = args.listIterator();
        Object expr = i.next();
        if (expr instanceof String)
        {
            st.append(expr.toString());
        }
        else
        {
            arg = (ScalarExpression)expr;
            st.append(arg);
        }


        while (i.hasNext())
        {
            expr = i.next();
            if (expr instanceof String)
            {
                st.append(',').append(expr.toString());
            }
            else
            {
                arg = (ScalarExpression)expr;
                st.append(',').append(arg);
            }
        }
        qs = arg.qs;

        st.append(')');
    }

    /**
     * Generates statement as e.g. FUNCTION_NAME(arg AS type[,argN as typeN])
     * @param functionName Name of function
     * @param args ScalarExpression list
     * @param types String or ScalarExpression list
     */
    protected ScalarExpression(String functionName, List args, List types)
    {
        st.append(functionName).append('(');

        ListIterator i = args.listIterator();
        ListIterator iTypes = types.listIterator();
        ScalarExpression arg = (ScalarExpression)i.next();
        st.append(arg);
        st.append(" AS ");
        Object type = iTypes.next();
        if (type instanceof ScalarExpression)
        {
            st.append((ScalarExpression)type);
        }
        else
        {
            st.append(type.toString());
        }
        this.qs = arg.qs;

        while (i.hasNext())
        {
            arg = (ScalarExpression)i.next();
            st.append(',').append(arg);
            st.append(" AS ");
            type = iTypes.next();
            if (type instanceof ScalarExpression)
            {
                st.append((ScalarExpression)type);
            }
            else
            {
                st.append(type.toString());
            }
        }

        st.append(')');
    }

    /**
     * Perform a function <code>op</code> on <code>operand</code>
     * @param op operator
     * @param operand operand
     */
    protected ScalarExpression(MonadicOperator op, ScalarExpression operand)
    {
        st.append(op.toString());

        if (op.isHigherThan(operand.lowestOperator))
        {
            st.append('(').append(operand).append(')');
        }
        else
        {
            st.append(operand);
        }

        qs = operand.qs;
        lowestOperator = op;
        mapping = operand.mapping;
    }

    /**
     * Performs a function on two arguments.
     * op(operand1,operand2)
     * operand1 op operand2
     * @param operand1 the first expression
     * @param op the operator between operands
     * @param operand2 the second expression
     */
    protected ScalarExpression(ScalarExpression operand1, DyadicOperator op, ScalarExpression operand2)
    {
        qs = operand1.qs;
        lowestOperator = op;

        if (op == ScalarExpression.OP_CONCAT)
        {
            //enclose within parentheses to make sure the concat expression is first evaluated
            st.append(qs.getStoreManager().getDatastoreAdapter().concatOperator(operand1, operand2).encloseWithInParentheses());
        }
        else
        {
          if (op.isHigherThanLeftSide(operand1.lowestOperator))
          {
              st.append('(').append(operand1).append(')');
          }
          else
          {
              st.append(operand1);
          }

            st.append(op.toString());

            if (op.isHigherThanRightSide(operand2.lowestOperator))
            {
                st.append('(').append(operand2).append(')');
            }
            else
            {
                st.append(operand2);
            }

            // some databases use a ESCAPE expression with LIKE when using an escaped pattern
            if (op == ScalarExpression.OP_LIKE && operand1.qs.getStoreManager().getDatastoreAdapter().supportsEscapeExpressionInLikePredicate())
            {
                if( operand2 instanceof Literal )
                {
                    st.append(' ');
                    st.append(operand1.qs.getStoreManager().getDatastoreAdapter().getEscapePatternExpression());
                    st.append(' ');
                }
            }
        }
    }

    /**
     * Mutator to set the parameter name that this expression/literal represents.
     * @param paramName Name of the parameter
     */
    public void setParameterName(String paramName)
    {
        this.parameterName = paramName;
    }

    /**
     * Accessor for the parameter name that this expression/literal represents (if any).
     * @return Name of the parameter represented by this expression
     */
    public String getParameterName()
    {
        return parameterName;
    }

    /**
     * Accessor for whether this expression/literal represents a parameter.
     * @return Whether a parameter is being represented.
     */
    public boolean isParameter()
    {
        return parameterName != null;
    }

    /**
     * Method to mark this expression/literal as needing checking for type assignability.
     * TODO Make this part of ObjectManager and allow ScalarExpression to reach the ObjectManager
     * so we don't need to propagate the info
     */
    public void checkForTypeAssignability()
    {
        checkForTypeAssignability = true;
    }

    /**
     * Accessor for the query expression.
     * @return The query expression
     */
    public QueryExpression getQueryExpression()
    {
        return qs;
    }

    /**
     * Accessor for the table expression being used by this expression.
     * @return Returns the table expression for this expression.
     */
    public LogicSetExpression getLogicSetExpression()
    {
        return te;
    }

    /**
     * Conditional And. Evaluates its right-hand operand only if the value of its left-hand operand is true.
     * @param expr the right-hand operand
     * @return the result value is true if both operand values are true; otherwise, the result is false.
     */
    public BooleanExpression and(ScalarExpression expr)
    {
        throw new IllegalOperationException(this, "&&", expr);
    }

    /**
     * Exclusive OR
     * @param expr the right-hand operand
     * @return the result value is the bitwise exclusive OR of the operand values.
     */
    public BooleanExpression eor(ScalarExpression expr)
    {
        throw new IllegalOperationException(this, "^", expr);
    }

    /**
     * Conditional OR. Evaluates its right-hand operand only if the value of its left-hand operand is false.
     * @param expr the right-hand operand
     * @return the result value is false if both operand values are false; otherwise, the result is true.
     */
    public BooleanExpression ior(ScalarExpression expr)
    {
        throw new IllegalOperationException(this, "||", expr);
    }

    /**
     * Logical complement
     * @return the result value is false if operand is true; otherwise, the result is true.
     */   
    public BooleanExpression not()
    {
        throw new IllegalOperationException("!", this);
    }
   
    /**
     * Equality operator (equals to)
     * @param expr the right-hand operand
     * @return The type of an equality expression is a boolean
     */
    public BooleanExpression eq(ScalarExpression expr)
    {
        if (this instanceof UnknownLiteral || expr instanceof UnknownLiteral)
        {
            return new BooleanLiteral(qs, mapping, true);
        }
        throw new IllegalOperationException(this, "==", expr);
    }

    /**
     * Equality operator (not equals to)
     * @param expr the right-hand operand
     * @return The type of an equality expression is a boolean
     */
    public BooleanExpression noteq(ScalarExpression expr)
    {
        if (this instanceof UnknownLiteral || expr instanceof UnknownLiteral)
        {
            return new BooleanLiteral(qs, mapping, true);
        }
        throw new IllegalOperationException(this, "!=", expr);
    }

    /**
     * Relational operator (lower than)
     * @param expr the right-hand operand
     * @return true if the value of the left-hand operand is less than the value of the right-hand operand, and otherwise is false.
     */   
    public BooleanExpression lt(ScalarExpression expr)
    {
        if (this instanceof UnknownLiteral || expr instanceof UnknownLiteral)
        {
            return new BooleanLiteral(qs, mapping, true);
        }
        throw new IllegalOperationException(this, "<", expr);
    }

    /**
     * Relational operator (lower than or equals)
     * @param expr the right-hand operand
     * @return true if the value of the left-hand operand is less than or equal to the value of the right-hand operand, and otherwise is false.
     */   
    public BooleanExpression lteq(ScalarExpression expr)
    {
        if (this instanceof UnknownLiteral || expr instanceof UnknownLiteral)
        {
            return new BooleanLiteral(qs, mapping, true);
        }
        throw new IllegalOperationException(this, "<=", expr);
    }

    /**
     * Relational operator (greater than)
     * @param expr the right-hand operand
     * @return true if the value of the left-hand operand is greater than the value of the right-hand operand, and otherwise is false.
     */   
    public BooleanExpression gt(ScalarExpression expr)
    {
        if (this instanceof UnknownLiteral || expr instanceof UnknownLiteral)
        {
            return new BooleanLiteral(qs, mapping, true);
        }
        throw new IllegalOperationException(this, ">", expr);
    }

    /**
     * Relational operator (greater than or equals)
     * @param expr the right-hand operand
     * @return true if the value of the left-hand operand is greater than or equal the value of the right-hand operand, and otherwise is false.
     */   
    public BooleanExpression gteq(ScalarExpression expr)
    {
        if (this instanceof UnknownLiteral || expr instanceof UnknownLiteral)
        {
            return new BooleanLiteral(qs, mapping, true);
        }
        throw new IllegalOperationException(this, ">=", expr);
    }

    /**
     * Type Comparison Operator instanceof
     * @param expr the right-hand ReferenceType expression
     * @return true if the value of the RelationalExpression is not null and the reference could be cast to the ReferenceType without raising a ClassCastException. Otherwise the result is false.
     */
    public BooleanExpression instanceOf(ScalarExpression expr)
    {
        if (this instanceof UnknownLiteral || expr instanceof UnknownLiteral)
        {
            return new BooleanLiteral(qs, mapping, true);
        }
        throw new IllegalOperationException(this, "instanceof", expr);
    }

    /**
     * In expression. Return true if this is contained by <code>expr</code>
     * @param expr the right-hand expression
     * @return true if the left-hand expression is contained by the right-hand expression. Otherwise the result is false.
     */   
    public BooleanExpression in(ScalarExpression expr)
    {
        if (this instanceof UnknownLiteral || expr instanceof UnknownLiteral)
        {
            return new BooleanLiteral(qs, mapping, true);
        }
        throw new IllegalOperationException(this, "in", expr);
    }

    /**
     * Additive Operator. The binary + operator performs addition when applied to two operands of numeric type, producing the sum of the operands. If the type of either operand of a + operator is String, then the operation is string concatenation.
     * @param expr the right-hand operand
     * @return If one of the operands is String, the returned value is the string concatenation; The sum of two operands of numeric type. The left-hand operand is the minuend and the right-hand operand is the subtrahend;
     */
    public ScalarExpression add(ScalarExpression expr)
    {
        if (this instanceof UnknownLiteral || expr instanceof UnknownLiteral)
        {
            return new FloatingPointLiteral(qs, mapping, new Double(0.0));
        }
        throw new IllegalOperationException(this, "+", expr);
    }

    /**
     * Additive Operator. The binary - operator subtracts right-hand operand from left-hand operand.
     * @param expr the right-hand operand
     * @return The binary - operator performs subtraction when applied to two operands of numeric type producing the difference of its operands; the left-hand operand is the minuend and the right-hand operand is the subtrahend.
     */
    public ScalarExpression sub(ScalarExpression expr)
    {
        if (this instanceof UnknownLiteral || expr instanceof UnknownLiteral)
        {
            return new FloatingPointLiteral(qs, mapping, new Double(0.0));
        }
        throw new IllegalOperationException(this, "-", expr);
    }

    /**
     * Multiplication Operator
     * @param expr the right-hand operator
     * @return The binary * operator performs multiplication, producing the product of its operands.
     */
    public ScalarExpression mul(ScalarExpression expr)
    {
        if (this instanceof UnknownLiteral || expr instanceof UnknownLiteral)
        {
            return new FloatingPointLiteral(qs, mapping, new Double(0.0));
        }
        throw new IllegalOperationException(this, "*", expr);
    }

    /**
     * Division Operator. The left-hand operand is the dividend and the right-hand operand is the divisor.
     * @param expr the right-hand operator
     * @return The binary / operator performs division, producing the quotient of its operands
     */
    public ScalarExpression div(ScalarExpression expr)
    {
        if (this instanceof UnknownLiteral || expr instanceof UnknownLiteral)
        {
            return new FloatingPointLiteral(qs, mapping, new Double(0.0));
        }
        throw new IllegalOperationException(this, "/", expr);
    }

    /**
     * Remainder Operator. The left-hand operand is the dividend and the right-hand operand is the divisor.
     * @param expr the right-hand operator
     * @return The binary % operator is said to yield the remainder of its operands from an implied division
     */
    public ScalarExpression mod(ScalarExpression expr)
    {
        if (this instanceof UnknownLiteral || expr instanceof UnknownLiteral)
        {
            return new FloatingPointLiteral(qs, mapping, new Double(0.0));
        }
        throw new IllegalOperationException(this, "%", expr);
    }

    /**
     * Unary Minus Operator
     * @return the type of the unary minus expression is the promoted type of the operand.
     */
    public ScalarExpression neg()
    {
        throw new IllegalOperationException("-", this);
    }

    /**
     * Bitwise Complement Operator
     * @return the type of the unary bitwise complement expression is the promoted type of the operand.
     */
    public ScalarExpression com()
    {
        throw new IllegalOperationException("~", this);
    }

    /**
     * A cast expression converts, at run time, a value of one type to a similar value of another type;
     * or confirms, at compile time, that the type of an expression is boolean; or checks, at run time, that a reference
     * value refers to an object whose class is compatible with a specified reference type.
     * The type of the operand expression must be converted to the type explicitly named by the cast operator.
     * @param type the type named by the cast operator
     * @return the converted value
     */
    public ScalarExpression cast(Class type)
    {
        throw new IllegalOperationException("cast to " + type.getName(), this);
    }

    /**
     * A field access expression may access a field of an object or array, a reference to which is the value of an expression
     * @param fieldName the field identifier
     * @param innerJoin true if in datastore to use inner joins; false to use left joins
     * @return the field expression
     */
    public ScalarExpression accessField(String fieldName, boolean innerJoin)
    {
        throw new IllegalOperationException("access field " + fieldName, this);
    }

    /**
     * Define a new identifier for this expression
     * @param aliasIdentifier the alias
     * @return this
     */
    public ScalarExpression as(String aliasIdentifier)
    {
        if (this.aliasIdentifier == null)
        {
            this.aliasIdentifier = aliasIdentifier;
            nonAliasExpression = st.toString();
            st.postpend(" AS " + aliasIdentifier);
            expressionList.updateExpressionAlias();
        }
        return this;
    }

    /**
     * Accessor for the alias (if any).
     * @return Returns the alias.
     */
    public String getAlias()
    {
        return aliasIdentifier;
    }

    /**
     * Accessor for the expression without any alias.
     * @return Returns the nonAliasExpression.
     */
    public String getNonAliasExpression()
    {
        return nonAliasExpression;
    }

    /*
     * This can be enlarged as needed, but for the present there are no methods
     * accepting more than 7 arguments.
     */
    private static final Class[][] PARAM_TYPE_SIGNATURES =
        {
            null,
            new Class[] { ScalarExpression.class },
            new Class[] { ScalarExpression.class, ScalarExpression.class },
            new Class[] { ScalarExpression.class, ScalarExpression.class, ScalarExpression.class },
            new Class[] { ScalarExpression.class, ScalarExpression.class, ScalarExpression.class, ScalarExpression.class },
            new Class[] { ScalarExpression.class, ScalarExpression.class, ScalarExpression.class, ScalarExpression.class, ScalarExpression.class },
            new Class[] { ScalarExpression.class, ScalarExpression.class, ScalarExpression.class, ScalarExpression.class, ScalarExpression.class, ScalarExpression.class },
            new Class[] { ScalarExpression.class, ScalarExpression.class, ScalarExpression.class, ScalarExpression.class, ScalarExpression.class, ScalarExpression.class, ScalarExpression.class }
        };

    /**
     * Invoke a function in a expression. The scalar expression must have
     * a method with the signature "<code>methodName</code>Method([ScalarExpression argument1, [ScalarExpression argument2],...])".
     * The number of arguments is equivalent to the size of the <code>arguments</code> list.
     * @param methodName the function name
     * @param arguments the arguments
     * @return the result returned when invoking the function
     */
    public ScalarExpression callMethod(String methodName, List arguments)
    {
        ScalarExpression expr;

        try
        {
            int numArgs = arguments.size();
            if (numArgs >= PARAM_TYPE_SIGNATURES.length)
            {
                throw new NoSuchMethodException(methodName + typeList(arguments));
            }

            boolean hasArrayArgument = false;
            for( int i=0; i<arguments.size(); i++)
            {
                if( arguments.get(i) instanceof ArrayExpression )
                {
                    hasArrayArgument = true;
                    break;
                }
            }
            Method m;
            if( !hasArrayArgument )
            {
                m = getClass().getMethod(methodName + "Method", PARAM_TYPE_SIGNATURES[numArgs]);
                expr = (ScalarExpression)m.invoke(this, arguments.toArray());
            }
            else
            {
                Class[] argTypes = new Class[arguments.size()];
                Object[] args = new Object[arguments.size()];
                for( int i=0; i<arguments.size(); i++)
                {
                    if( arguments.get(i) instanceof ArrayExpression )
                    {
                        argTypes[i] = new ScalarExpression[0].getClass();
                        args[i] = ((ArrayExpression)arguments.get(i)).exprs;
                    }
                    else
                    {
                        argTypes[i] = ScalarExpression.class;
                        args[i] = arguments.get(i);
                    }
                }
                m = getClass().getMethod(methodName + "Method", argTypes);
                expr = (ScalarExpression)m.invoke(this, args);
            }
        }
        catch (InvocationTargetException e)
        {
            Throwable t = e.getTargetException();

            if (t instanceof Error)
            {
                throw (Error)t;
            }
            if (t instanceof JPOXException)
            {
                throw (JPOXException)t;
            }
            else
            {
                throw new MethodInvocationException(methodName, arguments, t);
            }
        }
        catch (Exception e)
        {
            throw new MethodInvocationException(methodName, arguments, e);
        }

        return expr;
    }

    /**
     * Method to request the enclosure of this expression within parentheses
     * @return the enclosed expression
     */
    public ScalarExpression encloseWithInParentheses()
    {
        st.encloseWithInParentheses();
        return this;
    }

    /**
     * StatementText representation of this expression. I.E. A Boolean field may be stored in
     * boolean format like 0 or 1, and it can also be stored in other formats, like Y or N, TRUE or FALSE, and so on.
     * The projection mode for the boolean field is the real content of the value stored, (e.g. Y or N), and opposed to
     * that the filter mode for the boolean field is always represented by a boolean expression (e.g. Y=Y or N=N)
     * In SQL, the projection can be exemplified as "SELECT BOOLEAN_FIELD ... " and the filter as
     * "SELECT COLUMNS ... WHERE BOOLEAN_FIELD ='Y'"
     *
     * @param mode (0=PROJECTION;1=FILTER)
     * @return the StatementText
     */
    public StatementText toStatementText(int mode)
    {
        return st;
    }

    /**
     * Equality operator providing a simple comparison of expressions.
     * @param o The other object
     * @return Whether they are equal
     */
    public boolean equals(Object o)
    {
        if (o == null)
        {
            return false;
        }
        if (o == this)
        {
            return true;
        }
        if (!(o instanceof ScalarExpression))
        {
            return false;
        }

        // Just compare if the statement text is the same, removing any ALIAS
        String thisString = toStatementText(ScalarExpression.FILTER).toString();
        String otherString = ((ScalarExpression)o).toStatementText(ScalarExpression.FILTER).toString();
        if (thisString.indexOf(" AS ") > 0)
        {
            thisString = thisString.substring(0, thisString.indexOf(" AS "));
        }
        if (otherString.indexOf(" AS ") > 0)
        {
            otherString = otherString.substring(0, otherString.indexOf(" AS "));
        }

        return thisString.equals(otherString);
    }

    public String toString()
    {
        String value;

        try
        {
            value = " \"" + toStatementText(ScalarExpression.FILTER).toString() + '"';
        }
        catch (JPOXException e)
        {
            value = "";
        }

        String className = getClass().getName();

        return className.substring(className.lastIndexOf('.') + 1) + value;
    }

    private static String typeList(List exprs)
    {
        StringBuffer s = new StringBuffer("(");
        Iterator i = exprs.iterator();

        while (i.hasNext())
        {
            s.append(i.next()).append(i.hasNext() ? ',' : ')');
        }

        return s.toString();
    }

    /**
     * Method to check if the passed expression when being a parameter is of a valid type for
     * comparison. Throws a JPOXQueryInvalidParametersException if not of the same type.
     * @param expr The other expression
     */
    protected void assertValidTypeForParameterComparison(ScalarExpression expr, Class type)
    {
        if ((checkForTypeAssignability || expr.checkForTypeAssignability) &&
            expr.isParameter() && !type.isAssignableFrom(expr.getClass()))
        {
            throw new JPOXQueryInvalidParametersException(LOCALISER.msg("021077",
                expr.getParameterName(), getClass().getName(), expr.getClass().getName()));
        }
    }

    /**
     * Convenience method to return a ScalarExpression that we can use to compare against this object.
     * Returns the input expression unless it is for a parameter and the parameter expression has a different
     * datastore mapping (the default mapping whereas this has had its datastore mapping specially
     * selected).
     * @param expr The expression to check
     * @return The expression to use
     */
    protected ScalarExpression getConsistentTypeForParameterComparison(ScalarExpression expr)
    {
        if (expr.isParameter() && !isParameter() &&
            mapping != null && expr.getMapping() != null &&
            mapping.getDataStoreMapping(0).getClass() != expr.getMapping().getDataStoreMapping(0).getClass() &&
            ((Literal)expr).getRawValue() != null)
        {
            // TODO We should make the check in the opposite direction too but that would need to go in each method
            // Parameter literal with different datastore mapping, so replace with one with the same datastore mapping
            // Only replace if the literal has its raw value (TODO is this restriction needed ?)
            expr = mapping.newLiteral(qs, ((Literal)expr).getRawValue());
        }
        return expr;
    }

    /**
     * Accessor for the mapping for this expression (if any).
     * @return Returns the mapping.
     */
    public JavaTypeMapping getMapping()
    {
        return mapping;
    }

    /**
     * Ordered list of expressions comma separated
     **/
    public class ExpressionList
    {
        private List expressions = new ArrayList();

        /**
         * Add a new Expression to the list
         * @param expression in RDBMS datastore expressions can be columns
         */
        public void addExpression(ScalarExpression expression)
        {
            expressions.add(expression);
        }

        /**
         * Convenience method for the situation where the alias of a single-field expression has been
         * updated since adding the expression to the list. Does nothing when there are more than 1
         * expression in the list.
         */
        public void updateExpressionAlias()
        {
            if (expressions.size() == 1)
            {
                ((ScalarExpression)expressions.get(0)).as(aliasIdentifier);
            }
        }

        /**
         * Gets the expression in the index
         * @param index the position for the expression in the list of expressions
         * @return the expression in the index
         */
        public ScalarExpression getExpression(int index)
        {
            return (ScalarExpression) expressions.get(index);
        }
   
        /**
         * Number of expressions enclosed in this Scalar Expression
         * @return number of expressions enclosed in this Scalar Expression
         */
        public int size()
        {
            return expressions.size();
        }
       
        /**
         * Returns an string with a serie of expression comma separated.
         * BNF notation: [ expr [, expr] ]
         * @return the expression list comma separated
         */
        public String toString()
        {
            StringBuffer expr = new StringBuffer();
            for( int i=0; i<expressions.size(); i++)
            {
                expr.append(getExpression(i).toString());
                if( i<(expressions.size()-1))
                {
                    expr.append(',');
                }
            }
            return expr.toString();
        }
       
        /**
         * Return an array of nested expressions
         * @return ScalarExpression[]
         */
        public ScalarExpression[] toArray()
        {
            return (ScalarExpression[]) expressions.toArray(new ScalarExpression[expressions.size()]);
        }
    }
   
    /**
     * Returns the expression list.
     * @return The expressions list with atomic expressions (ScalarExpression.FieldExpression). A expression in this list can't be split down
     */
    public ExpressionList getExpressionList()
    {
        return expressionList;
    }

    /**
     * A field expression represents a storage location for the value with an associated type
     */
    public static class DatastoreFieldExpression extends ScalarExpression
    {
        DatastoreField field;
       
        /**
         *
         * @param qs
         * @param field
         * @param te
         */
        public DatastoreFieldExpression(QueryExpression qs, DatastoreField field, LogicSetExpression te)
        {
            super(qs);
            this.field = field;
            this.te = te;
            st.append(te.referenceColumn(field));
        }
       
        public BooleanExpression eq(ScalarExpression expr)
        {
            return new BooleanExpression(expr,OP_EQ,this);
        }

        public BooleanExpression noteq(ScalarExpression expr)
        {
            return new BooleanExpression(expr,OP_NOTEQ,this);
        }
       
        public int hashCode()
        {
            return te.hashCode() ^ field.hashCode();
        }

        public boolean equals(Object o)
        {
            if (o == this)
            {
                return true;
            }

            if (!(o instanceof DatastoreFieldExpression))
            {
                return false;
            }

            DatastoreFieldExpression qsc = (DatastoreFieldExpression)o;

            return te.equals(qsc.te) && field.equals(qsc.field);
        }

        public String toString()
        {
            if (aliasIdentifier != null)
            {
                return te.referenceColumn(field) + " AS " + aliasIdentifier;
            }
            else
            {
                return te.referenceColumn(field);
            }
        }
    }
   
    /**
     * An illegal argument error represents method invocations with unsupported/invalid argument types
     **/
    public static class IllegalArgumentTypeException extends IllegalArgumentException
    {
        /**
         * Constructor
         * @param arg the illegal expression for the method
         */
        public IllegalArgumentTypeException(ScalarExpression arg)
        {
            super("Illegal argument type: " + arg);
        }
    }

    /**
     * A method invocation error represents an effort to invoke a operation on a expression
     **/
    public static class MethodInvocationException extends JPOXUserException
    {
        /**
         * Constructor
         * @param methodName the function name
         * @param arguments the arguments
         * @param t the nested exception
         */
        public MethodInvocationException(String methodName, List arguments, Throwable t)
        {
            super("Exception occurred invoking method " + methodName + typeList(arguments), t);
        }
    }

    /**
     * Inner class representing an illegal operation.
     **/
    public static class IllegalOperationException extends JPOXUserException
    {
        /**
         * Constructor
         * @param operation the operation. It may include a descriptive localised error message
         * @param operand the operand
         */
        public IllegalOperationException(String operation, ScalarExpression operand)
        {
            super("Cannot perform operation \"" + operation + "\" on " + operand);
        }

        /**
         * Constructor
         * @param operation the operation. It may include a descriptive error message
         * @param operand1 the left-hand operand
         * @param operand2 the right-hand operand
         */
        public IllegalOperationException(ScalarExpression operand1, String operation, ScalarExpression operand2)
        {
            super("Cannot perform operation \"" + operation + "\" on " + operand1 + " and " + operand2);
        }
    }
}
TOP

Related Classes of org.jpox.store.mapped.expression.ScalarExpression

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.