Semantic.g revision ada1678a4262b208a7b87391f520a7767d25287c
/*
* 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.
*/
/*
* Semantic.g
*
* Created on March 8, 2000
*/
{
}
/**
* This class defines the semantic analysis of the JQL compiler.
* Input of this pass is the AST as produced by the parser,
* that consists of JQLAST nodes.
* The result is a typed JQLAST tree.
*
* @author Michael Bouschen
* @author Shing Wai Chan
* @version 0.1
*/
class Semantic extends TreeParser;
{
importVocab = JQL;
buildAST = true;
defaultErrorHandler = false;
}
{
/**
* I18N support
*/
protected final static ResourceBundle messages =
/**
* symbol table handling names of fields, variables and parameters
*/
protected SymbolTable symtab;
/**
* symbol table handling type names (candidate class and imported names)
*/
protected SymbolTable typeNames;
/**
* type table
*/
/**
* query parameter table
*/
protected ParameterTable paramtab;
/**
* variable table
*/
protected VariableTable vartab;
/**
*
*/
/**
* Result class for this query. This class is set by setClass.
*/
protected ClassType candidateClass;
/**
*
*/
{
this.symtab = new SymbolTable();
this.typeNames = new SymbolTable();
}
/**
*
*/
}
/**
*
*/
public void reportError(String s) {
}
/**
* Combines partial ASTs into one query AST.
*/
{
if (candidateClass != null)
if (importsAST != null)
if (orderingAST != null)
return query;
}
/**
* Creates the CLASS_DEF AST that represents the setClass value.
*/
{
{
"jqlc.semantic.checkcandidateclass.unknowntype", //NOI18N
}
}
/**
* This method analyses the expression of a single ordering definition.
* It checks whether the expression
* - is valid (see checkValidOrderingExpr)
* - is of a orderable type
* @param expr the expression of an ordering definition
*/
{
if (!exprType.isOrderable())
{
}
}
/**
* This method checks whether the ordering expression is valid.
* The following expressions are valid:
* - field access using the this object
* - navigation from a field of the this object
* @param expr the ordering definition
*/
{
{
case THIS:
case VARIABLE:
//OK;
break;
case FIELD_ACCESS:
case NAVIGATION:
{
// check first part of dot expr
}
break;
default:
}
}
/**
* This method checks whether the result expression is valid.
* The following expressions are valid:
* - field access using the this object
* - navigation from a field of the this object
* - variable access
* - distinct expression
* - aggreagte expression
* @param expr the result expression
*/
{
{
case THIS:
//OK;
break;
case FIELD_ACCESS:
case NAVIGATION:
{
// check first part of dot expr
}
break;
case VARIABLE:
// OK
break;
case DISTINCT:
break;
case AVG:
case SUM:
"jqlc.semantic.checkvalidresultexpr.invalidavgsumexpr", //NOI18N
}
break;
case MAX:
case MIN:
"jqlc.semantic.checkvalidresultexpr.invalidminmaxexpr", //NOI18N
}
break;
case COUNT:
break;
default:
}
}
/**
* Checks that result and ordering are compatible.
* If the query result is a field, then it must be the same as ordering
* item. If the query is an object, then ordering expression must
* have the same navigation prefix of the result expression.
*/
return;
}
boolean hasResultDistinct = false;
hasResultDistinct = true;
}
// skip RESULT_DEF node
}
// skip DISTINCT node
hasResultDistinct = true;
}
if (!hasResultDistinct) {
return;
}
// share buf
"jqlc.semantic.checkresultordering.invalidorderingfordistinctresultfield", //NOI18N
}
}
// share buf
"jqlc.semantic.checkresultordering.invalidorderingfordistinctresult", //NOI18N
}
}
}
}
/**
* Form a string representation of a dot expression and append to given
* StringBuffer.
* @param ast the AST node representing the root the of the expression
* @param buf the StringBuffer that will have result of path expression
* append
*/
return;
}
case FIELD_ACCESS:
case STATIC_FIELD_ACCESS:
case NAVIGATION:
break;
default:
break;
}
}
/**
* This method analyses a dot expression of the form expr.ident or
* expr.ident(params) where expr itself can again be a dot expression.
* It checks whether the dot expression is
* - part of a qualified class name specification
* - field access,
* - a method call
* The method returns a temporary single AST node that is defined with a
* specific token type (field access, method call, etc.). This node also
* contains the type of the dot expression.
* @param expr the left hand side of the dot expression
* @param ident the right hand side of the dot expression
* @param args arguments (in the case of a call)
* @return AST node representing the specialized dot expr
*/
{
{
// left expression is of a class type
{
// no paranethesis specified => field access
{
return dot;
}
{
// access of the form: className.staticField
}
else
{
// access of the form: object.field
}
}
else
{
// parenthesis specified => method call
{
}
{
}
{
}
return dot;
}
}
else
{
return dot;
}
}
/**
*
*/
{
if (classType.isPersistenceCapable())
{
if (!fieldInfo.isPersistent())
{
}
{
}
else
{
}
}
else
{
{
}
}
return access;
}
/**
*
*/
{
{
}
{
}
return access;
}
/**
* This method analyses and identifier defined in the current scope:
* - a field, variable or parameter defined in the symbol table
* - a type define in a separate symbol table for type names
* @param ident the identifier AST
* @param def the entry in the symbol table of the type names tables
* @return AST node representing a defined identifier
*/
{
{
}
{
}
{
{
}
else
{
}
}
{
}
else
{
"jqlc.semantic.analysedefinedidentifier.illegalident", //NOI18N
}
return ident;
}
/**
* Analyses a call for an object that implements Collection.
* Currently, contains is the only valid Collection method in a query filter.
*/
{
{
return dot;
}
{
// isEmpty does not take parameters
return dot;
}
return dot;
}
/**
* Analyses a call for an object of type String.
* Currently startsWith and endsWith are the only valid String methods in a query filter
*/
{
{
}
{
}
{
}
{
}
{
}
{
// length does not take parameters
}
else
{
}
return dot;
}
/**
* Analyses a java.lang.Math call.
*/
{
{
}
{
}
else
{
}
return dot;
}
/**
* This method checks the specified node (args) representing an empty
* argument list.
*/
{
{
"jqlc.semantic.generic.arguments.numbermismatch")); //NOI18N
}
}
/**
* This method checks the specified node (args) representing an argument
* list which consists of a single argument of type String.
*/
{
{
"jqlc.semantic.generic.arguments.numbermismatch")); //NOI18N
}
{
"jqlc.semantic.generic.arguments.numbermismatch")); //NOI18N
}
else
{
{
"jqlc.semantic.generic.arguments.typemismatch", //NOI18N
}
}
}
/**
* This method checks the specified node (args) representing a valid contains
* argument list: one argument denoting a variable.
*/
{
{
"jqlc.semantic.generic.arguments.numbermismatch")); //NOI18N
}
{
"jqlc.semantic.generic.arguments.numbermismatch")); //NOI18N
}
{
"jqlc.semantic.analysecollectioncall.nonvariable")); //NOI18N
}
else
{
if (collectionFieldInfo == null)
{
"jqlc.semantic.analysecollectioncall.unsupportedcollectionexpr", //NOI18N
collection.getText()));
}
else if (!collectionFieldInfo.isRelationship())
{
// check compatibilty of collection element type and type of variable
"jqlc.semantic.analysecollectioncall.relationshipexpected", //NOI18N
}
{
"jqlc.semantic.analysecollectioncall.typemismatch", //NOI18N
}
}
}
/**
* This method checks the specified node (args) representing a valid like
* argument list: a string argument plus an optional char argument.
*/
{
{
"jqlc.semantic.generic.arguments.numbermismatch")); //NOI18N
}
{
"jqlc.semantic.generic.arguments.numbermismatch")); //NOI18N
}
else
{
// check type of first arg
{
"jqlc.semantic.generic.arguments.typemismatch", //NOI18N
}
// check type of second arg (if available)
{
{
"jqlc.semantic.generic.arguments.typemismatch", //NOI18N
}
}
}
}
/**
* This method checks the specified node (args) representing an argument
* list which consists of two integer arguments.
*/
{
{
// no args specified
"jqlc.semantic.generic.arguments.numbermismatch")); //NOI18N
}
{
// one arg specified
"jqlc.semantic.generic.arguments.numbermismatch")); //NOI18N
}
{
// more than two args specified
"jqlc.semantic.generic.arguments.numbermismatch")); //NOI18N
}
else
{
// specified two args
// check type of first arg
{
"jqlc.semantic.generic.arguments.typemismatch", //NOI18N
}
// check type of second arg
{
"jqlc.semantic.generic.arguments.typemismatch", //NOI18N
}
}
}
/**
* This method checks the specified node (args) representing a valid indexOf
* argument list: a string argument plus an optional char argument.
*/
{
{
"jqlc.semantic.generic.arguments.numbermismatch")); //NOI18N
}
{
"jqlc.semantic.generic.arguments.numbermismatch")); //NOI18N
}
else
{
// check type of first arg
{
"jqlc.semantic.generic.arguments.typemismatch", //NOI18N
}
// check type of second arg (if available)
{
{
"jqlc.semantic.generic.arguments.typemismatch", //NOI18N
}
}
}
}
/**
* This method checks the specified node (args) representing a valid abs
* argument list: a single number argument.
*/
{
{
"jqlc.semantic.generic.arguments.numbermismatch")); //NOI18N
}
{
"jqlc.semantic.generic.arguments.numbermismatch")); //NOI18N
}
else
{
{
"jqlc.semantic.generic.arguments.typemismatch", //NOI18N
}
}
}
/**
* This method checks the specified node (args) representing a valid sqrt
* argument list: a single argument of type double or Double.
*/
{
{
"jqlc.semantic.generic.arguments.numbermismatch")); //NOI18N
}
{
"jqlc.semantic.generic.arguments.numbermismatch")); //NOI18N
}
else
{
{
"jqlc.semantic.generic.arguments.typemismatch", //NOI18N
}
}
}
/**
*
*/
{
{
case FIELD_ACCESS:
case NAVIGATION:
{
}
errorMsg.fatal(I18NHelper.getMessage(messages, "jqlc.semantic.getcollectionfield.missingchildren")); //NOI18N
break;
case TYPECAST:
{
}
errorMsg.fatal(I18NHelper.getMessage(messages, "jqlc.semantic.getcollectionfield.missingchildren")); //NOI18N
break;
}
return null;
}
/**
* @param leftAST left operand
* @param rightAST right operand
* @return Type
*/
{
// handle error type
{
case BAND:
case BOR:
return typetab.booleanType;
{
}
break;
case BXOR:
{
"jqlc.semantic.analysebitwiseexpr.exclusiveorop")); //NOI18N
}
{
}
break;
}
// if this code is reached a bitwise operator was used with invalid arguments
}
/**
* Analyses a boolean conditional operation (&&, ||)
* @param op the conditional operator
* @param leftAST left operand
* @param rightAST right operand
* @return Type
*/
{
// handle error type
{
case AND:
case OR:
return typetab.booleanType;
break;
}
// if this code is reached a conditional operator was used with invalid arguments
}
/**
* Analyses a relational operation (<, <=, >, >=, ==, !=)
* @param op the relational operator
* @param leftAST left operand
* @param rightAST right operand
* @return Type
*/
{
// handle error type
// special check for <, <=, >, >=
// left and right hand types must be orderable
{
case LT:
case LE:
case GT:
case GE:
if (!left.isOrderable())
{
}
if (!right.isOrderable())
{
}
break;
case EQUAL:
case NOT_EQUAL:
{
}
break;
}
// check for numeric types, numeric wrapper class types and math class types
return typetab.booleanType;
// check for boolean and java.lang.Boolean
return typetab.booleanType;
return typetab.booleanType;
// if this code is reached a conditional operator was used with invalid arguments
}
/**
* Analyses a
* @param op the operator
* @param leftAST left operand
* @param rightAST right operand
* @return Type
*/
{
// handle error type
{
// handle java.math.BigDecimal
return left;
return right;
// handle java.math.BigInteger
{
// if right is floating point return BigDecimal,
// otherwise return BigInteger
}
{
// if left is floating point return BigDecimal,
// otherwise return BigInteger
}
boolean wrapper = false;
if (left instanceof NumericWrapperClassType)
{
wrapper = true;
}
if (right instanceof NumericWrapperClassType)
{
wrapper = true;
}
// handle numeric types with arbitrary arithmetic operator
{
{
}
return promotedType;
}
}
{
// handle + for strings
// MBO: note, this if matches char + char (which it should'nt),
// but this case is already handled above
{
return typetab.stringType;
}
}
// if this code is reached a conditional operator was used with invalid arguments
}
/**
* Analyses a
* @param op the operator
* @param argAST right operand
* @return Type
*/
{
// handle error type
// handle java.math.BigDecimal and java.math.BigInteger
return arg;
// handle java.math.BigInteger
return arg;
boolean wrapper = false;
if (arg instanceof NumericWrapperClassType)
{
wrapper = true;
}
if (arg instanceof NumericType)
{
{
}
return promotedType;
}
// if this code is reached a conditional operator was used with invalid arguments
}
/**
* Analyses a
* @param op the operator
* @param argAST right operand
* @return Type
*/
{
// handle error type
{
case BNOT:
{
return arg;
}
break;
case LNOT:
{
{
}
return arg;
}
break;
}
// if this code is reached a conditional operator was used with invalid arguments
}
/**
*
*/
{
}
/**
*
*/
{
{
case VARIABLE:
break;
case CONTAINS:
break;
case BOR:
case BXOR:
case OR:
// prepare tab copy for right hand side and merge the right hand side copy into vartab
break;
default:
{
}
break;
}
}
}
// rules
: #( QUERY
{
symtab.enterScope();
}
{
// enter new scope for variable and parameter names
symtab.enterScope();
}
o:ordering
r:result
{
// leaves variable and parameter name scope
symtab.leaveScope();
// leaves global scope
symtab.leaveScope();
}
)
{
checkResultOrdering(#r, #o);
}
;
// ----------------------------------
// rules: candidate class
// ----------------------------------
{
}
: c:CLASS_DEF
{
// check persistence capable
if (!candidateClass.isPersistenceCapable())
{
className));
}
// get base name
// init symbol table with field names of the candidate class
{
}
}
;
// ----------------------------------
// rules: import declaration
// ----------------------------------
{
}
: ( declareImport )*
;
{
{
}
// get base name
{
}
}
;
// ----------------------------------
// rules: parameter declaration
// ----------------------------------
{
}
: ( declareParameter )*
;
{
{
}
#i.setJQLType(type);
}
;
// ----------------------------------
// rules: variable declaration
// ----------------------------------
{
}
: ( declareVariable )*
;
{
{
}
#i.setJQLType(type);
}
;
// ----------------------------------
// rules: ordering specification
// ----------------------------------
{
}
: ( orderSpec )*
;
{
checkConstraints(#e, vartab);
}
;
// ----------------------------------
// rules: result expression
// ----------------------------------
{
}
: #( r:RESULT_DEF e:resultExpr )
{
checkValidResultExpr(#e);
#r.setJQLType(#e.getJQLType());
checkConstraints(#e, vartab);
}
| // empty rule
;
{
}
{
}
{
}
{
}
{
}
| #( c:COUNT resultExpr )
{
}
;
// ----------------------------------
// rules: filer expression
// ----------------------------------
{
}
// There is always a filter defined and it is the last node of the query tree.
// Otherwise all the remaining subtrees after the CLASS_DEF subtree are empty
// which results in a ClassCastException antlr.ASTNullType when analysis
// the (non existsent) subtrees
: #( FILTER_DEF e:expression )
{
{
// filter expression must have the type boolean or java.lang.Boolean
}
checkConstraints(#e, vartab);
}
;
: repr = e:exprNoCheck[false]
{
{
}
}
;
// inside a dot expression
;
{
}
{
}
{
}
;
{
}
{
}
;
{
}
{
{
}
{
}
}
{
{
}
{
}
}
{
}
{
}
{
}
{
}
;
{
{
// change the operator from PLUS to CONCAT in the case of string concatenation
}
}
{
}
{
}
{
}
{
}
;
{
}
{
}
;
{
}
{
}
;
{
{
}
#c.setJQLType(type);
}
| literal
| i:THIS
{ #i.setJQLType(candidateClass); }
;
{
}
)
{
{
{
// name does not define a valid class => return qualifiedName
}
{
// found valid class name and NO arguments specified
// => use of the class name
}
else
{
// found valid class name and arguments specified =>
// looks like constructor call
}
}
else // no string repr of left hand side => expression is defined
{
}
}
;
: #( ARG_LIST (expression)* )
;
{
}
{
// check args, if defined => invalid method call
{
}
{
}
else if (insideDotExpr)
{
{
}
else
{
}
}
else
{
}
}
;
;
{
}
| #( d:DOT
{
}
)
;
{
{
{
}
else
{
}
}
else
{
{
// ckeck java.lang class without package name
}
{
}
}
// change AST to a single node that represents the full class name
}
| p:primitiveType
{
}
;
: BOOLEAN
| BYTE
| CHAR
| SHORT
| INT
| FLOAT
| LONG
| DOUBLE
;