| /* |
| * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0, which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * This Source Code may also be made available under the following Secondary |
| * Licenses when the conditions for such availability set forth in the |
| * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, |
| * version 2 with the GNU Classpath Exception, which is available at |
| * https://www.gnu.org/software/classpath/license.html. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 |
| */ |
| |
| /* |
| * Optimizer.g |
| * |
| * Created on June 11, 2001 |
| */ |
| |
| header |
| { |
| package com.sun.jdo.spi.persistence.support.sqlstore.query.jqlc; |
| |
| import java.util.*; |
| |
| import java.math.BigDecimal; |
| import java.math.BigInteger; |
| |
| import com.sun.jdo.api.persistence.support.JDOFatalUserException; |
| import org.glassfish.persistence.common.I18NHelper; |
| import com.sun.jdo.spi.persistence.support.sqlstore.query.util.type.TypeTable; |
| import com.sun.jdo.spi.persistence.support.sqlstore.query.util.type.Type; |
| import com.sun.jdo.spi.persistence.support.sqlstore.query.util.type.ClassType; |
| import com.sun.jdo.spi.persistence.support.sqlstore.query.util.type.FieldInfo; |
| import com.sun.jdo.spi.persistence.support.sqlstore.query.util.type.NumericType; |
| import com.sun.jdo.spi.persistence.support.sqlstore.query.util.type.NumericWrapperClassType; |
| import com.sun.jdo.spi.persistence.support.sqlstore.query.util.type.NumberType; |
| import com.sun.jdo.spi.persistence.support.sqlstore.query.util.type.StringType; |
| } |
| |
| /** |
| * This class defines the optimizer pass of the JQL compiler. |
| * It takes the typed AST as produced by the smenatic analysis and |
| * converts it into a simpler but equivalent typed AST. |
| * |
| * @author Michael Bouschen |
| * @version 0.1 |
| */ |
| class Optimizer extends TreeParser; |
| |
| options |
| { |
| importVocab = JQL; |
| buildAST = true; |
| defaultErrorHandler = false; |
| ASTLabelType = "JQLAST"; //NOI18N |
| } |
| |
| { |
| /** |
| * I18N support |
| */ |
| protected final static ResourceBundle messages = |
| I18NHelper.loadBundle(Optimizer.class); |
| |
| /** |
| * type table |
| */ |
| protected TypeTable typetab; |
| |
| /** |
| * query parameter table |
| */ |
| protected ParameterTable paramtab; |
| |
| /** |
| * |
| */ |
| protected ErrorMsg errorMsg; |
| |
| /** |
| * |
| */ |
| public void init(TypeTable typetab, ParameterTable paramtab, |
| ErrorMsg errorMsg) |
| { |
| this.typetab = typetab; |
| this.paramtab = paramtab; |
| this.errorMsg = errorMsg; |
| } |
| |
| /** |
| * |
| */ |
| public void reportError(RecognitionException ex) { |
| errorMsg.fatal("Optimizer error", ex); //NOI18N |
| } |
| |
| /** |
| * |
| */ |
| public void reportError(String s) { |
| errorMsg.fatal("Optimizer error: " + s); //NOI18N |
| } |
| |
| /** |
| * Converts the string argument into a single char. |
| */ |
| protected static char parseChar(String text) |
| { |
| char first = text.charAt(0); |
| if (first == '\\') |
| { |
| //found escape => check the next char |
| char second = text.charAt(1); |
| switch (second) |
| { |
| case 'n': return '\n'; |
| case 'r': return '\r'; |
| case 't': return '\t'; |
| case 'b': return '\b'; |
| case 'f': return '\f'; |
| case 'u': |
| // unicode spec |
| return (char)Integer.parseInt(text.substring(2, text.length()), 16); |
| case '0': |
| case '1': |
| case '2': |
| case '3': |
| case '4': |
| case '5': |
| case '6': |
| case '7': |
| // octal spec |
| return (char)Integer.parseInt(text.substring(1, text.length()), 8); |
| default : return second; |
| } |
| } |
| return first; |
| } |
| |
| |
| /** |
| * Check an AND operation (BAND, AND) for constant operands |
| * that could be optimized. |
| * @param op the AND operator |
| * @param left the left operand |
| * @param right the right operand |
| * @return optimized JQLAST |
| */ |
| protected JQLAST checkAnd(JQLAST op, JQLAST left, JQLAST right) |
| { |
| JQLAST ast = op; |
| |
| if (isBooleanValueAST(left)) |
| { |
| ast = handleValueAndExpr(op, left.getValue(), right); |
| } |
| else if (isBooleanValueAST(right)) |
| { |
| ast = handleValueAndExpr(op, right.getValue(), left); |
| } |
| return ast; |
| } |
| |
| /** |
| * Check an OR operation (BOR, OR) for constant operands |
| * that could be optimized. |
| * @param op the OR operator |
| * @param left the left operand |
| * @param right the right operand |
| * @return optimized JQLAST |
| */ |
| protected JQLAST checkOr(JQLAST op, JQLAST left, JQLAST right) |
| { |
| JQLAST ast = op; |
| |
| if (isBooleanValueAST(left)) |
| { |
| ast = handleValueOrExpr(op, left.getValue(), right); |
| } |
| else if (isBooleanValueAST(right)) |
| { |
| ast = handleValueOrExpr(op, right.getValue(), left); |
| } |
| return ast; |
| } |
| |
| /** |
| * Check a equality operation (EQUAL, NOT_EQUAL) for constant operands |
| * that could be optimized. |
| * @param op the equality operator |
| * @param left the left operand |
| * @param right the right operand |
| * @param negate true for not equal operation, false otherwise |
| * @return optimized JQLAST |
| */ |
| protected JQLAST checkEqualityOp(JQLAST op, JQLAST left, JQLAST right, |
| boolean negate) |
| { |
| JQLAST ast = op; |
| |
| // case <VALUE> <op> <VALUE> |
| if ((left.getType() == VALUE) && (right.getType() == VALUE)) |
| { |
| ast = handleValueEqValue(op, left, right, negate); |
| } |
| // case <boolean VALUE> <op> <expr> |
| else if (isBooleanValueAST(left)) |
| { |
| ast = handleBooleanValueEqExpr(op, left.getValue(), right, negate); |
| } |
| // case <expr> <op> <boolean VALUE> |
| else if (isBooleanValueAST(right)) |
| { |
| ast = handleBooleanValueEqExpr(op, right.getValue(), left, negate); |
| } |
| return ast; |
| } |
| |
| /** |
| * Check a object equality operation (OBJECT_EQUAL, OBJECT_NOT_EQUAL) |
| * for constant operands that could be optimized. |
| * @param op the object equality operator |
| * @param left the left operand |
| * @param right the right operand |
| * @param negate true for not equal operation, false otherwise |
| * @return optimized JQLAST |
| */ |
| protected JQLAST checkObjectEqualityOp(JQLAST op, JQLAST left, JQLAST right, |
| boolean negate) |
| { |
| JQLAST ast = op; |
| |
| if ((left.getType() == VALUE) && (right.getType() == VALUE)) |
| { |
| ast = handleValueEqValue(op, left, right, negate); |
| } |
| return ast; |
| } |
| |
| /** |
| * Check a collection equality operation (COLLECTION_EQUAL, |
| * COLLECTION_NOT_EQUAL) for constant operands that could be optimized. |
| * @param op the collection equality operator |
| * @param left the left operand |
| * @param right the right operand |
| * @param negate true for not equal operation, false otherwise |
| * @return optimized JQLAST |
| */ |
| protected JQLAST checkCollectionEqualityOp(JQLAST op, JQLAST left, |
| JQLAST right, boolean negate) |
| { |
| JQLAST ast = op; |
| boolean isLeftConstant = (left.getType() == VALUE); |
| boolean isRightConstant = (right.getType() == VALUE); |
| |
| if (isLeftConstant && isRightConstant) |
| { |
| ast = handleValueEqValue(op, left, right, negate); |
| } |
| else if ((isLeftConstant && (left.getValue() == null) && isNonConstantCollection(right)) || |
| (isRightConstant && (right.getValue() == null) && isNonConstantCollection(left))) |
| { |
| // This optimization is datastore dependend. |
| // In TP we know a collection returned by the datastore is never null. |
| // null == <collection field> -> false |
| // <collection field> == null -> false |
| // null != <collection field> -> true |
| // <collection field> != null -> true |
| ast.setType(VALUE); |
| ast.setValue(new Boolean(negate)); |
| ast.setFirstChild(null); |
| } |
| |
| return ast; |
| } |
| |
| /** |
| * Check a logical not operation (LNOT) for a constant operand |
| * that could be optimized. |
| * @param op the logical not operator |
| * @param arg the operand |
| * @return optimized JQLAST |
| */ |
| protected JQLAST checkLogicalNotOp(JQLAST op, JQLAST arg) |
| { |
| JQLAST ast = op; |
| |
| if (arg.getType() == VALUE) |
| { |
| // !value may be calculated at compile time. |
| Object valueObj = arg.getValue(); |
| boolean value = (valueObj instanceof Boolean) ? |
| ((Boolean)valueObj).booleanValue() : false; |
| arg.setType(VALUE); |
| arg.setValue(new Boolean(!value)); |
| arg.setNextSibling(null); |
| ast = arg; |
| } |
| else |
| { |
| ast = deMorgan(arg); |
| } |
| return ast; |
| } |
| |
| /** |
| * Check a binary plus operation (PLUS) for constant operands |
| * that could be optimized. |
| * @param op the plus operator |
| * @param left the left operand |
| * @param right the right operand |
| * @return optimized JQLAST |
| */ |
| protected JQLAST checkBinaryPlusOp(JQLAST op, JQLAST left, JQLAST right) |
| { |
| JQLAST ast = op; |
| |
| if ((left.getType() == VALUE) && (right.getType() == VALUE)) |
| { |
| Object leftValue = left.getValue(); |
| Object rightValue = right.getValue(); |
| Object value = null; |
| if (leftValue == null) |
| value = rightValue; |
| else if (rightValue == null) |
| value = leftValue; |
| else |
| { |
| Type type = op.getJQLType(); |
| |
| if (type instanceof NumericWrapperClassType) |
| type = ((NumericWrapperClassType)type).getPrimitiveType(); |
| |
| if (type.equals(typetab.intType)) |
| value = new Integer(((Number)leftValue).intValue() + |
| ((Number)rightValue).intValue()); |
| else if (type.equals(typetab.longType)) |
| value = new Long(((Number)leftValue).longValue() + |
| ((Number)rightValue).longValue()); |
| else if (type.equals(typetab.floatType)) |
| value = new Float(((Number)leftValue).floatValue() + |
| ((Number)rightValue).floatValue()); |
| else if (type.equals(typetab.doubleType)) |
| value = new Double(((Number)leftValue).doubleValue() + |
| ((Number)rightValue).doubleValue()); |
| else if (type.equals(typetab.bigDecimalType)) |
| value = getBigDecimalValue(leftValue).add( |
| getBigDecimalValue(rightValue)); |
| else if (type.equals(typetab.bigIntegerType)) |
| value = getBigIntegerValue(leftValue).add( |
| getBigIntegerValue(rightValue)); |
| else |
| errorMsg.fatal(I18NHelper.getMessage(messages, |
| "jqlc.optimizer.checkbinaryplusop.invalidtype", //NOI18N |
| String.valueOf(type))); |
| } |
| ast.setType(VALUE); |
| ast.setValue(value); |
| ast.setFirstChild(null); |
| } |
| return ast; |
| } |
| |
| /** |
| * Check a string concatenation operation (CONCAT) for constant operands |
| * that could be optimized. |
| * @param op the concat operator |
| * @param left the left operand |
| * @param right the right operand |
| * @return optimized JQLAST |
| */ |
| protected JQLAST checkConcatOp(JQLAST op, JQLAST left, JQLAST right) |
| { |
| JQLAST ast = op; |
| |
| if ((left.getType() == VALUE) && (right.getType() == VALUE)) |
| { |
| Object leftValue = left.getValue(); |
| Object rightValue = right.getValue(); |
| Object value = null; |
| if (leftValue == null) |
| value = rightValue; |
| else if (rightValue == null) |
| value = leftValue; |
| else |
| value = leftValue.toString() + rightValue.toString(); |
| ast.setType(VALUE); |
| ast.setValue(value); |
| ast.setFirstChild(null); |
| } |
| return ast; |
| } |
| |
| /** |
| * Check a binary minus operation (MINUS) for constant operands |
| * that could be optimized. |
| * @param op the minus operator |
| * @param left the left operand |
| * @param right the right operand |
| * @return optimized JQLAST |
| */ |
| protected JQLAST checkBinaryMinusOp(JQLAST op, JQLAST left, JQLAST right) |
| { |
| JQLAST ast = op; |
| |
| if ((left.getType() == VALUE) && (right.getType() == VALUE)) |
| { |
| Object leftValue = left.getValue(); |
| Object rightValue = right.getValue(); |
| Object value = null; |
| if (rightValue == null) |
| value = leftValue; |
| else |
| { |
| if (leftValue == null) |
| leftValue = new Integer(0); |
| |
| Type type = op.getJQLType(); |
| |
| if (type instanceof NumericWrapperClassType) |
| type = ((NumericWrapperClassType)type).getPrimitiveType(); |
| |
| if (type.equals(typetab.intType)) |
| value = new Integer(((Number)leftValue).intValue() - |
| ((Number)rightValue).intValue()); |
| else if (type.equals(typetab.longType)) |
| value = new Long(((Number)leftValue).longValue() - |
| ((Number)rightValue).longValue()); |
| else if (type.equals(typetab.floatType)) |
| value = new Float(((Number)leftValue).floatValue() - |
| ((Number)rightValue).floatValue()); |
| else if (type.equals(typetab.doubleType)) |
| value = new Double(((Number)leftValue).doubleValue() - |
| ((Number)rightValue).doubleValue()); |
| else if (type.equals(typetab.bigDecimalType)) |
| value = getBigDecimalValue(leftValue).subtract( |
| getBigDecimalValue(rightValue)); |
| else if (type.equals(typetab.bigIntegerType)) |
| value = getBigIntegerValue(leftValue).subtract( |
| getBigIntegerValue(rightValue)); |
| else |
| errorMsg.fatal(I18NHelper.getMessage(messages, |
| "jqlc.optimizer.checkbinaryminusop.invalidtype", //NOI18N |
| String.valueOf(type))); |
| } |
| ast.setType(VALUE); |
| ast.setValue(value); |
| ast.setFirstChild(null); |
| } |
| return ast; |
| } |
| |
| /** |
| * Check a binary multiplication operation (STAR) for constant operands |
| * that could be optimized. |
| * @param op the multiplication operator |
| * @param left the left operand |
| * @param right the right operand |
| * @return optimized JQLAST |
| */ |
| protected JQLAST checkMultiplicationOp(JQLAST op, JQLAST left, JQLAST right) |
| { |
| JQLAST ast = op; |
| |
| if ((left.getType() == VALUE) && (right.getType() == VALUE)) |
| { |
| Object leftValue = left.getValue(); |
| Object rightValue = right.getValue(); |
| Object value = null; |
| if (leftValue == null) |
| leftValue = new Integer(0); |
| if (rightValue == null) |
| rightValue = new Integer(0); |
| Type type = op.getJQLType(); |
| |
| if (type instanceof NumericWrapperClassType) |
| type = ((NumericWrapperClassType)type).getPrimitiveType(); |
| |
| if (type.equals(typetab.intType)) |
| value = new Integer(((Number)leftValue).intValue() * |
| ((Number)rightValue).intValue()); |
| else if (type.equals(typetab.longType)) |
| value = new Long(((Number)leftValue).longValue() * |
| ((Number)rightValue).longValue()); |
| else if (type.equals(typetab.floatType)) |
| value = new Float(((Number)leftValue).floatValue() * |
| ((Number)rightValue).floatValue()); |
| else if (type.equals(typetab.doubleType)) |
| value = new Double(((Number)leftValue).doubleValue() * |
| ((Number)rightValue).doubleValue()); |
| else if (type.equals(typetab.bigDecimalType)) |
| value = getBigDecimalValue(leftValue).multiply( |
| getBigDecimalValue(rightValue)); |
| else if (type.equals(typetab.bigIntegerType)) |
| value = getBigIntegerValue(leftValue).multiply( |
| getBigIntegerValue(rightValue)); |
| else |
| errorMsg.fatal(I18NHelper.getMessage(messages, |
| "jqlc.optimizer.checkmultiplicationop.invalidtype", //NOI18N |
| String.valueOf(type))); |
| |
| ast.setType(VALUE); |
| ast.setValue(value); |
| ast.setFirstChild(null); |
| } |
| return ast; |
| } |
| |
| /** |
| * Check a binary division operation (DIV) for constant operands |
| * that could be optimized. |
| * @param op the division operator |
| * @param left the left operand |
| * @param right the right operand |
| * @return optimized JQLAST |
| */ |
| protected JQLAST checkDivisionOp(JQLAST op, JQLAST left, JQLAST right) |
| { |
| JQLAST ast = op; |
| |
| if ((left.getType() == VALUE) && (right.getType() == VALUE)) |
| { |
| Object leftValue = left.getValue(); |
| Object rightValue = right.getValue(); |
| Object value = null; |
| if (leftValue == null) |
| leftValue = new Integer(0); |
| if (rightValue == null) |
| // division by zero! |
| rightValue = new Integer(0); |
| |
| Type type = op.getJQLType(); |
| |
| if (type instanceof NumericWrapperClassType) |
| type = ((NumericWrapperClassType)type).getPrimitiveType(); |
| |
| if (type.equals(typetab.intType)) |
| value = new Integer(((Number)leftValue).intValue() / |
| ((Number)rightValue).intValue()); |
| else if (type.equals(typetab.longType)) |
| value = new Long(((Number)leftValue).longValue() / |
| ((Number)rightValue).longValue()); |
| else if (type.equals(typetab.floatType)) |
| value = new Float(((Number)leftValue).floatValue() / |
| ((Number)rightValue).floatValue()); |
| else if (type.equals(typetab.doubleType)) |
| value = new Double(((Number)leftValue).doubleValue() / |
| ((Number)rightValue).doubleValue()); |
| else if (type.equals(typetab.bigDecimalType)) |
| value = getBigDecimalValue(leftValue).divide( |
| getBigDecimalValue(rightValue), BigDecimal.ROUND_HALF_EVEN); |
| else if (type.equals(typetab.bigIntegerType)) |
| value = getBigIntegerValue(leftValue).divide( |
| getBigIntegerValue(rightValue)); |
| else |
| errorMsg.fatal(I18NHelper.getMessage(messages, |
| "jqlc.optimizer.checkdivisionop.invalidtype", //NOI18N |
| String.valueOf(type))); |
| |
| ast.setType(VALUE); |
| ast.setValue(value); |
| ast.setFirstChild(null); |
| } |
| return ast; |
| } |
| |
| /** |
| * Check a binary modular operation (MOD) for constant operands |
| * that could be optimized. |
| * @param op the mod operator |
| * @param left the left operand |
| * @param right the right operand |
| * @return optimized JQLAST |
| */ |
| protected JQLAST checkModOp(JQLAST op, JQLAST left, JQLAST right) |
| { |
| JQLAST ast = op; |
| |
| if ((left.getType() == VALUE) && (right.getType() == VALUE)) |
| { |
| Object leftValue = left.getValue(); |
| Object rightValue = right.getValue(); |
| Object value = null; |
| if (leftValue == null) |
| leftValue = new Integer(0); |
| if (rightValue == null) |
| // division by zero! |
| rightValue = new Integer(0); |
| |
| Type type = op.getJQLType(); |
| |
| if (type instanceof NumericWrapperClassType) |
| type = ((NumericWrapperClassType)type).getPrimitiveType(); |
| |
| if (type.equals(typetab.intType)) |
| value = new Integer(((Number)leftValue).intValue() % |
| ((Number)rightValue).intValue()); |
| else if (type.equals(typetab.longType)) |
| value = new Long(((Number)leftValue).longValue() % |
| ((Number)rightValue).longValue()); |
| else if (type.equals(typetab.floatType)) |
| value = new Float(((Number)leftValue).floatValue() % |
| ((Number)rightValue).floatValue()); |
| else if (type.equals(typetab.doubleType)) |
| value = new Double(((Number)leftValue).doubleValue() % |
| ((Number)rightValue).doubleValue()); |
| else if (type.equals(typetab.bigDecimalType)) |
| { |
| BigDecimal leftBigDecimal = getBigDecimalValue(leftValue); |
| BigDecimal rightBigDecimal = getBigDecimalValue(rightValue); |
| //use ROUND_HALF_EVEN so that it is consistent with div |
| BigDecimal quotient = leftBigDecimal.divide(rightBigDecimal, |
| 0, BigDecimal.ROUND_HALF_EVEN); |
| value = leftBigDecimal.subtract( |
| rightBigDecimal.multiply(quotient)); |
| } |
| else if (type.equals(typetab.bigIntegerType)) |
| value = getBigIntegerValue(leftValue).remainder( |
| getBigIntegerValue(rightValue)); |
| else |
| errorMsg.fatal(I18NHelper.getMessage(messages, |
| "jqlc.optimizer.checkmodop.invalidtype", //NOI18N |
| String.valueOf(type))); |
| |
| ast.setType(VALUE); |
| ast.setValue(value); |
| ast.setFirstChild(null); |
| } |
| return ast; |
| } |
| |
| /** |
| * Check a unary minus operation (UNARY_MINUS) for a constant operand |
| * that could be optimized. |
| * @param op the unary minus operator |
| * @param arg the operand |
| * @return optimized JQLAST |
| */ |
| protected JQLAST checkUnaryMinusOp(JQLAST op, JQLAST arg) |
| { |
| JQLAST ast = op; |
| |
| if (arg.getType() == VALUE) |
| { |
| Object value = arg.getValue(); |
| Type type = op.getJQLType(); |
| Object negate = null; |
| |
| if (type instanceof NumberType) |
| negate = ((NumberType)type).negate((Number)value); |
| else |
| errorMsg.fatal(I18NHelper.getMessage(messages, |
| "jqlc.optimizer.checkunaryminusop.invalidtype", //NOI18N |
| String.valueOf(type))); |
| |
| ast.setType(VALUE); |
| ast.setValue(negate); |
| ast.setFirstChild(null); |
| } |
| return ast; |
| } |
| |
| /** |
| * Check a cast operation for a constant operand |
| * that could be optimized. |
| * @param op the cast operator |
| * @param castType the cast type |
| * @param expr the non constant operand |
| * @return optimized JQLAST |
| */ |
| protected JQLAST checkCastOp(JQLAST op, JQLAST castType, JQLAST expr) |
| { |
| JQLAST ast = op; |
| |
| if (expr.getType() == VALUE) |
| { |
| Object value = expr.getValue(); |
| Type type = op.getJQLType(); |
| if (type instanceof NumericWrapperClassType) |
| type = ((NumericWrapperClassType)type).getPrimitiveType(); |
| |
| if (type.equals(typetab.intType)) |
| value = new Integer(((Number)value).intValue()); |
| else if (type.equals(typetab.longType)) |
| value = new Long(((Number)value).longValue()); |
| else if (type.equals(typetab.floatType)) |
| value = new Float(((Number)value).floatValue()); |
| else if (type.equals(typetab.doubleType)) |
| value = new Double(((Number)value).doubleValue()); |
| else if (type.equals(typetab.bigDecimalType)) |
| value = getBigDecimalValue(value); |
| else if (type.equals(typetab.bigIntegerType)) |
| value = getBigIntegerValue(value); |
| else if (type.equals(typetab.byteType)) |
| value = new Byte((byte)((Number)value).intValue()); |
| else if (type.equals(typetab.shortType)) |
| value = new Short((short)((Number)value).intValue()); |
| else if (type.equals(typetab.charType)) |
| value = new Character((char)((Number)value).intValue()); |
| |
| // If non of the above type applies, leave the value as it is |
| |
| // convert the TYPECAST op into a VALUE |
| ast.setType(VALUE); |
| ast.setValue(value); |
| ast.setFirstChild(null); |
| } |
| return ast; |
| } |
| |
| /** |
| * Converts the specified value into a BigDecimal value. |
| * @param value value to be converted |
| * @return BigDecimal representation |
| */ |
| protected BigDecimal getBigDecimalValue(Object value) |
| { |
| BigDecimal ret = null; |
| if (value instanceof Number) |
| ret = (BigDecimal)typetab.bigDecimalType.getValue((Number)value); |
| else |
| errorMsg.fatal(I18NHelper.getMessage(messages, |
| "jqlc.optimizer.getbigdecimalvalue.notnumber", //NOI18N |
| String.valueOf(value))); |
| |
| return ret; |
| } |
| |
| /** |
| * Converts the specified value into a BigInteger value. |
| * @param value value to be converted |
| * @return BigInteger representation |
| */ |
| protected BigInteger getBigIntegerValue(Object value) |
| { |
| BigInteger ret = null; |
| |
| if (value instanceof Number) |
| ret = (BigInteger)typetab.bigIntegerType.getValue((Number)value); |
| else |
| errorMsg.fatal(I18NHelper.getMessage(messages, |
| "jqlc.optimizer.getbigintegervalue.notnumber", //NOI18N |
| String.valueOf(value))); |
| |
| return ret; |
| } |
| |
| /** |
| * This method is called in the case of an equality operation having two |
| * constant operands. It calculates the result of this constant operation |
| * and returns a JQLAST node representing a constant boolean value. |
| * @param op the equality operator |
| * @param left the left operand |
| * @param right the right operand |
| * @param negate true for not equal operation, false otherwise |
| * @return optimized JQLAST |
| */ |
| protected JQLAST handleValueEqValue(JQLAST op, JQLAST left, JQLAST right, |
| boolean negate) |
| { |
| Object leftValue = left.getValue(); |
| Object rightValue = right.getValue(); |
| boolean value = false; |
| |
| if ((leftValue == null) && (rightValue == null)) |
| { |
| // both values are null -> true |
| value = true; |
| } |
| else if ((leftValue != null) && (rightValue != null)) |
| { |
| // both values are not null -> use equals |
| value = leftValue.equals(rightValue); |
| } |
| else |
| { |
| // one value is null, the other is not null -> false |
| value = false; |
| } |
| if (negate) |
| { |
| value = !value; |
| } |
| op.setType(VALUE); |
| op.setValue(new Boolean(value)); |
| op.setFirstChild(null); |
| return op; |
| } |
| |
| /** |
| * This method is called in the case of an equality operation having |
| * a boolean constant operand and a non constant operand. |
| * It returns the non constant operand either as it is or inverted, |
| * depending on the equality operation. |
| * @param op the equality operator |
| * @param value the contant boolean value |
| * @param expr the non constant operand |
| * @param negate true for not equal operation, false otherwise |
| * @return optimized JQLAST |
| */ |
| private JQLAST handleBooleanValueEqExpr(JQLAST op, Object value, |
| JQLAST expr, boolean negate) |
| { |
| JQLAST ast; |
| boolean skip = (value instanceof Boolean) ? |
| ((Boolean)value).booleanValue() : false; |
| if (negate) skip = !skip; |
| |
| if (skip) |
| { |
| // expr == true -> expr |
| // expr != false -> expr |
| ast = expr; |
| } |
| else |
| { |
| // if expr is a equality op or a not op the invert operation may be "inlined": |
| // (expr1 == expr2) != true -> expr1 != expr2 |
| // (expr1 != expr2) != true -> expr1 == expr2 |
| // !expr != true -> expr |
| // !expr == false -> expr |
| // Otherwise wrap the expr with a not op |
| // expr != true -> !expr |
| // expr == false -> !expr |
| switch (expr.getType()) |
| { |
| case EQUAL: |
| expr.setType(NOT_EQUAL); |
| expr.setText("!="); //NOI18N |
| ast = expr; |
| break; |
| case NOT_EQUAL: |
| expr.setType(EQUAL); |
| expr.setText("=="); //NOI18N |
| ast = expr; |
| break; |
| case LNOT: |
| ast = (JQLAST)expr.getFirstChild(); |
| break; |
| default: |
| op.setType(LNOT); |
| op.setText("!"); //NOI18N |
| op.setFirstChild(expr); |
| ast = op; |
| } |
| expr.setNextSibling(null); |
| } |
| return ast; |
| } |
| |
| /** |
| * This method is called in the case of an AND operation having at least |
| * one constant operand. If the constant operand evaluates to true it |
| * returns the other operand. If it evaluates to false it returns an AST |
| * representing the constant boolean value false. |
| * @param op the AND operator |
| * @param left the left operand |
| * @param right the right operand |
| * @return optimized JQLAST |
| */ |
| private JQLAST handleValueAndExpr(JQLAST op, Object value, JQLAST expr) |
| { |
| JQLAST ast; |
| |
| if ((value instanceof Boolean) && ((Boolean)value).booleanValue()) |
| { |
| // true AND expr -> expr |
| // expr AND true -> expr |
| expr.setNextSibling(null); |
| ast = expr; |
| } |
| else |
| { |
| // false AND expr -> false |
| // expr AND false -> false |
| op.setType(VALUE); |
| op.setText("false"); //NOI18N |
| op.setValue(new Boolean(false)); |
| op.setFirstChild(null); |
| ast = op; |
| } |
| return ast; |
| } |
| |
| /** |
| * This method is called in the case of an OR operation having at least |
| * one constant operand. If the constant operand evaluates to false it |
| * returns the other operand. If it evaluates to true it returns an AST |
| * representing the constant boolean value true. |
| * @param op the AND operator |
| * @param left the left operand |
| * @param right the right operand |
| * @return optimized JQLAST |
| */ |
| private JQLAST handleValueOrExpr(JQLAST op, Object value, JQLAST expr) |
| { |
| JQLAST ast; |
| |
| if ((value instanceof Boolean) && ((Boolean)value).booleanValue()) |
| { |
| // true OR expr -> true |
| // expr OR true -> true |
| op.setType(VALUE); |
| op.setText("true"); //NOI18N |
| op.setValue(new Boolean(true)); |
| op.setFirstChild(null); |
| ast = op; |
| } |
| else |
| { |
| // false OR expr -> expr |
| // expr OR false -> expr |
| expr.setNextSibling(null); |
| ast = expr; |
| } |
| return ast; |
| } |
| |
| /** |
| * Returns true if the specified AST represents a constant boolean value. |
| */ |
| protected boolean isBooleanValueAST(JQLAST ast) |
| { |
| return (ast.getType() == VALUE) && |
| (typetab.booleanType.equals(ast.getJQLType())); |
| } |
| |
| /** |
| * Returns true if the specified AST represents a datastore value. |
| */ |
| protected boolean isNonConstantCollection(JQLAST ast) |
| { |
| switch (ast.getType()) |
| { |
| case FIELD_ACCESS : |
| case NAVIGATION : |
| return true; |
| case TYPECAST : |
| JQLAST expr = (JQLAST)ast.getFirstChild().getNextSibling(); |
| return isNonConstantCollection(expr); |
| default: |
| return false; |
| } |
| } |
| |
| /** |
| * Implements DeMorgans rule: |
| * <br> |
| * NOT (a AND b) -> NOT a OR NOT b |
| * <br> |
| * NOT (a OR b) -> NOT a AND NOT b |
| * <br> |
| * NOT (NOT a) -> a |
| * <br> |
| * The method assumes that the tree passed as an argument does not include |
| * the initial NOT. Note, this method checks for contains clauses, because |
| * they require special treatement. |
| */ |
| protected JQLAST deMorgan(JQLAST tree) |
| { |
| JQLAST result = null; |
| JQLAST left = null; |
| JQLAST right = null; |
| switch (tree.getType()) |
| { |
| case AND: |
| case BAND: |
| left = (JQLAST)tree.getFirstChild(); |
| right = (JQLAST)left.getNextSibling(); |
| String leftVar = getVariableFromContainsClause(left); |
| String rightVar = getVariableFromContainsClause(right); |
| if (leftVar != null) |
| { |
| // found AND ( CONTAINS, right ), so check right for special |
| // variable treatement |
| result = buildAST(tree, left, deMorgan(right, leftVar)); |
| } |
| else if (rightVar != null) |
| { |
| // found AND ( left, CONTAINS, ), so check left for special |
| // variable treatement |
| result = buildAST(tree, right, deMorgan(left, rightVar)); |
| } |
| else |
| { |
| invertNode(tree); |
| result = buildAST(tree, deMorgan(left), deMorgan(right)); |
| } |
| break; |
| case OR: |
| case BOR: |
| left = (JQLAST)tree.getFirstChild(); |
| right = (JQLAST)left.getNextSibling(); |
| invertNode(tree); |
| result = buildAST(tree, deMorgan(left), deMorgan(right)); |
| break; |
| case LNOT: |
| // This is !(!arg) => return arg |
| result = (JQLAST)tree.getFirstChild(); |
| break; |
| default: |
| // wrap arg into not operator |
| result = buildAST(new JQLAST(LNOT, "!", typetab.booleanType), tree); |
| break; |
| } |
| return result; |
| } |
| |
| /** |
| * This overloaded deMorgan method implements special treatment of variable |
| * access expressions in the case of !contains. The method keeps an expression |
| * accessing the specified variable as it is, but it inverts an expression NOT |
| * accessing the variable following regular DeMorgan rules. |
| */ |
| protected JQLAST deMorgan(JQLAST tree, String var) |
| { |
| JQLAST result = tree; |
| switch (tree.getType()) |
| { |
| case AND: |
| case BAND: |
| case OR: |
| case BOR: |
| JQLAST left = (JQLAST)tree.getFirstChild(); |
| JQLAST right = (JQLAST)left.getNextSibling(); |
| if (!includesVariableAccess(left, var) || |
| !includesVariableAccess(right, var)) |
| { |
| invertNode(tree); |
| } |
| result = buildAST(tree, deMorgan(left, var), deMorgan(right, var)); |
| break; |
| default: |
| if (!includesVariableAccess(tree, var)) |
| { |
| result = deMorgan(tree); |
| } |
| break; |
| } |
| return result; |
| } |
| |
| /** |
| * Checks the specified tree being a CONATAINS clause. If yes it returns |
| * the variable used in the contains clause. Otherwise it returns null. |
| */ |
| protected String getVariableFromContainsClause(JQLAST tree) |
| { |
| switch (tree.getType()) |
| { |
| case CONTAINS: |
| case NOT_CONTAINS: |
| return tree.getFirstChild().getNextSibling().getText(); |
| default: |
| return null; |
| } |
| } |
| |
| /** |
| * Checks whether the specified tree accesses the variable with the |
| * specified name. Accessing means either this node or of of the subnodes |
| * has the type VARIABLE. |
| * NOTE, the method is intended to be used in the ! contains case only! |
| * If it find a variable access node of the form |
| * <br> |
| * #(VARIABLE collection) |
| * it maps it to |
| * #(VARIABLE #(NOT_IN (collection)) |
| * <br> |
| * This incdicates a variable belonging to a !contains clause. |
| */ |
| protected boolean includesVariableAccess(AST tree, String var) |
| { |
| if ((tree == null) || (var == null)) |
| return false; |
| |
| boolean found = false; |
| JQLAST child = (JQLAST)tree.getFirstChild(); |
| if ((tree.getType() == VARIABLE) && (tree.getText().equals(var)) && |
| (child != null)) |
| { |
| found = true; |
| if (child.getType() != NOT_IN) |
| { |
| tree.setFirstChild(buildAST( |
| new JQLAST(NOT_IN, "notIn", typetab.booleanType), child)); |
| } |
| } |
| for (AST node = tree.getFirstChild(); node != null; node = node.getNextSibling()) |
| { |
| if (includesVariableAccess(node, var)) |
| found = true; |
| } |
| return found; |
| } |
| |
| /** |
| * Inverts the specified node: AND -> OR, == -> !=, etc. |
| */ |
| protected void invertNode(JQLAST node) |
| { |
| switch(node.getType()) |
| { |
| case AND: |
| node.setType(OR); |
| node.setText("||"); |
| break; |
| case BAND: |
| node.setType(BOR); |
| node.setText("|"); |
| break; |
| case OR: |
| node.setType(AND); |
| node.setText("&&"); |
| break; |
| case BOR: |
| node.setType(BAND); |
| node.setText("&"); |
| break; |
| case EQUAL: |
| node.setType(NOT_EQUAL); |
| node.setText("!="); |
| break; |
| case NOT_EQUAL: |
| node.setType(EQUAL); |
| node.setText("=="); |
| break; |
| case LT: |
| node.setType(GE); |
| node.setText(">="); |
| break; |
| case LE: |
| node.setType(GT); |
| node.setText(">"); |
| break; |
| case GT: |
| node.setType(LE); |
| node.setText("<="); |
| break; |
| case GE: |
| node.setType(LT); |
| node.setText("<"); |
| break; |
| } |
| } |
| |
| /** Builds a binary tree. */ |
| protected JQLAST buildAST(JQLAST root, JQLAST left, JQLAST right) |
| { |
| root.setFirstChild(left); |
| left.setNextSibling(right); |
| right.setNextSibling(null); |
| return root; |
| } |
| |
| /** */ |
| protected JQLAST buildAST(JQLAST root, JQLAST arg) |
| { |
| root.setFirstChild(arg); |
| arg.setNextSibling(null); |
| return root; |
| } |
| } |
| |
| // rules |
| |
| query |
| : #( q:QUERY |
| candidateClass |
| parameters |
| variables |
| ordering |
| result |
| filter |
| ) |
| ; |
| |
| // ---------------------------------- |
| // rules: candidate class |
| // ---------------------------------- |
| |
| candidateClass |
| { |
| errorMsg.setContext("setCandidates"); //NOI18N |
| } |
| : CLASS_DEF |
| ; |
| |
| // ---------------------------------- |
| // rules: parameter declaration |
| // ---------------------------------- |
| |
| parameters |
| { |
| errorMsg.setContext("declareParameters"); //NOI18N |
| } |
| : ( declareParameter )* |
| ; |
| |
| declareParameter |
| : #( PARAMETER_DEF type IDENT ) |
| ; |
| |
| // ---------------------------------- |
| // rules: variable declaration |
| // ---------------------------------- |
| |
| variables |
| { |
| errorMsg.setContext("declareVariables"); //NOI18N |
| } |
| : ( declareVariable )* |
| ; |
| |
| declareVariable |
| : #( VARIABLE_DEF type IDENT ) |
| ; |
| |
| // ---------------------------------- |
| // rules: ordering specification |
| // ---------------------------------- |
| |
| ordering |
| { |
| errorMsg.setContext("setOrdering"); //NOI18N |
| } |
| : ( orderSpec )* |
| ; |
| |
| orderSpec |
| : #( ORDERING_DEF ( ASCENDING | DESCENDING ) expression ) |
| ; |
| |
| // ---------------------------------- |
| // rules: result expression |
| // ---------------------------------- |
| |
| result |
| { |
| errorMsg.setContext("setResult"); //NOI18N |
| } |
| : #( r:RESULT_DEF resultExpr ) |
| | // empty rule |
| ; |
| |
| resultExpr |
| : #( DISTINCT resultExpr ) |
| | #( AVG resultExpr ) |
| | #( MAX resultExpr ) |
| | #( MIN resultExpr ) |
| | #( SUM resultExpr ) |
| | #( COUNT resultExpr ) |
| | expression |
| ; |
| |
| // ---------------------------------- |
| // rules: filer expression |
| // ---------------------------------- |
| |
| filter |
| { |
| errorMsg.setContext("setFilter"); //NOI18N |
| } |
| : #( FILTER_DEF expression ) |
| ; |
| |
| expression |
| : primary |
| | bitwiseExpr |
| | conditionalExpr |
| | relationalExpr |
| | binaryArithmeticExpr |
| | unaryArithmeticExpr |
| | complementExpr |
| ; |
| |
| bitwiseExpr |
| : #( op1:BAND left1:expression right1:expression ) |
| { |
| #bitwiseExpr = checkAnd(#op1, #left1, #right1); |
| } |
| | #( op2:BOR left2:expression right2:expression ) |
| { |
| #bitwiseExpr = checkOr(#op2, #left2, #right2); |
| } |
| | #( op3:BXOR left3:expression right3:expression ) |
| ; |
| |
| conditionalExpr |
| : #( op1:AND left1:expression right1:expression ) |
| { |
| #conditionalExpr = checkAnd(#op1, #left1, #right1); |
| } |
| | #( op2:OR left2:expression right2:expression ) |
| { |
| #conditionalExpr = checkOr(#op2, #left2, #right2); |
| } |
| ; |
| |
| relationalExpr |
| : #( op1:EQUAL left1:expression right1:expression ) |
| { |
| #relationalExpr = checkEqualityOp(#op1, #left1, #right1, false); |
| } |
| | #( op2:NOT_EQUAL left2:expression right2:expression ) |
| { |
| #relationalExpr = checkEqualityOp(#op2, #left2, #right2, true); |
| } |
| | #( op3:OBJECT_EQUAL left3:expression right3:expression ) |
| { |
| #relationalExpr = checkObjectEqualityOp(#op3, #left3, #right3, false); |
| } |
| | #( op4:OBJECT_NOT_EQUAL left4:expression right4:expression ) |
| { |
| #relationalExpr = checkObjectEqualityOp(#op4, #left4, #right4, true); |
| } |
| | #( op5:COLLECTION_EQUAL left5:expression right5:expression ) |
| { |
| #relationalExpr = checkCollectionEqualityOp(#op5, #left5, #right5, false); |
| } |
| | #( op6:COLLECTION_NOT_EQUAL left6:expression right6:expression ) |
| { |
| #relationalExpr = checkCollectionEqualityOp(#op6, #left6, #right6, true); |
| } |
| | #( LT expression expression ) |
| | #( GT expression expression ) |
| | #( LE expression expression ) |
| | #( GE expression expression ) |
| ; |
| |
| binaryArithmeticExpr |
| : #( op1:PLUS left1:expression right1:expression ) |
| { |
| #binaryArithmeticExpr = checkBinaryPlusOp(#op1, #left1, #right1); |
| } |
| | #( op2:CONCAT left2:expression right2:expression ) |
| { |
| #binaryArithmeticExpr = checkConcatOp(#op2, #left2, #right2); |
| } |
| | #( op3:MINUS left3:expression right3:expression ) |
| { |
| #binaryArithmeticExpr = checkBinaryMinusOp(#op3, #left3, #right3); |
| } |
| | #( op4:STAR left4:expression right4:expression ) |
| { |
| #binaryArithmeticExpr = checkMultiplicationOp(#op4, #left4, #right4); |
| } |
| | #( op5:DIV left5:expression right5:expression ) |
| { |
| #binaryArithmeticExpr = checkDivisionOp(#op5, #left5, #right5); |
| } |
| | #( op6:MOD left6:expression right6:expression ) |
| { |
| #binaryArithmeticExpr = checkModOp(#op6, #left6, #right6); |
| } |
| ; |
| |
| unaryArithmeticExpr |
| : #( UNARY_PLUS expression ) |
| | ( unaryMinusLiteralExpr )=> unaryMinusLiteralExpr |
| | #( op2:UNARY_MINUS arg2:expression ) |
| { |
| #unaryArithmeticExpr = checkUnaryMinusOp(#op2, #arg2); |
| } |
| ; |
| |
| unaryMinusLiteralExpr |
| : #( UNARY_MINUS ( i:INT_LITERAL | l:LONG_LITERAL ) ) |
| { |
| JQLAST li = (#i != null) ? #i : #l; |
| li.setText("-" + li.getText()); |
| // calling literal here directly does not work properly |
| // the following logic need to be in sync with that of literal |
| li.setValue(#literalHelper(li)); |
| li.setType(VALUE); |
| #unaryMinusLiteralExpr = #li; |
| } |
| ; |
| |
| complementExpr |
| : #( op1:BNOT arg1:expression ) |
| | #( op2:LNOT arg2:expression ) |
| { |
| #complementExpr = checkLogicalNotOp(#op2, #arg2); |
| } |
| ; |
| |
| primary |
| : castExpr |
| | literal |
| | VALUE |
| | THIS |
| | parameter |
| | staticFieldAccess |
| | fieldAccess |
| | navigation |
| | variableAccess |
| | #( CONTAINS expression VARIABLE ) |
| | #( NOT_CONTAINS expression VARIABLE ) |
| | startsWith |
| | endsWith |
| | isEmpty |
| | like |
| | substring |
| | indexOf |
| | length |
| | abs |
| | sqrt |
| ; |
| |
| castExpr |
| : #( c:TYPECAST t:type e:expression ) |
| { |
| #castExpr = checkCastOp(#c, #t, #e); |
| } |
| ; |
| |
| literal |
| { |
| Object value = null; |
| } |
| : value = l:literalHelper |
| { |
| #l.setType(VALUE); |
| #l.setValue(value); |
| } |
| ; |
| |
| literalHelper returns [Object value] |
| { |
| value = null; |
| } |
| : TRUE |
| { value = new Boolean(true); } |
| | FALSE |
| { value = new Boolean(false); } |
| | i:INT_LITERAL |
| { |
| try |
| { |
| value = Integer.decode(i.getText()); |
| } |
| catch (NumberFormatException ex) |
| { |
| errorMsg.error(i.getLine(), i.getColumn(), |
| I18NHelper.getMessage(messages, |
| "jqlc.optimizer.literal.invalid", //NOI18N |
| i.getJQLType().getName(), i.getText())); |
| } |
| } |
| | l:LONG_LITERAL |
| { |
| String txt = l.getText(); |
| char last = txt.charAt(txt.length() - 1); |
| if ((last == 'l') || (last == 'L')) |
| { |
| txt = txt.substring(0, txt.length() - 1); |
| } |
| try |
| { |
| value = Long.decode(txt); |
| } |
| catch (NumberFormatException ex) |
| { |
| errorMsg.error(l.getLine(), l.getColumn(), |
| I18NHelper.getMessage(messages, |
| "jqlc.optimizer.literal.invalid", //NOI18N |
| l.getJQLType().getName(), l.getText())); |
| } |
| } |
| | f:FLOAT_LITERAL |
| { |
| String txt = f.getText(); |
| char last = txt.charAt(txt.length() - 1); |
| if ((last == 'f') || (last == 'F')) |
| { |
| txt = txt.substring(0, txt.length() - 1); |
| } |
| try |
| { |
| value = new Float(txt); |
| } |
| catch (NumberFormatException ex) |
| { |
| errorMsg.error(f.getLine(), f.getColumn(), |
| I18NHelper.getMessage(messages, |
| "jqlc.optimizer.literal.invalid", //NOI18N |
| f.getJQLType().getName(), f.getText())); |
| } |
| } |
| | d:DOUBLE_LITERAL |
| { |
| String txt = d.getText(); |
| char last = txt.charAt(txt.length() - 1); |
| if ((last == 'd') || (last == 'd')) |
| { |
| txt = txt.substring(0, txt.length() - 1); |
| } |
| try |
| { |
| value = new Double(txt); |
| } |
| catch (NumberFormatException ex) |
| { |
| errorMsg.error(d.getLine(), d.getColumn(), |
| I18NHelper.getMessage(messages, |
| "jqlc.optimizer.literal.invalid", //NOI18N |
| d.getJQLType().getName(), d.getText())); |
| } |
| } |
| | c:CHAR_LITERAL |
| { value = new Character(parseChar(c.getText())); } |
| | s:STRING_LITERAL |
| { value = s.getText(); } |
| | n:NULL |
| { value = null; } |
| ; |
| |
| parameter |
| : p:PARAMETER |
| { |
| if (paramtab.inline(#p.getText())) { |
| #p.setType(VALUE); |
| #p.setValue(paramtab.getValueByName(#p.getText())); |
| } |
| } |
| ; |
| |
| staticFieldAccess |
| { |
| Object value = null; |
| } |
| : #( s:STATIC_FIELD_ACCESS t:TYPENAME i:IDENT) |
| { |
| // Calculate the value of the static field at compile time |
| // and treat it as constant value. |
| ClassType classType = (ClassType)t.getJQLType(); |
| FieldInfo fieldInfo = classType.getFieldInfo(i.getText()); |
| try |
| { |
| value = fieldInfo.getField().get(null); |
| #s.setType(VALUE); |
| #s.setValue(value); |
| #s.setFirstChild(null); |
| } |
| catch (IllegalAccessException e) |
| { |
| throw new JDOFatalUserException( |
| I18NHelper.getMessage(messages, |
| "jqlc.optimizer.staticfieldaccess.illegal", //NOI18N |
| i.getText(), classType.getName()), e); |
| } |
| } |
| ; |
| |
| fieldAccess |
| : #( f:FIELD_ACCESS o:expression name:IDENT ) |
| { |
| if (#o.getType() == VALUE) |
| { |
| // If the object of the field access is a constant value, |
| // evaluate the field access at compile time and |
| // treat the expression as constant value. |
| Object object = #o.getValue(); |
| ClassType classType = (ClassType)#o.getJQLType(); |
| Object value = CodeGeneration.getFieldValue(classType, object, |
| #name.getText()); |
| #f.setType(VALUE); |
| #f.setValue(value); |
| #f.setFirstChild(null); |
| } |
| } |
| ; |
| |
| navigation |
| : #( n:NAVIGATION o:expression name:IDENT ) |
| { |
| if (#o.getType() == VALUE) |
| { |
| // If the object of the navigation is a constant value, |
| // evaluate the field access at compile time and |
| // treat the expression as constant value. |
| Object object = #o.getValue(); |
| ClassType classType = (ClassType)#o.getJQLType(); |
| Object value = CodeGeneration.getFieldValue(classType, object, |
| #name.getText()); |
| #n.setType(VALUE); |
| #n.setValue(value); |
| #n.setFirstChild(null); |
| } |
| } |
| ; |
| |
| variableAccess |
| : #( VARIABLE ( expression )? ) |
| ; |
| |
| startsWith |
| : #( STARTS_WITH expression expression ) |
| ; |
| |
| endsWith |
| : #( ENDS_WITH expression expression ) |
| ; |
| |
| isEmpty |
| : #( op:IS_EMPTY e:expression) |
| { |
| if (#e.getType() == VALUE) |
| { |
| // If the expression that specifies the collection is a constant value, |
| // evaluate the isEmpty call at compile time and treat the expression |
| // as constant value. |
| Object object = #e.getValue(); |
| Object value = null; |
| if (object == null) |
| { |
| value = new Boolean(false); |
| } |
| else if (object instanceof Collection) |
| { |
| value = new Boolean(((Collection)object).isEmpty()); |
| } |
| else |
| { |
| errorMsg.fatal(I18NHelper.getMessage(messages, "jqlc.optimizer.isempty.requirecollection")); //NOI18N |
| } |
| #op.setType(VALUE); |
| #op.setValue(value); |
| #op.setFirstChild(null); |
| } |
| } |
| ; |
| |
| like |
| : #( LIKE expression expression ( expression )? ) |
| ; |
| |
| substring |
| : #( SUBSTRING expression expression expression ) |
| ; |
| |
| indexOf |
| : #( INDEXOF expression expression ( expression )? ) |
| ; |
| |
| length |
| : #( LENGTH expression ) |
| ; |
| |
| abs |
| : #( ABS expression ) |
| ; |
| |
| sqrt |
| : #( SQRT expression ) |
| ; |
| |
| // ---------------------- |
| // types |
| // ---------------------- |
| |
| type |
| : TYPENAME |
| | primitiveType |
| ; |
| |
| primitiveType |
| : BOOLEAN |
| | BYTE |
| | CHAR |
| | SHORT |
| | INT |
| | FLOAT |
| | LONG |
| | DOUBLE |
| ; |