0001: /**********************************************************************
0002: Copyright (c) 2002 Mike Martin (TJDO) and others. All rights reserved.
0003: Licensed under the Apache License, Version 2.0 (the "License");
0004: you may not use this file except in compliance with the License.
0005: You may obtain a copy of the License at
0006:
0007: http://www.apache.org/licenses/LICENSE-2.0
0008:
0009: Unless required by applicable law or agreed to in writing, software
0010: distributed under the License is distributed on an "AS IS" BASIS,
0011: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0012: See the License for the specific language governing permissions and
0013: limitations under the License.
0014:
0015:
0016: Contributors:
0017: 2003 Andy Jefferson - commented
0018: ...
0019: **********************************************************************/package org.jpox.store.expression;
0020:
0021: import java.lang.reflect.InvocationTargetException;
0022: import java.lang.reflect.Method;
0023: import java.util.ArrayList;
0024: import java.util.Iterator;
0025: import java.util.List;
0026: import java.util.ListIterator;
0027:
0028: import org.jpox.exceptions.JPOXException;
0029: import org.jpox.exceptions.JPOXUserException;
0030: import org.jpox.store.DatastoreField;
0031: import org.jpox.store.mapping.JavaTypeMapping;
0032: import org.jpox.store.query.JPOXQueryInvalidParametersException;
0033: import org.jpox.store.query.StatementText;
0034: import org.jpox.util.Localiser;
0035:
0036: /**
0037: * A Scalar expression in a Query. Used to compute values with a resulting type
0038: *
0039: * @version $Revision: 1.34 $
0040: **/
0041: public abstract class ScalarExpression {
0042: /** Localiser for messages */
0043: protected static final Localiser LOCALISER = Localiser
0044: .getInstance("org.jpox.store.Localisation");
0045:
0046: /**
0047: * when translated to StatementText, the expression is generated to
0048: * be projected as result of executing a statement
0049: **/
0050: public static int PROJECTION = 0;
0051:
0052: /**
0053: * when translated to StatementText, the expression is generated to
0054: * be used as filter to a executing statement
0055: **/
0056: public static int FILTER = 1;
0057:
0058: /**
0059: * Inner class representing an Operator
0060: **/
0061: protected static class Operator {
0062: protected final String symbol;
0063: protected final int precedence;
0064:
0065: /**
0066: * Operator
0067: * @param symbol the source text or symbol of an operator. e.g =, ==, +, /, >, <, etc
0068: * @param precedence the order of precedence where the expression is compiled
0069: */
0070: public Operator(String symbol, int precedence) {
0071: this .symbol = symbol;
0072: this .precedence = precedence;
0073: }
0074:
0075: public String toString() {
0076: return symbol;
0077: }
0078: }
0079:
0080: /**
0081: * "Monadic" operator performs a function on one operand.
0082: * It can be in either postfix or prefix notation.
0083: * <ul>
0084: * <li>
0085: * Prefix notation means that the operator appears before its operand. <i>operator operand</i>
0086: * </li>
0087: * <li>
0088: * Postfix notation means that the operator appears after its operand. <i>operand operator</i>
0089: * </li>
0090: * </ul>
0091: **/
0092: protected static class MonadicOperator extends Operator {
0093: /**
0094: * Monodiac operator
0095: * @param symbol the source text or symbol of an operator. e.g =, ==, +, /, >, <, etc
0096: * @param precedence the order of precedence where the expression is compiled
0097: */
0098: public MonadicOperator(String symbol, int precedence) {
0099: super (symbol, precedence);
0100: }
0101:
0102: /**
0103: * Check if this operator has higher precedence than <code>op</code>
0104: * @param op the operator
0105: * @return true if this operation is higher
0106: */
0107: public boolean isHigherThan(Operator op) {
0108: if (op == null) {
0109: return false;
0110: } else {
0111: return precedence > op.precedence;
0112: }
0113: }
0114: }
0115:
0116: /**
0117: * "Dyadic" operator performs operation on one or two operands
0118: **/
0119: protected static class DyadicOperator extends Operator {
0120: /**
0121: * An associative operator is one for which parentheses can be inserted and removed without changing the meaning of the expression
0122: */
0123: private final boolean isAssociative;
0124:
0125: /**
0126: * Dyadic operator
0127: * @param symbol the source text or symbol of an operator. e.g =, ==, +, /, >, <, etc
0128: * @param precedence the order of precedence where the expression is compiled
0129: * @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
0130: */
0131: public DyadicOperator(String symbol, int precedence,
0132: boolean isAssociative) {
0133: super (" " + symbol + " ", precedence);
0134: this .isAssociative = isAssociative;
0135: }
0136:
0137: /**
0138: * Checks if this operation is higher than operator <code>op</code> in left side of the expression
0139: * @param op the operator in the left side of the expression
0140: * @return true if this operation is higher than operator <code>op</code> in left side of the expression
0141: */
0142: public boolean isHigherThanLeftSide(Operator op) {
0143: if (op == null) {
0144: return false;
0145: } else {
0146: return precedence > op.precedence;
0147: }
0148: }
0149:
0150: /**
0151: * Checks if this operation is higher than operator <code>op</code> in right side of the expression
0152: * @param op the operator in the right side of the expression
0153: * @return true if this operation is higher than operator <code>op</code> in right side of the expression
0154: */
0155: public boolean isHigherThanRightSide(Operator op) {
0156: if (op == null) {
0157: return false;
0158: } else if (precedence == op.precedence) {
0159: return !isAssociative;
0160: } else {
0161: return precedence > op.precedence;
0162: }
0163: }
0164: }
0165:
0166: /** OR **/
0167: public static final DyadicOperator OP_OR = new DyadicOperator("OR",
0168: 0, true);
0169: /** AND **/
0170: public static final DyadicOperator OP_AND = new DyadicOperator(
0171: "AND", 1, true);
0172: /** NOT **/
0173: public static final MonadicOperator OP_NOT = new MonadicOperator(
0174: "NOT ", 2);
0175: /** EQ **/
0176: public static final DyadicOperator OP_EQ = new DyadicOperator("=",
0177: 3, false);
0178: /** NOTEQ **/
0179: public static final DyadicOperator OP_NOTEQ = new DyadicOperator(
0180: "<>", 3, false);
0181: /** LT **/
0182: public static final DyadicOperator OP_LT = new DyadicOperator("<",
0183: 3, false);
0184: /** LTEQ **/
0185: public static final DyadicOperator OP_LTEQ = new DyadicOperator(
0186: "<=", 3, false);
0187: /** GT **/
0188: public static final DyadicOperator OP_GT = new DyadicOperator(">",
0189: 3, false);
0190: /** GTEQ **/
0191: public static final DyadicOperator OP_GTEQ = new DyadicOperator(
0192: ">=", 3, false);
0193: /** LIKE **/
0194: public static final DyadicOperator OP_LIKE = new DyadicOperator(
0195: "LIKE", 3, false);
0196: /** NOT LIKE **/
0197: public static final DyadicOperator OP_NOTLIKE = new DyadicOperator(
0198: "NOT LIKE", 3, false);
0199: /** BETWEEN **/
0200: public static final DyadicOperator OP_BETWEEN = new DyadicOperator(
0201: "BETWEEN", 3, false);
0202: /** IS **/
0203: public static final DyadicOperator OP_IS = new DyadicOperator("IS",
0204: 3, false);
0205: /** ISNOT **/
0206: public static final DyadicOperator OP_ISNOT = new DyadicOperator(
0207: "IS NOT", 3, false);
0208: /** IN **/
0209: public static final DyadicOperator OP_IN = new DyadicOperator("IN",
0210: 3, false);
0211: /** NOT IN **/
0212: public static final DyadicOperator OP_NOTIN = new DyadicOperator(
0213: "NOT IN", 3, false);
0214: /** ADD **/
0215: public static final DyadicOperator OP_ADD = new DyadicOperator("+",
0216: 4, true);
0217: /** SUB **/
0218: public static final DyadicOperator OP_SUB = new DyadicOperator("-",
0219: 4, false);
0220: /** CONCAT **/
0221: public static final DyadicOperator OP_CONCAT = new DyadicOperator(
0222: "||", 4, true);
0223: /** MUL **/
0224: public static final DyadicOperator OP_MUL = new DyadicOperator("*",
0225: 5, true);
0226: /** DIV **/
0227: public static final DyadicOperator OP_DIV = new DyadicOperator("/",
0228: 5, false);
0229: /** MOD **/
0230: public static final DyadicOperator OP_MOD = new DyadicOperator("%",
0231: 5, false);
0232: /** NEG **/
0233: public static final MonadicOperator OP_NEG = new MonadicOperator(
0234: "-", 6);
0235: /** COM **/
0236: public static final MonadicOperator OP_COM = new MonadicOperator(
0237: "~", 6);
0238:
0239: /** The Query Expression. */
0240: protected final QueryExpression qs;
0241:
0242: /** The table expression being used. */
0243: protected LogicSetExpression te;
0244:
0245: /** The Statement Text for this expression. */
0246: protected final StatementText st = new StatementText();
0247:
0248: protected Operator lowestOperator = null;
0249:
0250: /** alias identifier for this expression **/
0251: protected String aliasIdentifier;
0252:
0253: /** The expression string without any alias. */
0254: private String nonAliasExpression;
0255:
0256: /** Mapping for the field. */
0257: protected JavaTypeMapping mapping;
0258:
0259: /** List of sub-expressions. Used where we have a field that has multiple datastore mappings (one subexpression for each). */
0260: protected ExpressionList expressionList = new ExpressionList();
0261:
0262: /** Name of a parameter that this expression/literal represents (if any). */
0263: protected String parameterName = null;
0264:
0265: /** Flag whether we should make checks for type assignability (currently for JPA). */
0266: protected boolean checkForTypeAssignability = false;
0267:
0268: /**
0269: * Constructor.
0270: * @param qs The Query Expression
0271: */
0272: protected ScalarExpression(QueryExpression qs) {
0273: this .qs = qs;
0274: }
0275:
0276: /**
0277: * Constructor for an expression of the mapping in the specified table.
0278: * Creates a list of (field) expressions for the datastore mappings of the specified field.
0279: * @param qs The Query Expression
0280: * @param mapping The field mapping
0281: * @param te The table
0282: */
0283: protected ScalarExpression(QueryExpression qs,
0284: JavaTypeMapping mapping, LogicSetExpression te) {
0285: this (qs);
0286: this .mapping = mapping;
0287: this .te = te;
0288: for (int i = 0; i < mapping.getNumberOfDatastoreFields(); i++) {
0289: DatastoreFieldExpression fieldExpr = new DatastoreFieldExpression(
0290: qs, mapping.getDataStoreMapping(i)
0291: .getDatastoreField(), te);
0292: expressionList.addExpression(fieldExpr);
0293: }
0294: st.append(expressionList.toString());
0295: }
0296:
0297: /**
0298: * Generates statement as e.g. FUNCTION_NAME(arg[,argN])
0299: * @param functionName Name of the function
0300: * @param args ScalarExpression list
0301: */
0302: protected ScalarExpression(String functionName, List args) {
0303: st.append(functionName).append('(');
0304:
0305: ListIterator i = args.listIterator();
0306: ScalarExpression arg = (ScalarExpression) i.next();
0307: st.append(arg);
0308: qs = arg.qs;
0309:
0310: while (i.hasNext()) {
0311: arg = (ScalarExpression) i.next();
0312: st.append(',').append(arg);
0313: }
0314:
0315: st.append(')');
0316: }
0317:
0318: /**
0319: * Generates statement as e.g. FUNCTION_NAME(arg AS type[,argN as typeN])
0320: * @param functionName Name of function
0321: * @param args ScalarExpression list
0322: * @param types String or ScalarExpression list
0323: */
0324: protected ScalarExpression(String functionName, List args,
0325: List types) {
0326: st.append(functionName).append('(');
0327:
0328: ListIterator i = args.listIterator();
0329: ListIterator iTypes = types.listIterator();
0330: ScalarExpression arg = (ScalarExpression) i.next();
0331: st.append(arg);
0332: st.append(" AS ");
0333: Object type = iTypes.next();
0334: if (type instanceof ScalarExpression) {
0335: st.append((ScalarExpression) type);
0336: } else {
0337: st.append(type.toString());
0338: }
0339: this .qs = arg.qs;
0340:
0341: while (i.hasNext()) {
0342: arg = (ScalarExpression) i.next();
0343: st.append(',').append(arg);
0344: st.append(" AS ");
0345: type = iTypes.next();
0346: if (type instanceof ScalarExpression) {
0347: st.append((ScalarExpression) type);
0348: } else {
0349: st.append(type.toString());
0350: }
0351: }
0352:
0353: st.append(')');
0354: }
0355:
0356: /**
0357: * Perform a function <code>op</code> on <code>operand</code>
0358: * @param op operator
0359: * @param operand operand
0360: */
0361: protected ScalarExpression(MonadicOperator op,
0362: ScalarExpression operand) {
0363: st.append(op.toString());
0364:
0365: if (op.isHigherThan(operand.lowestOperator)) {
0366: st.append('(').append(operand).append(')');
0367: } else {
0368: st.append(operand);
0369: }
0370:
0371: qs = operand.qs;
0372: lowestOperator = op;
0373: mapping = operand.mapping;
0374: }
0375:
0376: /**
0377: * Performs a function on two arguments.
0378: * op(operand1,operand2)
0379: * operand1 op operand2
0380: * @param operand1 the first expression
0381: * @param op the operator between operands
0382: * @param operand2 the second expression
0383: */
0384: protected ScalarExpression(ScalarExpression operand1,
0385: DyadicOperator op, ScalarExpression operand2) {
0386: qs = operand1.qs;
0387: lowestOperator = op;
0388:
0389: if (op == ScalarExpression.OP_CONCAT) {
0390: //enclose within parentheses to make sure the concat expression is first evaluated
0391: st.append(qs.getStoreManager().getDatastoreAdapter()
0392: .concatOperator(operand1, operand2)
0393: .encloseWithInParentheses());
0394: } else {
0395: if (op.isHigherThanLeftSide(operand1.lowestOperator)) {
0396: st.append('(').append(operand1).append(')');
0397: } else {
0398: st.append(operand1);
0399: }
0400:
0401: st.append(op.toString());
0402:
0403: if (op.isHigherThanRightSide(operand2.lowestOperator)) {
0404: st.append('(').append(operand2).append(')');
0405: } else {
0406: st.append(operand2);
0407: }
0408:
0409: // some databases use a ESCAPE expression with LIKE when using an escaped pattern
0410: if (op == ScalarExpression.OP_LIKE
0411: && operand1.qs.getStoreManager()
0412: .getDatastoreAdapter()
0413: .supportsEscapeExpressionInLikePredicate()) {
0414: if (operand2 instanceof Literal) {
0415: st.append(' ');
0416: st.append(operand1.qs.getStoreManager()
0417: .getDatastoreAdapter()
0418: .getEscapePatternExpression());
0419: st.append(' ');
0420: }
0421: }
0422: }
0423: }
0424:
0425: /**
0426: * Mutator to set the parameter name that this expression/literal represents.
0427: * @param paramName Name of the parameter
0428: */
0429: public void setParameterName(String paramName) {
0430: this .parameterName = paramName;
0431: }
0432:
0433: /**
0434: * Accessor for the parameter name that this expression/literal represents (if any).
0435: * @return Name of the parameter represented by this expression
0436: */
0437: public String getParameterName() {
0438: return parameterName;
0439: }
0440:
0441: /**
0442: * Accessor for whether this expression/literal represents a parameter.
0443: * @return Whether a parameter is being represented.
0444: */
0445: public boolean isParameter() {
0446: return parameterName != null;
0447: }
0448:
0449: /**
0450: * Method to mark this expression/literal as needing checking for type assignability.
0451: * TODO Make this part of ObjectManager and allow ScalarExpression to reach the ObjectManager
0452: * so we don't need to propagate the info
0453: */
0454: public void checkForTypeAssignability() {
0455: checkForTypeAssignability = true;
0456: }
0457:
0458: /**
0459: * Accessor for the query expression.
0460: * @return The query expression
0461: */
0462: public QueryExpression getQueryExpression() {
0463: return qs;
0464: }
0465:
0466: /**
0467: * Accessor for the table expression being used by this expression.
0468: * @return Returns the table expression for this expression.
0469: */
0470: public LogicSetExpression getLogicSetExpression() {
0471: return te;
0472: }
0473:
0474: /**
0475: * Conditional And. Evaluates its right-hand operand only if the value of its left-hand operand is true.
0476: * @param expr the right-hand operand
0477: * @return the result value is true if both operand values are true; otherwise, the result is false.
0478: */
0479: public BooleanExpression and(ScalarExpression expr) {
0480: throw new IllegalOperationException(this , "&&", expr);
0481: }
0482:
0483: /**
0484: * Exclusive OR
0485: * @param expr the right-hand operand
0486: * @return the result value is the bitwise exclusive OR of the operand values.
0487: */
0488: public BooleanExpression eor(ScalarExpression expr) {
0489: throw new IllegalOperationException(this , "^", expr);
0490: }
0491:
0492: /**
0493: * Conditional OR. Evaluates its right-hand operand only if the value of its left-hand operand is false.
0494: * @param expr the right-hand operand
0495: * @return the result value is false if both operand values are false; otherwise, the result is true.
0496: */
0497: public BooleanExpression ior(ScalarExpression expr) {
0498: throw new IllegalOperationException(this , "||", expr);
0499: }
0500:
0501: /**
0502: * Logical complement
0503: * @return the result value is false if operand is true; otherwise, the result is true.
0504: */
0505: public BooleanExpression not() {
0506: throw new IllegalOperationException("!", this );
0507: }
0508:
0509: /**
0510: * Equality operator (equals to)
0511: * @param expr the right-hand operand
0512: * @return The type of an equality expression is a boolean
0513: */
0514: public BooleanExpression eq(ScalarExpression expr) {
0515: if (this instanceof UnknownLiteral
0516: || expr instanceof UnknownLiteral) {
0517: return new BooleanLiteral(qs, mapping, true);
0518: }
0519: throw new IllegalOperationException(this , "==", expr);
0520: }
0521:
0522: /**
0523: * Equality operator (not equals to)
0524: * @param expr the right-hand operand
0525: * @return The type of an equality expression is a boolean
0526: */
0527: public BooleanExpression noteq(ScalarExpression expr) {
0528: if (this instanceof UnknownLiteral
0529: || expr instanceof UnknownLiteral) {
0530: return new BooleanLiteral(qs, mapping, true);
0531: }
0532: throw new IllegalOperationException(this , "!=", expr);
0533: }
0534:
0535: /**
0536: * Relational operator (lower than)
0537: * @param expr the right-hand operand
0538: * @return true if the value of the left-hand operand is less than the value of the right-hand operand, and otherwise is false.
0539: */
0540: public BooleanExpression lt(ScalarExpression expr) {
0541: if (this instanceof UnknownLiteral
0542: || expr instanceof UnknownLiteral) {
0543: return new BooleanLiteral(qs, mapping, true);
0544: }
0545: throw new IllegalOperationException(this , "<", expr);
0546: }
0547:
0548: /**
0549: * Relational operator (lower than or equals)
0550: * @param expr the right-hand operand
0551: * @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.
0552: */
0553: public BooleanExpression lteq(ScalarExpression expr) {
0554: if (this instanceof UnknownLiteral
0555: || expr instanceof UnknownLiteral) {
0556: return new BooleanLiteral(qs, mapping, true);
0557: }
0558: throw new IllegalOperationException(this , "<=", expr);
0559: }
0560:
0561: /**
0562: * Relational operator (greater than)
0563: * @param expr the right-hand operand
0564: * @return true if the value of the left-hand operand is greater than the value of the right-hand operand, and otherwise is false.
0565: */
0566: public BooleanExpression gt(ScalarExpression expr) {
0567: if (this instanceof UnknownLiteral
0568: || expr instanceof UnknownLiteral) {
0569: return new BooleanLiteral(qs, mapping, true);
0570: }
0571: throw new IllegalOperationException(this , ">", expr);
0572: }
0573:
0574: /**
0575: * Relational operator (greater than or equals)
0576: * @param expr the right-hand operand
0577: * @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.
0578: */
0579: public BooleanExpression gteq(ScalarExpression expr) {
0580: if (this instanceof UnknownLiteral
0581: || expr instanceof UnknownLiteral) {
0582: return new BooleanLiteral(qs, mapping, true);
0583: }
0584: throw new IllegalOperationException(this , ">=", expr);
0585: }
0586:
0587: /**
0588: * Type Comparison Operator instanceof
0589: * @param expr the right-hand ReferenceType expression
0590: * @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.
0591: */
0592: public BooleanExpression instanceOf(ScalarExpression expr) {
0593: if (this instanceof UnknownLiteral
0594: || expr instanceof UnknownLiteral) {
0595: return new BooleanLiteral(qs, mapping, true);
0596: }
0597: throw new IllegalOperationException(this , "instanceof", expr);
0598: }
0599:
0600: /**
0601: * In expression. Return true if this is contained by <code>expr</code>
0602: * @param expr the right-hand expression
0603: * @return true if the left-hand expression is contained by the right-hand expression. Otherwise the result is false.
0604: */
0605: public BooleanExpression in(ScalarExpression expr) {
0606: if (this instanceof UnknownLiteral
0607: || expr instanceof UnknownLiteral) {
0608: return new BooleanLiteral(qs, mapping, true);
0609: }
0610: throw new IllegalOperationException(this , "in", expr);
0611: }
0612:
0613: /**
0614: * 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.
0615: * @param expr the right-hand operand
0616: * @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;
0617: */
0618: public ScalarExpression add(ScalarExpression expr) {
0619: if (this instanceof UnknownLiteral
0620: || expr instanceof UnknownLiteral) {
0621: return new FloatingPointLiteral(qs, mapping,
0622: new Double(0.0));
0623: }
0624: throw new IllegalOperationException(this , "+", expr);
0625: }
0626:
0627: /**
0628: * Additive Operator. The binary - operator subtracts right-hand operand from left-hand operand.
0629: * @param expr the right-hand operand
0630: * @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.
0631: */
0632: public ScalarExpression sub(ScalarExpression expr) {
0633: if (this instanceof UnknownLiteral
0634: || expr instanceof UnknownLiteral) {
0635: return new FloatingPointLiteral(qs, mapping,
0636: new Double(0.0));
0637: }
0638: throw new IllegalOperationException(this , "-", expr);
0639: }
0640:
0641: /**
0642: * Multiplication Operator
0643: * @param expr the right-hand operator
0644: * @return The binary * operator performs multiplication, producing the product of its operands.
0645: */
0646: public ScalarExpression mul(ScalarExpression expr) {
0647: if (this instanceof UnknownLiteral
0648: || expr instanceof UnknownLiteral) {
0649: return new FloatingPointLiteral(qs, mapping,
0650: new Double(0.0));
0651: }
0652: throw new IllegalOperationException(this , "*", expr);
0653: }
0654:
0655: /**
0656: * Division Operator. The left-hand operand is the dividend and the right-hand operand is the divisor.
0657: * @param expr the right-hand operator
0658: * @return The binary / operator performs division, producing the quotient of its operands
0659: */
0660: public ScalarExpression div(ScalarExpression expr) {
0661: if (this instanceof UnknownLiteral
0662: || expr instanceof UnknownLiteral) {
0663: return new FloatingPointLiteral(qs, mapping,
0664: new Double(0.0));
0665: }
0666: throw new IllegalOperationException(this , "/", expr);
0667: }
0668:
0669: /**
0670: * Remainder Operator. The left-hand operand is the dividend and the right-hand operand is the divisor.
0671: * @param expr the right-hand operator
0672: * @return The binary % operator is said to yield the remainder of its operands from an implied division
0673: */
0674: public ScalarExpression mod(ScalarExpression expr) {
0675: if (this instanceof UnknownLiteral
0676: || expr instanceof UnknownLiteral) {
0677: return new FloatingPointLiteral(qs, mapping,
0678: new Double(0.0));
0679: }
0680: throw new IllegalOperationException(this , "%", expr);
0681: }
0682:
0683: /**
0684: * Unary Minus Operator
0685: * @return the type of the unary minus expression is the promoted type of the operand.
0686: */
0687: public ScalarExpression neg() {
0688: throw new IllegalOperationException("-", this );
0689: }
0690:
0691: /**
0692: * Bitwise Complement Operator
0693: * @return the type of the unary bitwise complement expression is the promoted type of the operand.
0694: */
0695: public ScalarExpression com() {
0696: throw new IllegalOperationException("~", this );
0697: }
0698:
0699: /**
0700: * A cast expression converts, at run time, a value of one type to a similar value of another type;
0701: * or confirms, at compile time, that the type of an expression is boolean; or checks, at run time, that a reference
0702: * value refers to an object whose class is compatible with a specified reference type.
0703: * The type of the operand expression must be converted to the type explicitly named by the cast operator.
0704: * @param type the type named by the cast operator
0705: * @return the converted value
0706: */
0707: public ScalarExpression cast(Class type) {
0708: throw new IllegalOperationException(
0709: "cast to " + type.getName(), this );
0710: }
0711:
0712: /**
0713: * A field access expression may access a field of an object or array, a reference to which is the value of an expression
0714: * @param fieldName the field identifier
0715: * @param innerJoin true if in datastore to use inner joins; false to use left joins
0716: * @return the field expression
0717: */
0718: public ScalarExpression accessField(String fieldName,
0719: boolean innerJoin) {
0720: throw new IllegalOperationException(
0721: "access field " + fieldName, this );
0722: }
0723:
0724: /**
0725: * Define a new identifier for this expression
0726: * @param aliasIdentifier the alias
0727: * @return this
0728: */
0729: public ScalarExpression as(String aliasIdentifier) {
0730: if (this .aliasIdentifier == null) {
0731: this .aliasIdentifier = aliasIdentifier;
0732: nonAliasExpression = st.toString();
0733: st.postpend(" AS " + aliasIdentifier);
0734: expressionList.updateExpressionAlias();
0735: }
0736: return this ;
0737: }
0738:
0739: /**
0740: * Accessor for the alias (if any).
0741: * @return Returns the alias.
0742: */
0743: public String getAlias() {
0744: return aliasIdentifier;
0745: }
0746:
0747: /**
0748: * Accessor for the expression without any alias.
0749: * @return Returns the nonAliasExpression.
0750: */
0751: public String getNonAliasExpression() {
0752: return nonAliasExpression;
0753: }
0754:
0755: /*
0756: * This can be enlarged as needed, but for the present there are no methods
0757: * accepting more than 7 arguments.
0758: */
0759: private static final Class[][] PARAM_TYPE_SIGNATURES = {
0760: null,
0761: new Class[] { ScalarExpression.class },
0762: new Class[] { ScalarExpression.class,
0763: ScalarExpression.class },
0764: new Class[] { ScalarExpression.class,
0765: ScalarExpression.class, ScalarExpression.class },
0766: new Class[] { ScalarExpression.class,
0767: ScalarExpression.class, ScalarExpression.class,
0768: ScalarExpression.class },
0769: new Class[] { ScalarExpression.class,
0770: ScalarExpression.class, ScalarExpression.class,
0771: ScalarExpression.class, ScalarExpression.class },
0772: new Class[] { ScalarExpression.class,
0773: ScalarExpression.class, ScalarExpression.class,
0774: ScalarExpression.class, ScalarExpression.class,
0775: ScalarExpression.class },
0776: new Class[] { ScalarExpression.class,
0777: ScalarExpression.class, ScalarExpression.class,
0778: ScalarExpression.class, ScalarExpression.class,
0779: ScalarExpression.class, ScalarExpression.class } };
0780:
0781: /**
0782: * Invoke a function in a expression. The scalar expression must have
0783: * a method with the signature "<code>methodName</code>Method([ScalarExpression argument1, [ScalarExpression argument2],...])".
0784: * The number of arguments is equivalent to the size of the <code>arguments</code> list.
0785: * @param methodName the function name
0786: * @param arguments the arguments
0787: * @return the result returned when invoking the function
0788: */
0789: public ScalarExpression callMethod(String methodName, List arguments) {
0790: ScalarExpression expr;
0791:
0792: try {
0793: int numArgs = arguments.size();
0794: if (numArgs >= PARAM_TYPE_SIGNATURES.length) {
0795: throw new NoSuchMethodException(methodName
0796: + typeList(arguments));
0797: }
0798:
0799: boolean hasArrayArgument = false;
0800: for (int i = 0; i < arguments.size(); i++) {
0801: if (arguments.get(i) instanceof ArrayExpression) {
0802: hasArrayArgument = true;
0803: break;
0804: }
0805: }
0806: Method m;
0807: if (!hasArrayArgument) {
0808: m = getClass().getMethod(methodName + "Method",
0809: PARAM_TYPE_SIGNATURES[numArgs]);
0810: expr = (ScalarExpression) m.invoke(this , arguments
0811: .toArray());
0812: } else {
0813: Class[] argTypes = new Class[arguments.size()];
0814: Object[] args = new Object[arguments.size()];
0815: for (int i = 0; i < arguments.size(); i++) {
0816: if (arguments.get(i) instanceof ArrayExpression) {
0817: argTypes[i] = new ScalarExpression[0]
0818: .getClass();
0819: args[i] = ((ArrayExpression) arguments.get(i)).exprs;
0820: } else {
0821: argTypes[i] = ScalarExpression.class;
0822: args[i] = arguments.get(i);
0823: }
0824: }
0825: m = getClass().getMethod(methodName + "Method",
0826: argTypes);
0827: expr = (ScalarExpression) m.invoke(this , args);
0828: }
0829: } catch (InvocationTargetException e) {
0830: Throwable t = e.getTargetException();
0831:
0832: if (t instanceof Error) {
0833: throw (Error) t;
0834: }
0835: if (t instanceof JPOXException) {
0836: throw (JPOXException) t;
0837: } else {
0838: throw new MethodInvocationException(methodName,
0839: arguments, t);
0840: }
0841: } catch (Exception e) {
0842: throw new MethodInvocationException(methodName, arguments,
0843: e);
0844: }
0845:
0846: return expr;
0847: }
0848:
0849: /**
0850: * Method to request the enclosure of this expression within parentheses
0851: * @return the enclosed expression
0852: */
0853: public ScalarExpression encloseWithInParentheses() {
0854: st.encloseWithInParentheses();
0855: return this ;
0856: }
0857:
0858: /**
0859: * StatementText representation of this expression. I.E. A Boolean field may be stored in
0860: * 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.
0861: * The projection mode for the boolean field is the real content of the value stored, (e.g. Y or N), and opposed to
0862: * that the filter mode for the boolean field is always represented by a boolean expression (e.g. Y=Y or N=N)
0863: * In SQL, the projection can be exemplified as "SELECT BOOLEAN_FIELD ... " and the filter as
0864: * "SELECT COLUMNS ... WHERE BOOLEAN_FIELD ='Y'"
0865: *
0866: * @param mode (0=PROJECTION;1=FILTER)
0867: * @return the StatementText
0868: */
0869: public StatementText toStatementText(int mode) {
0870: return st;
0871: }
0872:
0873: /**
0874: * Equality operator providing a simple comparison of expressions.
0875: * @param o The other object
0876: * @return Whether they are equal
0877: */
0878: public boolean equals(Object o) {
0879: if (o == null) {
0880: return false;
0881: }
0882: if (o == this ) {
0883: return true;
0884: }
0885: if (!(o instanceof ScalarExpression)) {
0886: return false;
0887: }
0888:
0889: // Just compare if the statement text is the same, removing any ALIAS
0890: String this String = toStatementText(ScalarExpression.FILTER)
0891: .toString();
0892: String otherString = ((ScalarExpression) o).toStatementText(
0893: ScalarExpression.FILTER).toString();
0894: if (this String.indexOf(" AS ") > 0) {
0895: this String = this String.substring(0, this String
0896: .indexOf(" AS "));
0897: }
0898: if (otherString.indexOf(" AS ") > 0) {
0899: otherString = otherString.substring(0, otherString
0900: .indexOf(" AS "));
0901: }
0902:
0903: return this String.equals(otherString);
0904: }
0905:
0906: public String toString() {
0907: String value;
0908:
0909: try {
0910: value = " \""
0911: + toStatementText(ScalarExpression.FILTER)
0912: .toString() + '"';
0913: } catch (JPOXException e) {
0914: value = "";
0915: }
0916:
0917: String className = getClass().getName();
0918:
0919: return className.substring(className.lastIndexOf('.') + 1)
0920: + value;
0921: }
0922:
0923: private static String typeList(List exprs) {
0924: StringBuffer s = new StringBuffer("(");
0925: Iterator i = exprs.iterator();
0926:
0927: while (i.hasNext()) {
0928: s.append(i.next()).append(i.hasNext() ? ',' : ')');
0929: }
0930:
0931: return s.toString();
0932: }
0933:
0934: /**
0935: * Method to check if the passed expression when being a parameter is of a valid type for
0936: * comparison. Throws a JPOXQueryInvalidParametersException if not of the same type.
0937: * @param expr The other expression
0938: */
0939: protected void assertValidTypeForParameterComparison(
0940: ScalarExpression expr, Class type) {
0941: if ((checkForTypeAssignability || expr.checkForTypeAssignability)
0942: && expr.isParameter()
0943: && !type.isAssignableFrom(expr.getClass())) {
0944: throw new JPOXQueryInvalidParametersException(LOCALISER
0945: .msg("021077", expr.getParameterName(), getClass()
0946: .getName(), expr.getClass().getName()));
0947: }
0948: }
0949:
0950: /**
0951: * Convenience method to return a ScalarExpression that we can use to compare against this object.
0952: * Returns the input expression unless it is for a parameter and the parameter expression has a different
0953: * datastore mapping (the default mapping whereas this has had its datastore mapping specially
0954: * selected).
0955: * @param expr The expression to check
0956: * @return The expression to use
0957: */
0958: protected ScalarExpression getConsistentTypeForParameterComparison(
0959: ScalarExpression expr) {
0960: if (expr.isParameter()
0961: && !isParameter()
0962: && mapping != null
0963: && expr.getMapping() != null
0964: && mapping.getDataStoreMapping(0).getClass() != expr
0965: .getMapping().getDataStoreMapping(0).getClass()
0966: && ((Literal) expr).getRawValue() != null) {
0967: // TODO We should make the check in the opposite direction too but that would need to go in each method
0968: // Parameter literal with different datastore mapping, so replace with one with the same datastore mapping
0969: // Only replace if the literal has its raw value (TODO is this restriction needed ?)
0970: expr = mapping.newLiteral(qs, ((Literal) expr)
0971: .getRawValue());
0972: }
0973: return expr;
0974: }
0975:
0976: /**
0977: * Accessor for the mapping for this expression (if any).
0978: * @return Returns the mapping.
0979: */
0980: public JavaTypeMapping getMapping() {
0981: return mapping;
0982: }
0983:
0984: /**
0985: * Ordered list of expressions comma separated
0986: **/
0987: public class ExpressionList {
0988: private List expressions = new ArrayList();
0989:
0990: /**
0991: * Add a new Expression to the list
0992: * @param expression in RDBMS datastore expressions can be columns
0993: */
0994: public void addExpression(ScalarExpression expression) {
0995: expressions.add(expression);
0996: }
0997:
0998: /**
0999: * Convenience method for the situation where the alias of a single-field expression has been
1000: * updated since adding the expression to the list. Does nothing when there are more than 1
1001: * expression in the list.
1002: */
1003: public void updateExpressionAlias() {
1004: if (expressions.size() == 1) {
1005: ((ScalarExpression) expressions.get(0))
1006: .as(aliasIdentifier);
1007: }
1008: }
1009:
1010: /**
1011: * Gets the expression in the index
1012: * @param index the position for the expression in the list of expressions
1013: * @return the expression in the index
1014: */
1015: public ScalarExpression getExpression(int index) {
1016: return (ScalarExpression) expressions.get(index);
1017: }
1018:
1019: /**
1020: * Number of expressions enclosed in this Scalar Expression
1021: * @return number of expressions enclosed in this Scalar Expression
1022: */
1023: public int size() {
1024: return expressions.size();
1025: }
1026:
1027: /**
1028: * Returns an string with a serie of expression comma separated.
1029: * BNF notation: [ expr [, expr] ]
1030: * @return the expression list comma separated
1031: */
1032: public String toString() {
1033: StringBuffer expr = new StringBuffer();
1034: for (int i = 0; i < expressions.size(); i++) {
1035: expr.append(getExpression(i).toString());
1036: if (i < (expressions.size() - 1)) {
1037: expr.append(',');
1038: }
1039: }
1040: return expr.toString();
1041: }
1042:
1043: /**
1044: * Return an array of nested expressions
1045: * @return ScalarExpression[]
1046: */
1047: public ScalarExpression[] toArray() {
1048: return (ScalarExpression[]) expressions
1049: .toArray(new ScalarExpression[expressions.size()]);
1050: }
1051: }
1052:
1053: /**
1054: * Returns the expression list.
1055: * @return The expressions list with atomic expressions (ScalarExpression.FieldExpression). A expression in this list can't be split down
1056: */
1057: public ExpressionList getExpressionList() {
1058: return expressionList;
1059: }
1060:
1061: /**
1062: * A field expression represents a storage location for the value with an associated type
1063: */
1064: public static class DatastoreFieldExpression extends
1065: ScalarExpression {
1066: DatastoreField field;
1067:
1068: /**
1069: *
1070: * @param qs
1071: * @param field
1072: * @param te
1073: */
1074: public DatastoreFieldExpression(QueryExpression qs,
1075: DatastoreField field, LogicSetExpression te) {
1076: super (qs);
1077: this .field = field;
1078: this .te = te;
1079: st.append(te.referenceColumn(field));
1080: }
1081:
1082: public BooleanExpression eq(ScalarExpression expr) {
1083: return new BooleanExpression(expr, OP_EQ, this );
1084: }
1085:
1086: public BooleanExpression noteq(ScalarExpression expr) {
1087: return new BooleanExpression(expr, OP_NOTEQ, this );
1088: }
1089:
1090: public int hashCode() {
1091: return te.hashCode() ^ field.hashCode();
1092: }
1093:
1094: public boolean equals(Object o) {
1095: if (o == this ) {
1096: return true;
1097: }
1098:
1099: if (!(o instanceof DatastoreFieldExpression)) {
1100: return false;
1101: }
1102:
1103: DatastoreFieldExpression qsc = (DatastoreFieldExpression) o;
1104:
1105: return te.equals(qsc.te) && field.equals(qsc.field);
1106: }
1107:
1108: public String toString() {
1109: if (aliasIdentifier != null) {
1110: return te.referenceColumn(field) + " AS "
1111: + aliasIdentifier;
1112: } else {
1113: return te.referenceColumn(field);
1114: }
1115: }
1116: }
1117:
1118: /**
1119: * An illegal argument error represents method invocations with unsupported/invalid argument types
1120: **/
1121: public static class IllegalArgumentTypeException extends
1122: IllegalArgumentException {
1123: /**
1124: * Constructor
1125: * @param arg the illegal expression for the method
1126: */
1127: public IllegalArgumentTypeException(ScalarExpression arg) {
1128: super ("Illegal argument type: " + arg);
1129: }
1130: }
1131:
1132: /**
1133: * A method invocation error represents an effort to invoke a operation on a expression
1134: **/
1135: public static class MethodInvocationException extends
1136: JPOXUserException {
1137: /**
1138: * Constructor
1139: * @param methodName the function name
1140: * @param arguments the arguments
1141: * @param t the nested exception
1142: */
1143: public MethodInvocationException(String methodName,
1144: List arguments, Throwable t) {
1145: super ("Exception occurred invoking method " + methodName
1146: + typeList(arguments), t);
1147: }
1148: }
1149:
1150: /**
1151: * Inner class representing an illegal operation.
1152: **/
1153: public static class IllegalOperationException extends
1154: JPOXUserException {
1155: /**
1156: * Constructor
1157: * @param operation the operation. It may include a descriptive localised error message
1158: * @param operand the operand
1159: */
1160: public IllegalOperationException(String operation,
1161: ScalarExpression operand) {
1162: super ("Cannot perform operation \"" + operation + "\" on "
1163: + operand);
1164: }
1165:
1166: /**
1167: * Constructor
1168: * @param operation the operation. It may include a descriptive error message
1169: * @param operand1 the left-hand operand
1170: * @param operand2 the right-hand operand
1171: */
1172: public IllegalOperationException(ScalarExpression operand1,
1173: String operation, ScalarExpression operand2) {
1174: super ("Cannot perform operation \"" + operation + "\" on "
1175: + operand1 + " and " + operand2);
1176: }
1177: }
1178: }
|