/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
/*
* Optimizer.g
*
* Created on June 11, 2001
*/
{
}
/**
* 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;
{
importVocab = JQL;
buildAST = true;
defaultErrorHandler = false;
}
{
/**
* I18N support
*/
protected final static ResourceBundle messages =
/**
* type table
*/
/**
* query parameter table
*/
protected ParameterTable paramtab;
/**
*
*/
/**
*
*/
{
}
/**
*
*/
}
/**
*
*/
public void reportError(String s) {
}
/**
* Converts the string argument into a single char.
*/
{
if (first == '\\')
{
//found escape => check the next char
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
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
// octal spec
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
*/
{
if (isBooleanValueAST(left))
{
}
else if (isBooleanValueAST(right))
{
}
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
*/
{
if (isBooleanValueAST(left))
{
}
else if (isBooleanValueAST(right))
{
}
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
*/
boolean negate)
{
// case <VALUE> <op> <VALUE>
{
}
// case <boolean VALUE> <op> <expr>
else if (isBooleanValueAST(left))
{
}
// case <expr> <op> <boolean VALUE>
else if (isBooleanValueAST(right))
{
}
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
*/
boolean 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
*/
{
if (isLeftConstant && isRightConstant)
{
}
{
// 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
}
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
*/
{
{
// !value may be calculated at compile time.
}
else
{
}
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
*/
{
{
value = rightValue;
else if (rightValue == null)
else
{
if (type instanceof NumericWrapperClassType)
else
"jqlc.optimizer.checkbinaryplusop.invalidtype", //NOI18N
}
}
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
*/
{
{
value = rightValue;
else if (rightValue == null)
else
}
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
*/
{
{
if (rightValue == null)
else
{
if (type instanceof NumericWrapperClassType)
else
"jqlc.optimizer.checkbinaryminusop.invalidtype", //NOI18N
}
}
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
*/
{
{
if (rightValue == null)
if (type instanceof NumericWrapperClassType)
else
"jqlc.optimizer.checkmultiplicationop.invalidtype", //NOI18N
}
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
*/
{
{
if (rightValue == null)
// division by zero!
if (type instanceof NumericWrapperClassType)
else
"jqlc.optimizer.checkdivisionop.invalidtype", //NOI18N
}
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
*/
{
{
if (rightValue == null)
// division by zero!
if (type instanceof NumericWrapperClassType)
{
//use ROUND_HALF_EVEN so that it is consistent with div
}
else
"jqlc.optimizer.checkmodop.invalidtype", //NOI18N
}
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
*/
{
{
if (type instanceof NumberType)
else
"jqlc.optimizer.checkunaryminusop.invalidtype", //NOI18N
}
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
*/
{
{
if (type instanceof NumericWrapperClassType)
// If non of the above type applies, leave the value as it is
// convert the TYPECAST op into a VALUE
}
return ast;
}
/**
* Converts the specified value into a BigDecimal value.
* @param value value to be converted
* @return BigDecimal representation
*/
{
else
"jqlc.optimizer.getbigdecimalvalue.notnumber", //NOI18N
return ret;
}
/**
* Converts the specified value into a BigInteger value.
* @param value value to be converted
* @return BigInteger representation
*/
{
else
"jqlc.optimizer.getbigintegervalue.notnumber", //NOI18N
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
*/
boolean negate)
{
boolean value = false;
{
// both values are null -> true
value = true;
}
{
// both values are not null -> use equals
}
else
{
// one value is null, the other is not null -> false
value = false;
}
if (negate)
{
}
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
*/
{
if (skip)
{
// expr == true -> expr
// expr != false -> 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
{
case EQUAL:
break;
case NOT_EQUAL:
break;
case LNOT:
break;
default:
}
}
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
*/
{
{
// true AND expr -> expr
// expr AND true -> expr
}
else
{
// false AND expr -> false
// expr AND false -> false
}
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
*/
{
{
// true OR expr -> true
// expr OR true -> true
}
else
{
// false OR expr -> expr
// expr OR false -> expr
}
return ast;
}
/**
* Returns true if the specified AST represents a constant boolean value.
*/
{
}
/**
* Returns true if the specified AST represents a datastore value.
*/
{
{
case FIELD_ACCESS :
case NAVIGATION :
return true;
case TYPECAST :
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.
*/
{
{
case AND:
case BAND:
{
// found AND ( CONTAINS, right ), so check right for special
// variable treatement
}
{
// found AND ( left, CONTAINS, ), so check left for special
// variable treatement
}
else
{
}
break;
case OR:
case BOR:
break;
case LNOT:
// This is !(!arg) => return arg
break;
default:
// wrap arg into not operator
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.
*/
{
{
case AND:
case BAND:
case OR:
case BOR:
{
}
break;
default:
{
}
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.
*/
{
{
case CONTAINS:
case NOT_CONTAINS:
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.
*/
{
return false;
boolean found = false;
{
found = true;
{
}
}
{
found = true;
}
return found;
}
/**
* Inverts the specified node: AND -> OR, == -> !=, etc.
*/
{
{
case AND:
break;
case BAND:
break;
case OR:
break;
case BOR:
break;
case EQUAL:
break;
case NOT_EQUAL:
break;
case LT:
break;
case LE:
break;
case GT:
break;
case GE:
break;
}
}
/** Builds a binary tree. */
{
return root;
}
/** */
{
return root;
}
}
// rules
: #( q:QUERY
)
;
// ----------------------------------
// rules: candidate class
// ----------------------------------
{
}
;
// ----------------------------------
// rules: parameter declaration
// ----------------------------------
{
}
: ( declareParameter )*
;
;
// ----------------------------------
// rules: variable declaration
// ----------------------------------
{
}
: ( declareVariable )*
;
;
// ----------------------------------
// rules: ordering specification
// ----------------------------------
{
}
: ( orderSpec )*
;
;
// ----------------------------------
// rules: result expression
// ----------------------------------
{
}
: #( r:RESULT_DEF resultExpr )
| // empty rule
;
: #( DISTINCT resultExpr )
| #( AVG resultExpr )
| #( MAX resultExpr )
| #( MIN resultExpr )
| #( SUM resultExpr )
| #( COUNT resultExpr )
;
// ----------------------------------
// rules: filer expression
// ----------------------------------
{
}
: #( FILTER_DEF expression )
;
: primary
;
{
}
{
}
;
{
}
{
}
;
{
}
{
}
{
}
{
}
{
}
{
}
;
{
}
{
}
{
}
{
}
{
}
{
}
;
: #( UNARY_PLUS expression )
{
}
;
{
// calling literal here directly does not work properly
// the following logic need to be in sync with that of literal
#unaryMinusLiteralExpr = #li;
}
;
{
}
;
: castExpr
| literal
| VALUE
| THIS
| endsWith
| isEmpty
| like
| indexOf
| length
| abs
| sqrt
;
{
#castExpr = checkCastOp(#c, #t, #e);
}
;
{
}
: value = l:literalHelper
{
}
;
{
}
: TRUE
| FALSE
| i:INT_LITERAL
{
try
{
}
catch (NumberFormatException ex)
{
"jqlc.optimizer.literal.invalid", //NOI18N
}
}
| l:LONG_LITERAL
{
{
}
try
{
}
catch (NumberFormatException ex)
{
"jqlc.optimizer.literal.invalid", //NOI18N
}
}
| f:FLOAT_LITERAL
{
{
}
try
{
}
catch (NumberFormatException ex)
{
"jqlc.optimizer.literal.invalid", //NOI18N
}
}
| d:DOUBLE_LITERAL
{
{
}
try
{
}
catch (NumberFormatException ex)
{
"jqlc.optimizer.literal.invalid", //NOI18N
}
}
| c:CHAR_LITERAL
| s:STRING_LITERAL
| n:NULL
;
: p:PARAMETER
{
}
}
;
{
}
{
// Calculate the value of the static field at compile time
// and treat it as constant value.
try
{
#s.setFirstChild(null);
}
catch (IllegalAccessException e)
{
throw new JDOFatalUserException(
"jqlc.optimizer.staticfieldaccess.illegal", //NOI18N
}
}
;
{
{
// 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.
#f.setFirstChild(null);
}
}
;
{
{
// If the object of the navigation is a constant value,
// evaluate the field access at compile time and
// treat the expression as constant value.
#n.setFirstChild(null);
}
}
;
: #( VARIABLE ( expression )? )
;
;
;
{
{
// 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.
{
}
else if (object instanceof Collection)
{
}
else
{
errorMsg.fatal(I18NHelper.getMessage(messages, "jqlc.optimizer.isempty.requirecollection")); //NOI18N
}
}
}
;
;
;
;
: #( LENGTH expression )
;
: #( ABS expression )
;
: #( SQRT expression )
;
// ----------------------
// types
// ----------------------
: TYPENAME
;
: BOOLEAN
| BYTE
| CHAR
| SHORT
| INT
| FLOAT
| LONG
| DOUBLE
;