You are viewing a plain text version of this content. The canonical link for it is here.
Posted to jdo-commits@db.apache.org by mb...@apache.org on 2005/05/22 20:09:00 UTC
svn commit: r171353 [5/13] - in /incubator/jdo/trunk/query20: ./ src/
src/conf/ src/java/ src/java/org/ src/java/org/apache/
src/java/org/apache/jdo/ src/java/org/apache/jdo/impl/
src/java/org/apache/jdo/impl/jdoql/
src/java/org/apache/jdo/impl/jdoql/jdoqlc/
src/java/org/apache/jdo/impl/jdoql/scope/
src/java/org/apache/jdo/impl/jdoql/tree/ src/java/org/apache/jdo/jdoql/
src/java/org/apache/jdo/jdoql/tree/
Added: incubator/jdo/trunk/query20/src/java/org/apache/jdo/impl/jdoql/jdoqlc/Semantic.g
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/query20/src/java/org/apache/jdo/impl/jdoql/jdoqlc/Semantic.g?rev=171353&view=auto
==============================================================================
--- incubator/jdo/trunk/query20/src/java/org/apache/jdo/impl/jdoql/jdoqlc/Semantic.g (added)
+++ incubator/jdo/trunk/query20/src/java/org/apache/jdo/impl/jdoql/jdoqlc/Semantic.g Sun May 22 11:08:57 2005
@@ -0,0 +1,1727 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Semantic.g
+ *
+ * Created on August 28, 2001
+ */
+
+header
+{
+ package org.apache.jdo.impl.jdoql.jdoqlc;
+
+ import java.util.Collection;
+
+ import org.apache.jdo.model.java.JavaType;
+ import org.apache.jdo.model.java.JavaField;
+ import org.apache.jdo.impl.model.java.ErrorType;
+ import org.apache.jdo.impl.model.java.NullType;
+ import org.apache.jdo.impl.model.java.PrimitiveType;
+ import org.apache.jdo.impl.model.java.WrapperClassType;
+ import org.apache.jdo.impl.model.java.PredefinedType;
+
+ import org.apache.jdo.impl.jdoql.tree.*;
+ import org.apache.jdo.impl.jdoql.scope.ParameterTable;
+ import org.apache.jdo.impl.jdoql.scope.SymbolTable;
+ import org.apache.jdo.impl.jdoql.scope.TypeNames;
+ import org.apache.jdo.impl.jdoql.scope.VariableTable;
+
+ import org.apache.jdo.util.I18NHelper;
+}
+
+/**
+ * This class defines the semantic analysis of the JDOQL compiler.
+ * Input of this pass is the AST as produced by the parser,
+ * that consists of JDOQLAST nodes.
+ * The result is a typed JDOQLAST tree.
+ * <p>
+ * TBD:
+ * <ul>
+ * <li> Check for non portable contains queries
+ * </ul>
+ *
+ * @author Michael Bouschen
+ */
+class Semantic extends TreeParser;
+
+options
+{
+ importVocab = JDOQL;
+ buildAST = true;
+ defaultErrorHandler = false;
+ ASTLabelType = "JDOQLAST"; //NOI18N
+}
+
+{
+ /** The error message support class. */
+ protected ErrorMsg errorMsg;
+
+ /** Symbol table handling names of variables and parameters. */
+ protected SymbolTable symtab;
+
+ /** Table of type names handling imports. */
+ protected TypeNames typeNames;
+
+ /** The type support. */
+ protected TypeSupport typeSupport;
+
+ /** The query parameter table */
+ protected ParameterTable paramtab;
+
+ /** The variable table. */
+ protected VariableTable vartab;
+
+ /** The variable checker. */
+ protected VariableChecker varChecker;
+
+ /** Candidate class. */
+ protected JavaType candidateClass;
+
+ /** I18N support */
+ protected final static I18NHelper msg = I18NHelper.getInstance(
+ "org.apache.jdo.impl.jdoql.Bundle", Semantic.class.getClassLoader()); //NOI18N
+
+ /**
+ *
+ */
+ public void init(TypeSupport typeSupport, ParameterTable paramtab,
+ VariableTable vartab, ErrorMsg errorMsg)
+ {
+ this.errorMsg = errorMsg;
+ this.symtab = new SymbolTable();
+ this.typeNames = new TypeNames(typeSupport);
+ this.vartab = vartab;
+ this.typeSupport = typeSupport;
+ this.paramtab = paramtab;
+ this.varChecker = new VariableChecker();
+ }
+
+ /**
+ *
+ */
+ public void reportError(RecognitionException ex) {
+ errorMsg.fatal(msg.msg("ERR_SemanticError"), ex); //NOI18N
+ }
+
+ /**
+ *
+ */
+ public void reportError(String s) {
+ errorMsg.fatal(msg.msg("ERR_SemanticError") + s); //NOI18N
+ }
+
+ /**
+ * Combines partial ASTs into one query AST.
+ */
+ public JDOQLAST createQueryTree(Class candidateClass, JDOQLAST importsAST,
+ JDOQLAST paramsAST, JDOQLAST varsAST,
+ JDOQLAST orderingAST, JDOQLAST filterAST)
+ {
+ CandidateClassImpl candidateClassAST = new CandidateClassImpl();
+ candidateClassAST.setType(CANDIDATE_CLASS);
+ candidateClassAST.setText(candidateClass.getName());
+ candidateClassAST.setCandidateClass(candidateClass);
+ JDOQLAST query = new NodeImpl();
+ query.setType(QUERY_TREE);
+ query.setText("query"); //NOI18N
+ query.addChild(candidateClassAST);
+ if (importsAST != null)
+ query.addChild(importsAST);
+ if (paramsAST != null)
+ query.addChild(paramsAST);
+ if (varsAST != null)
+ query.addChild(varsAST);
+ if (orderingAST != null)
+ query.addChild(orderingAST);
+ if (filterAST != null)
+ query.addChild(filterAST);
+ return query;
+ }
+
+ /**
+ * This method analyses the expression of a single ordering definition.
+ * It checks whether the expression
+ * - is of a orderable type
+ * @param expr the expression of an ordering definition
+ */
+ protected void analyseOrderingExpression(JDOQLAST expr)
+ {
+ JavaType exprType = expr.getTypeInfo();
+ if (!exprType.isOrderable())
+ {
+ errorMsg.error(expr.getLine(), expr.getColumn(),
+ msg.msg("EXC_NotSortableInOrdering", //NOI18N
+ exprType.getName()));
+ expr.setTypeInfo(ErrorType.errorType);
+ }
+ }
+
+ /**
+ * 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
+ */
+ protected JDOQLAST analyseDotExpr(JDOQLAST dot, JDOQLAST expr,
+ JDOQLAST ident, JDOQLAST args)
+ {
+ JavaType exprType = expr.getTypeInfo();
+ String name = ident.getText();
+ dot.setText(expr.getText() + '.' + name);
+ if (!exprType.isPrimitive()) {
+ // left expression is of a class type
+ if (args == null) {
+ // no paranethesis specified => field access
+ JavaField javaField = exprType.getJavaField(name);
+ if (javaField == null) {
+ errorMsg.error(ident.getLine(), ident.getColumn(),
+ msg.msg("EXC_UnknownField", //NOI18N
+ ident.getText(), exprType.getName()));
+ dot.setTypeInfo(ErrorType.errorType);
+ ident.setTypeInfo(ErrorType.errorType);
+ return dot;
+ }
+ else if (expr.getType() == TYPE) {
+ // access of the form: className.staticField
+ JDOQLAST fieldAccess = analyseStaticFieldAccess(
+ expr, ident, exprType, javaField);
+ fieldAccess.setLine(#dot.getLine());
+ fieldAccess.setColumn(#dot.getColumn());
+ return fieldAccess;
+ }
+ else {
+ // access of the form: object.field
+ JDOQLAST fieldAccess =
+ analyseFieldAccess(expr, ident, exprType, javaField);
+ fieldAccess.setLine(#dot.getLine());
+ fieldAccess.setColumn(#dot.getColumn());
+ return fieldAccess;
+ }
+ }
+ else {
+ // parenthesis specified => method call
+ if (exprType.isJDOSupportedCollection()) {
+ JDOQLAST call =
+ analyseCollectionCall(dot, expr, ident, args);
+ call.setLine(#dot.getLine());
+ call.setColumn(#dot.getColumn());
+ return call;
+ }
+ else if (exprType.equals(PredefinedType.stringType)) {
+ JDOQLAST call = analyseStringCall(dot, expr, ident, args);
+ call.setLine(#dot.getLine());
+ call.setColumn(#dot.getColumn());
+ return call;
+ }
+ errorMsg.error(dot.getLine(), dot.getColumn(),
+ msg.msg("EXC_InvalidMethodCall")); //NOI18N
+ dot.setTypeInfo(ErrorType.errorType);
+ return dot;
+ }
+ }
+ else {
+ errorMsg.error(expr.getLine(), expr.getColumn(),
+ msg.msg("EXC_ClassTypeExpressionExpected", //NOI18N
+ ident.getText(), exprType.getName()));
+ dot.setTypeInfo(ErrorType.errorType);
+ return dot;
+ }
+ }
+
+ /**
+ *
+ */
+ protected JDOQLAST analyseFieldAccess(
+ JDOQLAST objectExpr, JDOQLAST ident, JavaType classType,
+ JavaField javaField)
+ {
+ FieldAccessExpr fieldAccess = new FieldAccessExpr();
+ String name = ident.getText();
+ JavaType fieldType = javaField.getType();
+ int tokenType = (classType.isPersistenceCapable() &&
+ fieldType.isPersistenceCapable()) ?
+ NAVIGATION : FIELD_ACCESS;
+ fieldAccess.initialize(tokenType, objectExpr.getText() + '.' + name,
+ fieldType);
+ fieldAccess.setName(name);
+ fieldAccess.setFirstChild(objectExpr);
+ objectExpr.setNextSibling(null);
+ return fieldAccess;
+ }
+
+ /**
+ *
+ */
+ protected JDOQLAST analyseStaticFieldAccess(
+ JDOQLAST typename, JDOQLAST ident, JavaType classType,
+ JavaField javaField)
+ {
+ String name = ident.getText();
+ if (!typeSupport.isStaticField(javaField)) {
+ errorMsg.error(ident.getLine(), ident.getColumn(),
+ msg.msg("EXC_InvalidStaticReference", //NOI18N
+ name, classType.getName()));
+ }
+ StaticFieldAccessExpr fieldAccess = new StaticFieldAccessExpr();
+ fieldAccess.initialize(STATIC_FIELD_ACCESS,
+ typename.getText() + '.' + name,
+ javaField.getType());
+ fieldAccess.setName(name);
+ fieldAccess.setFirstChild(typename);
+ typename.setNextSibling(null);
+ return fieldAccess;
+ }
+
+ /**
+ * This method analyses an identifier defined in the current scope which
+ * is a field, variable or parameter defined in the symbol table.
+ * @param ident the identifier AST
+ * @return AST node representing a defined identifier
+ */
+ protected JDOQLAST analyseDefinedIdentifier(JDOQLAST ident)
+ {
+ JDOQLAST ast = null;
+ String name = ident.getText();
+ Decl decl = (Decl)symtab.getDeclaration(name);
+ if (decl != null) {
+ if (decl instanceof VariableDecl) {
+ ast = new VariableAccessExpr();
+ ast.initialize(VARIABLE_ACCESS, name, decl.getTypeInfo());
+ ast.setLine(ident.getLine());
+ ast.setColumn(ident.getColumn());
+ }
+ else if (decl instanceof ParameterDecl) {
+ ast = new ParameterAccessExpr();
+ ast.initialize(PARAMETER_ACCESS, name, decl.getTypeInfo());
+ ast.setLine(ident.getLine());
+ ast.setColumn(ident.getColumn());
+ }
+ }
+ else {
+ JavaField javaField = candidateClass.getJavaField(name);
+ if (javaField != null) {
+ ThisExpr thisAST = new ThisExpr();
+ thisAST.initialize(THIS, "this", candidateClass); //NOI18N
+ ast = analyseFieldAccess(thisAST, ident, candidateClass,
+ javaField);
+ ast.setLine(ident.getLine());
+ ast.setColumn(ident.getColumn());
+ }
+ }
+ return ast;
+ }
+
+ /**
+ * Analyses a call for an object that implements Collection.
+ * Currently, only contains and isEmpty are supported.
+ */
+ protected JDOQLAST analyseCollectionCall(JDOQLAST dot, JDOQLAST collection,
+ JDOQLAST method, JDOQLAST args)
+ {
+ String methodName = method.getText();
+ JDOQLAST call = null;
+ JDOQLAST firstArg = (JDOQLAST)args.getFirstChild();
+ if (methodName.equals("contains")) { //NOI18N
+ call = new ContainsCallExpr();
+ call.initialize(CONTAINS, methodName, PredefinedType.booleanType);
+ checkContainsArgs(collection, method, firstArg);
+ call.setFirstChild(collection);
+ collection.setNextSibling(firstArg);
+ }
+ else if (methodName.equals("isEmpty")) { //NOI18N
+ call = new IsEmptyCallExpr();
+ call.initialize(IS_EMPTY, methodName, PredefinedType.booleanType);
+ checkIsEmptyArgs(firstArg);
+ call.setFirstChild(collection);
+ collection.setNextSibling(null);
+ }
+ else {
+ errorMsg.error(dot.getLine(), dot.getColumn(),
+ msg.msg("EXC_InvalidMethodCall")); //NOI18N
+ call = new IsEmptyCallExpr();
+ call.initialize(IS_EMPTY, methodName, ErrorType.errorType);
+ }
+ return call;
+ }
+
+ /**
+ * Check the arguments of a contains call.
+ */
+ protected void checkContainsArgs(JDOQLAST collection, JDOQLAST method,
+ JDOQLAST args)
+ {
+ JDOQLAST firstArg = args;
+ if (firstArg == null) {
+ errorMsg.error(method.getLine(), method.getColumn(),
+ msg.msg("EXC_WrongNumberOfArgs")); //NOI18N
+ }
+ else if (firstArg.getNextSibling() != null) {
+ JDOQLAST nextArg = (JDOQLAST)firstArg.getNextSibling();
+ errorMsg.error(nextArg.getLine(), nextArg.getColumn(),
+ msg.msg("EXC_WrongNumberOfArgs")); //NOI18N
+ }
+ else {
+ JavaType elementType = PredefinedType.objectType;
+ JavaField collectionJavaField = getCollectionField(collection);
+ if (collectionJavaField != null) {
+ elementType = TypeSupport.getElementType(collectionJavaField);
+ }
+ JavaType argumentType = firstArg.getTypeInfo();
+ JavaType testElementType = elementType.isPrimitive() ?
+ ((PrimitiveType)elementType).getWrapperClassType() : elementType;
+ JavaType testArgumentType = argumentType.isPrimitive() ?
+ ((PrimitiveType)argumentType).getWrapperClassType() : argumentType;
+ // elementType compatible with argumentType => OK
+ // argumentType compatible with elementType => OK
+ // otherwise error
+ if (!testElementType.isCompatibleWith(testArgumentType) &&
+ !testArgumentType.isCompatibleWith(testElementType)) {
+ errorMsg.error(collection.getLine(), collection.getColumn(),
+ msg.msg("EXC_CollectionElementTypeMismatch", //NOI18N
+ elementType.getName(), argumentType.getName()));
+ }
+ }
+ }
+
+ /**
+ *
+ */
+ protected JavaField getCollectionField(JDOQLAST expr)
+ {
+ JDOQLAST child = (JDOQLAST)expr.getFirstChild();
+ switch (expr.getType()) {
+ case FIELD_ACCESS:
+ case NAVIGATION:
+ if (child != null) {
+ JavaType classType = child.getTypeInfo();
+ String fieldName = null;
+ if (child.getNextSibling() != null) {
+ fieldName = child.getNextSibling().getText();
+ }
+ else {
+ fieldName = ((FieldAccessExpr)expr).getName();
+ }
+ return classType.getJavaField(fieldName);
+ }
+ errorMsg.fatal(msg.msg("ERR_MissingChildren", expr)); //NOI18N
+ break;
+ case CAST:
+ if ((child != null) && (child.getNextSibling() != null)) {
+ return getCollectionField((JDOQLAST)child.getNextSibling());
+ }
+ errorMsg.fatal(msg.msg("ERR_MissingChildren", expr)); //NOI18N
+ break;
+ }
+ return null;
+ }
+
+ /**
+ * Check the arguments of a isEmpty call.
+ */
+ protected void checkIsEmptyArgs(JDOQLAST args)
+ {
+ if (args != null) {
+ // isEmpty does not take parameters
+ errorMsg.error(args.getLine(), args.getColumn(),
+ msg.msg("EXC_WrongNumberOfArgs")); //NOI18N
+ }
+ }
+
+ /**
+ * Analyses a call for an object of type String.
+ * Currently startsWith and endsWith are the only valid String methods
+ * in a query filter.
+ */
+ protected JDOQLAST analyseStringCall(JDOQLAST dot, JDOQLAST string,
+ JDOQLAST method, JDOQLAST args)
+ {
+ String methodName = method.getText();
+ JDOQLAST call = null;
+ JDOQLAST firstArg = (JDOQLAST)args.getFirstChild();
+ if (methodName.equals("startsWith")) { //NOI18N
+ call = new StartsWithCallExpr();
+ call.initialize(STARTS_WITH, methodName, PredefinedType.booleanType);
+ checkStringCallArgs(method, firstArg);
+ call.setFirstChild(string);
+ string.setNextSibling(firstArg);
+ }
+ else if (methodName.equals("endsWith")) { //NOI18N
+ call = new EndsWithCallExpr();
+ call.initialize(ENDS_WITH, methodName, PredefinedType.booleanType);
+ checkStringCallArgs(method, firstArg);
+ call.setFirstChild(string);
+ string.setNextSibling(firstArg);
+ }
+ else
+ {
+ errorMsg.error(dot.getLine(), dot.getColumn(),
+ msg.msg("EXC_InvalidMethodCall")); //NOI18N
+ call = new StartsWithCallExpr();
+ call.initialize(STARTS_WITH, methodName, ErrorType.errorType);
+ }
+ return call;
+ }
+
+ /**
+ * Check the arguments of a startWith or endWith call.
+ */
+ protected void checkStringCallArgs(JDOQLAST method, JDOQLAST args)
+ {
+ JDOQLAST firstArg = args;
+ if (firstArg == null) {
+ errorMsg.error(method.getLine(), method.getColumn(),
+ msg.msg("EXC_WrongNumberOfArgs")); //NOI18N
+ }
+ else if (firstArg.getNextSibling() != null) {
+ JDOQLAST nextArg = (JDOQLAST)firstArg.getNextSibling();
+ errorMsg.error(nextArg.getLine(), nextArg.getColumn(),
+ msg.msg("EXC_WrongNumberOfArgs")); //NOI18N
+ }
+ else {
+ JavaType argType = firstArg.getTypeInfo();
+ if (!argType.equals(PredefinedType.stringType)) {
+ errorMsg.error(firstArg.getLine(), firstArg.getColumn(),
+ msg.msg("EXC_ArgumentTypeMismatch", //NOI18N
+ argType.getName(), PredefinedType.stringType.getName()));
+ }
+ }
+ }
+
+ /**
+ * Analyses a bitwise/logical operation (&, |, ^)
+ * @param op the bitwise/logical operator
+ * @param leftAST left operand
+ * @param rightAST right operand
+ * @return the type info of the operator
+ */
+ protected JavaType analyseBitwiseExpr(JDOQLAST op, JDOQLAST leftAST,
+ JDOQLAST rightAST)
+ {
+ JavaType left = leftAST.getTypeInfo();
+ JavaType right = rightAST.getTypeInfo();
+
+ // handle error type
+ if (left.equals(ErrorType.errorType) ||
+ right.equals(ErrorType.errorType))
+ return ErrorType.errorType;
+
+ switch(op.getType()) {
+ case BAND:
+ case BOR:
+ if (TypeSupport.isBooleanType(left) &&
+ TypeSupport.isBooleanType(right)) {
+ JavaType common = PredefinedType.booleanType;
+ ((BinaryExpr)op).setCommonOperandType(
+ TypeSupport.getJavaClass(common));
+ return common;
+ }
+ break;
+ }
+
+ // if this code is reached a bitwise operator was used with
+ // invalid arguments
+ errorMsg.error(op.getLine(), op.getColumn(),
+ msg.msg("EXC_InvalidArguments", op.getText())); //NOI18N
+ return ErrorType.errorType;
+ }
+
+ /**
+ * Analyses a boolean conditional operation (&&, ||)
+ * @param op the conditional operator
+ * @param leftAST left operand
+ * @param rightAST right operand
+ * @return the type info of the operator
+ */
+ protected JavaType analyseConditionalExpr(JDOQLAST op, JDOQLAST leftAST,
+ JDOQLAST rightAST)
+ {
+ JavaType left = leftAST.getTypeInfo();
+ JavaType right = rightAST.getTypeInfo();
+
+ // handle error type
+ if (left.equals(ErrorType.errorType) ||
+ right.equals(ErrorType.errorType))
+ return ErrorType.errorType;
+
+ switch(op.getType()) {
+ case AND:
+ case OR:
+ if (TypeSupport.isBooleanType(left) &&
+ TypeSupport.isBooleanType(right)) {
+ JavaType common = PredefinedType.booleanType;
+ ((BinaryExpr)op).setCommonOperandType(
+ TypeSupport.getJavaClass(common));
+ return common;
+ }
+ break;
+ }
+
+ // if this code is reached a conditional operator was used
+ // with invalid arguments
+ errorMsg.error(op.getLine(), op.getColumn(),
+ msg.msg("EXC_InvalidArguments", op.getText())); //NOI18N
+ return ErrorType.errorType;
+ }
+
+ /**
+ * Analyses a relational operation.
+ * A relational operation contains one of <, <=, >, >=, ==, or !=.
+ * @param op the relational operator
+ * @param leftAST left operand
+ * @param rightAST right operand
+ * @return the node representing the relational expr
+ */
+ protected JDOQLAST analyseRelationalExpr(JDOQLAST op, JDOQLAST leftAST,
+ JDOQLAST rightAST)
+ {
+ JavaType left = leftAST.getTypeInfo();
+ JavaType right = rightAST.getTypeInfo();
+
+ // handle error type
+ if (left.equals(ErrorType.errorType) ||
+ right.equals(ErrorType.errorType)) {
+ op.setTypeInfo(ErrorType.errorType);
+ return op;
+ }
+
+ // special check for <, <=, >, >=
+ // left and right hand types must be orderable
+ switch(op.getType()) {
+ case LT:
+ case LE:
+ case GT:
+ case GE:
+ if (!left.isOrderable()) {
+ errorMsg.error(op.getLine(), op.getColumn(),
+ msg.msg("EXC_NotSortableType", //NOI18N
+ left.getName(), op.getText()));
+ op.setTypeInfo(ErrorType.errorType);
+ return op;
+ }
+ if (!right.isOrderable()) {
+ errorMsg.error(op.getLine(), op.getColumn(),
+ msg.msg("EXC_NotSortableType", //NOI18N
+ right.getName(), op.getText()));
+ op.setTypeInfo(ErrorType.errorType);
+ return op;
+ }
+ break;
+ case EQUAL:
+ if (left.isPersistenceCapable() ||
+ right.isPersistenceCapable()) {
+ op.setType(OBJECT_EQUAL);
+ }
+ else if (left.isJDOSupportedCollection() ||
+ right.isJDOSupportedCollection()) {
+ op.setType(COLLECTION_EQUAL);
+ }
+ break;
+ case NOT_EQUAL:
+ if (left.isPersistenceCapable() ||
+ right.isPersistenceCapable()) {
+ op.setType(OBJECT_NOT_EQUAL);
+ }
+ else if (left.isJDOSupportedCollection() ||
+ right.isJDOSupportedCollection()) {
+ op.setType(COLLECTION_NOT_EQUAL);
+ }
+ break;
+ }
+
+ JavaType common = getCommonOperandType(left, right);
+ if (common != ErrorType.errorType) {
+ ((BinaryExpr)op).setCommonOperandType(
+ TypeSupport.getJavaClass(common));
+ op.setTypeInfo(PredefinedType.booleanType);
+ // check for operands of type char or Character;
+ // they need to be explictly cast to the common operand type.
+ leftAST = addCharacterCast(leftAST, common);
+ rightAST = addCharacterCast(rightAST, common);
+ op.setFirstChild(leftAST);
+ leftAST.setNextSibling(rightAST);
+ return op;
+ }
+
+ // if this code is reached a conditional operator was used with
+ // invalid arguments
+ errorMsg.error(op.getLine(), op.getColumn(),
+ msg.msg("EXC_InvalidArguments", op.getText())); //NOI18N
+ op.setTypeInfo(ErrorType.errorType);
+ return op;
+ }
+
+ /**
+ * Analyses a binary arithmetic expression +, -, *, /.
+ * @param op the operator
+ * @param leftAST left operand
+ * @param rightAST right operand
+ * @return the node representing the binary arithmetic op
+ */
+ protected JDOQLAST analyseBinaryArithmeticExpr(JDOQLAST op, JDOQLAST leftAST,
+ JDOQLAST rightAST)
+ {
+ JavaType left = leftAST.getTypeInfo();
+ JavaType right = rightAST.getTypeInfo();
+
+ // handle error type
+ if (left.equals(ErrorType.errorType) ||
+ right.equals(ErrorType.errorType)) {
+ op.setTypeInfo(ErrorType.errorType);
+ return op;
+ }
+
+ if (TypeSupport.isNumberType(left) && TypeSupport.isNumberType(right)) {
+ JavaType common = getCommonOperandType(left, right);
+ if (common != ErrorType.errorType) {
+ ((BinaryExpr)op).setCommonOperandType(
+ TypeSupport.getJavaClass(common));
+ op.setTypeInfo(common);
+ // Check for operands of type char or Character;
+ // they need to be explictly cast to the common operand type.
+ leftAST = addCharacterCast(leftAST, common);
+ rightAST = addCharacterCast(rightAST, common);
+ op.setFirstChild(leftAST);
+ leftAST.setNextSibling(rightAST);
+ return op;
+ }
+ }
+ else if (op.getType() == PLUS) {
+ // handle + for strings
+ if (left.equals(PredefinedType.stringType) &&
+ right.equals(PredefinedType.stringType)) {
+ JavaType common = PredefinedType.stringType;
+ ((BinaryExpr)op).setCommonOperandType(
+ TypeSupport.getJavaClass(common));
+ op.setTypeInfo(common);
+ // change the token type to CONCAT
+ op.setType(CONCAT);
+ return op;
+ }
+ }
+
+ // if this code is reached a conditional operator was used
+ // with invalid arguments
+ errorMsg.error(op.getLine(), op.getColumn(),
+ msg.msg("EXC_InvalidArguments", op.getText())); //NOI18N
+ op.setTypeInfo(ErrorType.errorType);
+ return op;
+ }
+
+ /**
+ * The query runtime has a problem with binary or relational expressions
+ * having an operand of type char or Character. The query runtime treats the
+ * value of the expression to be a value of the promoted type (see binary
+ * numeric promotion). The value will be of type Character which is not
+ * compatible to java.lang.Number.
+ * In the current query runtime only the CastExpression includes the
+ * conversion code from the Character to Number. This keeps the code for
+ * binary and relational expressions free from any special treatement of
+ * char or Character values.
+ * As a consequence the semantic analsis needs to insert a cast node
+ * whenever binary numeric promotion converts a char or Character into a
+ * numeric or Number type.
+ * Method addCharacterCast checks whether the specified ast is of type
+ * char or Character. If so it wraps it into a cast expression ast using
+ * the specified type. If not the ast is reured as it is.
+ * @param ast the ast to be checked
+ * @param common the type to be used inside the cast
+ * @return a cast node that wraps the specified ast node, if the ast is
+ * of type char or Character; the ast itself otherwise.
+ */
+ protected JDOQLAST addCharacterCast(JDOQLAST ast, JavaType common)
+ {
+ JDOQLAST node = ast;
+ if (TypeSupport.isCharType(ast.getTypeInfo())) {
+ CastExpr cast = new CastExpr();
+ cast.initialize(CAST, "CAST", common); //NOI18N
+ cast.setLine(ast.getLine());
+ cast.setColumn(ast.getColumn());
+ TypeImpl typeNode = new TypeImpl();
+ typeNode.initialize(TYPE, common.getName(), common);
+ cast.setFirstChild(typeNode);
+ typeNode.setNextSibling(ast);
+ node = cast;
+ }
+ return node;
+ }
+
+ /**
+ * Returns the common type info for the specified operand types.
+ * This includes binary numeric promotion as specified in Java.
+ * @param left type info of left operand
+ * @param right type info of right operand
+ * @return the common type info
+ */
+ protected JavaType getCommonOperandType(JavaType left, JavaType right)
+ {
+ if (TypeSupport.isNumberType(left) && TypeSupport.isNumberType(right)) {
+ // handle java.math.BigDecimal
+ if (left.isCompatibleWith(PredefinedType.bigDecimalType))
+ return left;
+ if (right.isCompatibleWith(PredefinedType.bigDecimalType))
+ return right;
+
+ // handle java.math.BigInteger
+ if (left.isCompatibleWith(PredefinedType.bigIntegerType)) {
+ // if right is floating point return BigDecimal,
+ // otherwise return BigInteger
+ if (right.isWrapperClass())
+ right = ((WrapperClassType)right).getWrappedPrimitiveType();
+ return right.isFloatingPoint() ?
+ PredefinedType.bigDecimalType : left;
+ }
+ if (right.isCompatibleWith(PredefinedType.bigIntegerType)) {
+ // if left is floating point return BigDecimal,
+ // otherwise return BigInteger
+ if (left.isWrapperClass())
+ left = ((WrapperClassType)left).getWrappedPrimitiveType();
+ return left.isFloatingPoint() ?
+ PredefinedType.bigDecimalType : right;
+ }
+
+ boolean wrapper = false;
+ if (left.isWrapperClass()) {
+ left = ((WrapperClassType)left).getWrappedPrimitiveType();
+ wrapper = true;
+ }
+ if (right.isWrapperClass()) {
+ right = ((WrapperClassType)right).getWrappedPrimitiveType();
+ wrapper = true;
+ }
+
+ // handle numeric types with arbitrary arithmetic operator
+ if (TypeSupport.isNumericType(left) &&
+ TypeSupport.isNumericType(right)) {
+ JavaType promotedType =
+ TypeSupport.binaryNumericPromotion(left, right);
+ if (wrapper && TypeSupport.isNumericType(promotedType)) {
+ promotedType =
+ ((PrimitiveType)promotedType).getWrapperClassType();
+ }
+ return promotedType;
+ }
+ }
+ else if (TypeSupport.isBooleanType(left) &&
+ TypeSupport.isBooleanType(right)) {
+ // check for boolean wrapper class: if one of the operands has the
+ // type Boolean return Boolean, otherwise return boolean.
+ if (left instanceof WrapperClassType)
+ return left;
+ else if (right instanceof WrapperClassType)
+ return right;
+ else
+ return PredefinedType.booleanType;
+ }
+ else if (left.isCompatibleWith(right)) {
+ return right;
+ }
+ else if (right.isCompatibleWith(left)) {
+ return left;
+ }
+
+ // not compatible types => return errorType
+ return ErrorType.errorType;
+ }
+
+ /**
+ * Analyses a unary expression + and -
+ * @param op the operator
+ * @param argAST the opreand
+ * @return the node representing the unary expression
+ */
+ protected JDOQLAST analyseUnaryArithmeticExpr(JDOQLAST op, JDOQLAST argAST)
+ {
+ JDOQLAST expr = null;
+ JavaType type = analyseUnaryArithmeticExprType(op, argAST);
+
+ switch(op.getType()) {
+ case UNARY_PLUS:
+ // create the correct node type here,
+ // the lexer create Binary plus
+ expr = new UnaryPlusExpr();
+ expr.initialize(UNARY_PLUS, "+", type); //NOI18N
+ break;
+ case UNARY_MINUS:
+ // create the correct node type here,
+ // the lexer create Binary plus
+ expr = new UnaryMinusExpr();
+ expr.initialize(UNARY_MINUS, "-", type); //NOI18N
+ break;
+ }
+ expr.setFirstChild(addCharacterCast(argAST, type));
+ expr.setLine(op.getLine());
+ expr.setColumn(op.getColumn());
+
+ return expr;
+ }
+
+ /**
+ * Analyses a unary expression + and -
+ * @param op the operator
+ * @param argAST the operand
+ * @return the type info of the operator
+ */
+ protected JavaType analyseUnaryArithmeticExprType(JDOQLAST op,
+ JDOQLAST argAST)
+ {
+ JavaType arg = argAST.getTypeInfo();
+
+ // handle error type
+ if (arg.equals(ErrorType.errorType))
+ return ErrorType.errorType;
+
+ // handle java.math.BigDecimal and java.math.BigInteger
+ if (arg.isCompatibleWith(PredefinedType.bigDecimalType))
+ return arg;
+
+ // handle java.math.BigInteger
+ if (arg.isCompatibleWith(PredefinedType.bigIntegerType))
+ return arg;
+
+ boolean wrapper = false;
+ if (arg.isWrapperClass()) {
+ arg = ((WrapperClassType)arg).getWrappedPrimitiveType();
+ wrapper = true;
+ }
+
+ if (TypeSupport.isNumericType(arg)) {
+ JavaType promotedType = TypeSupport.unaryNumericPromotion(arg);
+ if (wrapper && TypeSupport.isNumericType(promotedType)) {
+ promotedType =
+ ((PrimitiveType)promotedType).getWrapperClassType();
+ }
+ return promotedType;
+ }
+
+ // if this code is reached a conditional operator was used
+ // with invalid arguments
+ errorMsg.error(op.getLine(), op.getColumn(),
+ msg.msg("EXC_InvalidArguments", op.getText())); //NOI18N
+ return ErrorType.errorType;
+ }
+
+ /**
+ * Analyses a complement expression.
+ * A complement expression contains one of ! and ~
+ * @param op the operator
+ * @param argAST the operand
+ * @return the node representing the complement expression
+ */
+ protected JDOQLAST analyseComplementExpr(JDOQLAST op, JDOQLAST argAST)
+ {
+ JDOQLAST expr = null;
+ JavaType type = analyseComplementExprType(op, argAST);
+
+ switch(op.getType()) {
+ case BNOT:
+ // create the correct node type here,
+ // the lexer create Binary plus
+ expr = new ComplementExpr();
+ expr.initialize(BNOT, "~", type); //NOI18N
+ break;
+ case LNOT:
+ // create the correct node type here,
+ // the lexer create Binary plus
+ expr = new NotExpr();
+ expr.initialize(LNOT, "!", type); //NOI18N
+ break;
+ }
+ expr.setFirstChild(addCharacterCast(argAST, type));
+ expr.setLine(op.getLine());
+ expr.setColumn(op.getColumn());
+
+ return expr;
+ }
+
+ /**
+ * Analyses a complement expression.
+ * A complement expression contains one of ! and ~
+ * @param op the operator
+ * @param argAST the operand
+ * @return the type info of the operator
+ */
+ protected JavaType analyseComplementExprType(JDOQLAST op, JDOQLAST argAST)
+ {
+ JavaType arg = argAST.getTypeInfo();
+
+ // handle error type
+ if (arg.equals(ErrorType.errorType))
+ return ErrorType.errorType;
+
+ switch(op.getType()) {
+ case BNOT:
+ if (TypeSupport.isIntegralType(arg)) {
+ boolean wrapper = false;
+ if (arg.isWrapperClass()) {
+ arg = ((WrapperClassType)arg).getWrappedPrimitiveType();
+ wrapper = true;
+ }
+
+ JavaType promotedType = TypeSupport.unaryNumericPromotion(arg);
+ if (wrapper) {
+ promotedType =
+ ((PrimitiveType)promotedType).getWrapperClassType();
+ }
+ return promotedType;
+ }
+ break;
+ case LNOT:
+ if (TypeSupport.isBooleanType(arg)) {
+ return arg;
+ }
+ break;
+ }
+
+ // if this code is reached a conditional operator was used with
+ // invalid arguments
+ errorMsg.error(op.getLine(), op.getColumn(),
+ msg.msg("EXC_InvalidArguments", op.getText())); //NOI18N
+ return ErrorType.errorType;
+ }
+
+ /**
+ *
+ */
+ protected void checkConstraints(JDOQLAST ast, VariableChecker tab)
+ {
+ checkConstraints(ast, null, tab);
+ }
+
+ /**
+ *
+ */
+ protected void checkConstraints(JDOQLAST ast, String dependentVariable,
+ VariableChecker tab)
+ {
+ if (ast == null) return;
+ switch (ast.getType()) {
+ case VARIABLE_ACCESS:
+ tab.markUsed(ast, dependentVariable);
+ break;
+ case CONTAINS:
+ JDOQLAST expr = (JDOQLAST)ast.getFirstChild();
+ JDOQLAST var = (JDOQLAST)expr.getNextSibling();
+ if (var.getType() == VARIABLE_ACCESS) {
+ checkConstraints(expr, var.getText(), tab);
+ tab.markConstraint(var, expr);
+ }
+ else {
+ checkConstraints(expr, dependentVariable, tab);
+ }
+ break;
+ case BOR:
+ case OR:
+ JDOQLAST left = (JDOQLAST)ast.getFirstChild();
+ JDOQLAST right = (JDOQLAST)left.getNextSibling();
+ // prepare tab copy for right hand side and merge
+ // the right hand side copy into vartab
+ VariableChecker copy = new VariableChecker(tab);
+ checkConstraints(left, dependentVariable, tab);
+ checkConstraints(right, dependentVariable, copy);
+ tab.merge(copy);
+ break;
+ default:
+ for (JDOQLAST node = (JDOQLAST)ast.getFirstChild();
+ node != null; node = (JDOQLAST)node.getNextSibling()) {
+ checkConstraints(node, dependentVariable, tab);
+ }
+ break;
+ }
+ }
+
+}
+
+// rules
+
+query
+ : #( QUERY_TREE
+ candidateClass
+ {
+ typeNames.init(candidateClass.getName());
+ }
+ imports
+ parameters
+ variables
+ ordering
+ filter
+ )
+ ;
+
+// ----------------------------------
+// rules: candidate class
+// ----------------------------------
+
+candidateClass
+{
+ errorMsg.setContext("setClass"); //NOI18N
+}
+ : #( c:CANDIDATE_CLASS t:type )
+ {
+ candidateClass = #t.getTypeInfo();
+ if (candidateClass == null) {
+ errorMsg.fatal(msg.msg("EXC_UnknownCandidateClass", //NOI18N
+ #t.getText()));
+ }
+ #c.setTypeInfo(candidateClass);
+ }
+ ;
+
+// ----------------------------------
+// rules: import declaration
+// ----------------------------------
+
+imports!
+{
+ errorMsg.setContext("declareImports"); //NOI18N
+}
+ : ( declareImport )*
+ ;
+
+declareImport
+ { String name = null; }
+ : #( i1:IMPORT name = qualifiedName )
+ {
+ JavaType type = typeSupport.checkType(name);
+ if (type == null) {
+ errorMsg.error(#i1.getLine(), #i1.getColumn(),
+ msg.msg("EXC_UnknownType", name)); //NOI18N
+ }
+
+ String old = typeNames.declareImport(name);
+ if (old != null) {
+ errorMsg.error(#i1.getLine(), #i1.getColumn(),
+ msg.msg("EXC_MultipleImport", name)); //NOI18N
+ }
+ }
+ | #( i2:IMPORT_ON_DEMAND name = qualifiedName )
+ {
+ typeNames.declareImportOnDemand(name);
+ }
+ ;
+
+// ----------------------------------
+// rules: parameter declaration
+// ----------------------------------
+
+parameters
+{
+ errorMsg.setContext("declareParameters"); //NOI18N
+}
+ : ( declareParameter )*
+ ;
+
+declareParameter
+ : #( p:PARAMETER_DECL t:type ( i:IDENT )? )
+ {
+ ParameterDecl paramDecl = (ParameterDecl)#p;
+ String name = (#i != null) ? #i.getText() : paramDecl.getName();
+ JavaType type = #t.getTypeInfo();
+ if (symtab.declare(name, paramDecl) != null) {
+ errorMsg.error(#i.getLine(), #i.getColumn(),
+ msg.msg("EXC_MultipleDeclaration", name)); //NOI18N
+ }
+ paramDecl.setTypeInfo(type);
+ paramDecl.setName(name);
+ paramDecl.setFirstChild(#t);
+ paramtab.declare(paramDecl);
+ #t.setNextSibling(null);
+ }
+ ;
+
+// ----------------------------------
+// rules: variable declaration
+// ----------------------------------
+
+variables
+{
+ errorMsg.setContext("declareVariables"); //NOI18N
+}
+ : ( declareVariable )*
+ ;
+
+declareVariable
+ : #( v:VARIABLE_DECL t:type ( i:IDENT )? )
+ {
+ VariableDecl varDecl = (VariableDecl)#v;
+ String name = (#i != null) ? #i.getText() : varDecl.getName();
+ JavaType type = #t.getTypeInfo();
+ if (symtab.declare(name, varDecl) != null) {
+ errorMsg.error(#i.getLine(), #i.getColumn(),
+ msg.msg("EXC_MultipleDeclaration", name)); //NOI18N
+ }
+ varDecl.setTypeInfo(type);
+ varDecl.setName(name);
+ varDecl.setFirstChild(#t);
+ #t.setNextSibling(null);
+ vartab.declare(varDecl);
+ varChecker.add(name);
+ }
+ ;
+
+// ----------------------------------
+// rules: ordering specification
+// ----------------------------------
+
+ordering
+{
+ errorMsg.setContext("setOrdering"); //NOI18N
+}
+ : ( orderSpec )*
+ ;
+
+orderSpec
+ : #( ASCENDING e1:expression )
+ { analyseOrderingExpression(#e1); }
+ | #( DESCENDING e2:expression )
+ { analyseOrderingExpression(#e2); }
+ ;
+
+// ----------------------------------
+// rules: filer expression
+// ----------------------------------
+
+filter
+{
+ errorMsg.setContext("setFilter"); //NOI18N
+}
+ // There is always a filter defined and it is the last node of the query
+ // tree. Otherwise all the remaining subtrees after the CANDIDATE_CLASS
+ // subtree are empty which results in a ClassCastException
+ // antlr.ASTNullType during analysis of the (non existsent) subtrees
+ : e:expression
+ {
+ JavaType exprType = #e.getTypeInfo();
+ if (!(TypeSupport.isBooleanType(exprType) ||
+ exprType.equals(ErrorType.errorType))) {
+ // filter expression must have the type boolean or Boolean
+ errorMsg.error(#e.getLine(), #e.getColumn(),
+ msg.msg("EXC_BooleanFilterExpected", exprType)); //NOI18N
+ }
+ checkConstraints(#e, varChecker);
+ varChecker.checkConstraints();
+ }
+ ;
+
+expression
+ { String repr; }
+ : repr = e:exprNoCheck[false]
+ {
+ if (repr != null) {
+ #e.setTypeInfo(ErrorType.errorType);
+ errorMsg.error(#e.getLine(), #e.getColumn(),
+ msg.msg("EXC_UndefinedExpression", repr)); //NOI18N
+ }
+ }
+ ;
+
+exprNoCheck [boolean insideDotExpr] returns [String repr]
+ { repr = null; } // repr is used to get the text of identifier
+ // inside a dot expression
+ : bitwiseExpr
+ | conditionalExpr
+ | relationalExpr
+ | binaryArithmeticExpr
+ | unaryArithmeticExpr
+ | complementExpr
+ | repr = primary[insideDotExpr]
+ ;
+
+bitwiseExpr
+ : #( op1:BAND left1:expression right1:expression )
+ {
+ #op1.setTypeInfo(analyseBitwiseExpr(#op1, #left1, #right1));
+ }
+ | #( op2:BOR left2:expression right2:expression )
+ {
+ #op2.setTypeInfo(analyseBitwiseExpr(#op2, #left2, #right2));
+ }
+ ;
+
+conditionalExpr
+ : #( op1:AND left1:expression right1:expression )
+ {
+ #op1.setTypeInfo(analyseConditionalExpr(#op1, #left1, #right1));
+ }
+ | #( op2:OR left2:expression right2:expression )
+ {
+ #op2.setTypeInfo(analyseConditionalExpr(#op2, #left2, #right2));
+ }
+ ;
+
+relationalExpr
+{
+ JavaType left = null;
+ JavaType right = null;
+}
+ : #( op1:EQUAL left1:expression right1:expression )
+ {
+ #relationalExpr = analyseRelationalExpr(#op1, #left1, #right1);
+ }
+ | #( op2:NOT_EQUAL left2:expression right2:expression )
+ {
+ #relationalExpr = analyseRelationalExpr(#op2, #left2, #right2);
+ }
+ | #( op3:LT left3:expression right3:expression )
+ {
+ #relationalExpr = analyseRelationalExpr(#op3, #left3, #right3);
+ }
+ | #( op4:GT left4:expression right4:expression )
+ {
+ #relationalExpr = analyseRelationalExpr(#op4, #left4, #right4);
+ }
+ | #( op5:LE left5:expression right5:expression )
+ {
+ #relationalExpr = analyseRelationalExpr(#op5, #left5, #right5);
+ }
+ | #( op6:GE left6:expression right6:expression )
+ {
+ #relationalExpr = analyseRelationalExpr(#op6, #left6, #right6);
+ }
+ ;
+
+binaryArithmeticExpr
+ : #( op1:PLUS left1:expression right1:expression )
+ {
+ #binaryArithmeticExpr = analyseBinaryArithmeticExpr(#op1, #left1,
+ #right1);
+ }
+ | #( op2:MINUS left2:expression right2:expression )
+ {
+ #binaryArithmeticExpr = analyseBinaryArithmeticExpr(#op2, #left2,
+ #right2);
+ }
+ | #( op3:STAR left3:expression right3:expression )
+ {
+ #binaryArithmeticExpr = analyseBinaryArithmeticExpr(#op3, #left3,
+ #right3);
+ }
+ | #( op4:DIV left4:expression right4:expression )
+ {
+ #binaryArithmeticExpr = analyseBinaryArithmeticExpr(#op4, #left4,
+ #right4);
+ }
+ ;
+
+unaryArithmeticExpr
+ : #( op1:UNARY_PLUS arg1:expression )
+ {
+ #unaryArithmeticExpr = analyseUnaryArithmeticExpr(#op1, #arg1);
+ }
+ | #( op2:UNARY_MINUS arg2:expression )
+ {
+ #unaryArithmeticExpr = analyseUnaryArithmeticExpr(#op2, #arg2);
+ }
+ ;
+
+complementExpr
+ : #( op1:BNOT arg1:expression )
+ {
+ #complementExpr = analyseComplementExpr(#op1, #arg1);
+ }
+ | #( op2:LNOT arg2:expression )
+ {
+ #complementExpr = analyseComplementExpr(#op2, #arg2);
+ }
+ ;
+
+primary [boolean insideDotExpr] returns [String repr]
+{ repr = null; }
+ : #( c:CAST t:type e:expression )
+ {
+ JavaType type = #t.getTypeInfo();
+ JavaType exprType = #e.getTypeInfo();
+ if (! ((TypeSupport.isNumberType(type) &&
+ TypeSupport.isNumberType(exprType)) ||
+ type.isCompatibleWith(exprType) ||
+ exprType.isCompatibleWith(type))) {
+ errorMsg.error(#c.getLine(), #c.getColumn(),
+ msg.msg("EXC_InvalidCast", //NOI18N
+ exprType.getName(), type.getName()));
+ type = ErrorType.errorType;
+ }
+ CastExpr cast = new CastExpr();
+ cast.initialize(#c.getType(), "CAST", type); //NOI18N
+ cast.setLine(#c.getLine());
+ cast.setColumn(#c.getColumn());
+ cast.setFirstChild(#t);
+ #t.setNextSibling(#e);
+ #primary = cast;
+ }
+ | literal
+ | i:THIS
+ { #i.setTypeInfo(candidateClass); }
+ | repr = dotExpr
+ | repr = identifier [insideDotExpr]
+ | parameterAccess
+ | variableAccess
+ | fieldAccess
+ | navigation
+ | contains
+ | isEmpty
+ | startsWith
+ | endsWith
+ ;
+
+literal
+ : b1:TRUE { #b1.setTypeInfo(PredefinedType.booleanType); }
+ | b2:FALSE { #b2.setTypeInfo(PredefinedType.booleanType); }
+ | b3:BOOLEAN_LITERAL { #b3.setTypeInfo(PredefinedType.booleanType); }
+ | i:INT_LITERAL { #i.setTypeInfo(PredefinedType.intType); }
+ | l:LONG_LITERAL { #l.setTypeInfo(PredefinedType.longType); }
+ | f:FLOAT_LITERAL { #f.setTypeInfo(PredefinedType.floatType); }
+ | d:DOUBLE_LITERAL { #d.setTypeInfo(PredefinedType.doubleType); }
+ | c:CHAR_LITERAL { #c.setTypeInfo(PredefinedType.charType); }
+ | s:STRING_LITERAL { #s.setTypeInfo(PredefinedType.stringType); }
+ | n:NULL { #n.setTypeInfo(NullType.nullType); }
+ | by:BYTE_LITERAL { #by.setTypeInfo(PredefinedType.byteType); }
+ | sh:SHORT_LITERAL { #sh.setTypeInfo(PredefinedType.shortType); }
+ | con:CONSTANT
+ {
+ Object value = ((ConstantExpr)#con).getValue();
+ #con.setTypeInfo((value == null) ? NullType.nullType :
+ typeSupport.checkType(value.getClass()));
+ }
+ ;
+
+dotExpr returns [String repr]
+ {
+ repr = null;
+ }
+ : #( dot:DOT
+ repr = expr:exprNoCheck[true] ident:IDENT ( args:argList )?
+ )
+ {
+ JavaType type = null;
+ if (repr != null) { // possible package name
+ String qualifiedName = repr + '.' + #ident.getText();
+ type = typeSupport.checkType(qualifiedName);
+ if (type == null) {
+ // name does not define a valid class => return qualifiedName
+ repr = qualifiedName;
+ }
+ else if (#args == null) {
+ // found valid class name and NO arguments specified
+ // => use of the class name
+ repr = null;
+ TypeImpl typeNode = new TypeImpl();
+ typeNode.initialize(TYPE, qualifiedName, type);
+ #dotExpr = typeNode;
+ }
+ else {
+ // found valid class name and arguments specified =>
+ // looks like constructor call
+ repr = null;
+ errorMsg.error(dot.getLine(), dot.getColumn(),
+ msg.msg("EXC_InvalidMethodCall")); //NOI18N
+ }
+ #dot.setTypeInfo(type);
+ #dot.setText(#expr.getText() + '.' + #ident.getText());
+ }
+ else { // no string repr of left hand side => expression is defined
+ #dotExpr = analyseDotExpr(#dot, #expr, #ident, #args);
+ }
+ }
+ ;
+
+argList
+ : #(ARG_LIST args)
+ ;
+
+identifier [boolean insideDotExpr] returns [String repr]
+ {
+ repr = null; // repr is set when ident is part of a package name spec
+ }
+ : ident:IDENT ( args:argList )?
+ {
+ String name = #ident.getText();
+
+ // check args, if defined => invalid method call
+ if (#args != null) {
+ #ident.setTypeInfo(ErrorType.errorType);
+ errorMsg.error(#ident.getLine(), #ident.getColumn(),
+ msg.msg("EXC_InvalidMethodCall")); //NOI18N
+ }
+ JDOQLAST ast = analyseDefinedIdentifier(#ident);
+ if (ast != null) {
+ #identifier = ast;
+ }
+ else if (insideDotExpr) {
+ JavaType resolved = typeNames.resolve(name);
+ if (resolved != null) {
+ // type name
+ TypeImpl typeNode = new TypeImpl();
+ typeNode.initialize(TYPE, resolved.getName(), resolved);
+ #identifier = typeNode;
+ }
+ else {
+ repr = #ident.getText();
+ }
+ }
+ else {
+ #ident.setTypeInfo(ErrorType.errorType);
+ errorMsg.error(ident.getLine(), ident.getColumn(),
+ msg.msg("EXC_UndefinedIdentifier", //NOI18N
+ ident.getText()));
+ }
+ }
+ ;
+
+parameterAccess
+ : p:PARAMETER_ACCESS
+ {
+ String name = #p.getText();
+ Decl decl = (Decl)symtab.getDeclaration(name);
+ if (decl instanceof ParameterDecl) {
+ #p.setTypeInfo(decl.getTypeInfo());
+ }
+ else {
+ errorMsg.error(#p.getLine(), #p.getColumn(),
+ msg.msg("EXC_InvalidParameterAccess", name)); //NOI18N
+ }
+ }
+ ;
+
+variableAccess
+ : #( v:VARIABLE_ACCESS ( expression )? )
+ {
+ String name = #v.getText();
+ Decl decl = (Decl)symtab.getDeclaration(name);
+ if (decl instanceof VariableDecl) {
+ #v.setTypeInfo(decl.getTypeInfo());
+ }
+ else {
+ errorMsg.error(#v.getLine(), #v.getColumn(),
+ msg.msg("EXC_InvalidVariableAccess", name)); //NOI18N
+ }
+ }
+ ;
+
+staticFieldAccess
+ : #( s:STATIC_FIELD_ACCESS t:type ( i:IDENT )? )
+ {
+ String fieldName = ((StaticFieldAccessExpr)#s).getName();
+ JavaType type = #t.getTypeInfo();
+ JavaType fieldType = ErrorType.errorType;
+ if (!type.isPrimitive()) {
+ // left expression is of a class type
+ JavaField javaField = type.getJavaField(fieldName);
+ if (javaField == null) {
+ errorMsg.error(#s.getLine(), #s.getColumn(),
+ msg.msg("EXC_UnknownField", //NOI18N
+ fieldName, type.getName()));
+ }
+ else {
+ if (typeSupport.isStaticField(javaField)) {
+ errorMsg.error(#s.getLine(), #s.getColumn(),
+ msg.msg("EXC_InvalidStaticReference", //NOI18N
+ fieldName, type.getName()));
+ }
+ }
+ fieldType = javaField.getType();
+ }
+ else {
+ errorMsg.error(#s.getLine(), #s.getColumn(),
+ msg.msg("EXC_ClassTypeExpressionExpected")); //NOI18N
+ }
+ #s.setTypeInfo(fieldType);
+ }
+ ;
+
+fieldAccess
+ : #( f:FIELD_ACCESS o:expression ( i:IDENT )? )
+ {
+ String fieldName = ((FieldAccessExpr)#f).getName();
+ JavaType exprType = #o.getTypeInfo();
+ JavaType fieldType = ErrorType.errorType;
+ if (!exprType.isPrimitive()) {
+ // left expression is of a class type
+ JavaField javaField = exprType.getJavaField(fieldName);
+ if (javaField == null) {
+ errorMsg.error(#f.getLine(), #f.getColumn(),
+ msg.msg("EXC_UnknownField", //NOI18N
+ fieldName, exprType.getName()));
+ }
+ fieldType = javaField.getType();
+ }
+ else {
+ errorMsg.error(#o.getLine(), #o.getColumn(),
+ msg.msg("EXC_ClassTypeExpressionExpected")); //NOI18N
+ }
+ #f.setTypeInfo(fieldType);
+ if (fieldType.isPersistenceCapable()) {
+ #f.setType(NAVIGATION);
+ }
+ }
+ ;
+
+navigation
+ : #( n:NAVIGATION o:expression ( i:IDENT )? )
+ {
+ String fieldName = ((FieldAccessExpr)#n).getName();
+ JavaType exprType = #o.getTypeInfo();
+ JavaType fieldType = ErrorType.errorType;
+ if (!exprType.isPrimitive()) {
+ // left expression is of a class type
+ JavaField javaField = exprType.getJavaField(fieldName);
+ if (javaField == null) {
+ errorMsg.error(#n.getLine(), #n.getColumn(),
+ msg.msg("EXC_UnknownField", //NOI18N
+ fieldName, exprType.getName()));
+ }
+ fieldType = javaField.getType();
+ }
+ else {
+ errorMsg.error(#o.getLine(), #o.getColumn(),
+ msg.msg("EXC_ClassTypeExpressionExpected")); //NOI18N
+ }
+ #n.setTypeInfo(fieldType);
+ }
+ ;
+
+contains
+ : #(c:CONTAINS collection:expression args:args )
+ {
+ // check type of collection expression
+ JavaType type = #collection.getTypeInfo();
+ if (!type.isJDOSupportedCollection()) {
+ errorMsg.error(#collection.getLine(), #collection.getColumn(),
+ msg.msg("EXC_CollectionTypeExpected", type.getName())); //NOI18N
+ }
+ checkContainsArgs(#collection, #c, #args);
+ #c.setTypeInfo(PredefinedType.booleanType);
+ }
+ ;
+
+isEmpty
+ : #(i:IS_EMPTY collection:expression args:args )
+ {
+ // check type of collection expression
+ JavaType type = #collection.getTypeInfo();
+ if (!type.isJDOSupportedCollection()) {
+ errorMsg.error(#collection.getLine(), #collection.getColumn(),
+ msg.msg("EXC_CollectionTypeExpected", type.getName())); //NOI18N
+ }
+ checkIsEmptyArgs(#args);
+ #i.setTypeInfo(PredefinedType.booleanType);
+ }
+ ;
+
+startsWith
+ : #(s:STARTS_WITH string:expression args:args )
+ {
+ // check type of string expression
+ JavaType type = #string.getTypeInfo();
+ if (!PredefinedType.stringType.equals(type)) {
+ errorMsg.error(#string.getLine(), #string.getColumn(),
+ msg.msg("EXC_StringTypeExpected", type.getName())); //NOI18N
+ }
+ checkStringCallArgs(#s, #args);
+ #s.setTypeInfo(PredefinedType.booleanType);
+ }
+ ;
+
+endsWith
+ : #(e:ENDS_WITH string:expression args:args )
+ {
+ // check type of string expression
+ JavaType type = #string.getTypeInfo();
+ if (!PredefinedType.stringType.equals(type)) {
+ errorMsg.error(#string.getLine(), #string.getColumn(),
+ msg.msg("EXC_StringTypeExpected", type.getName())); //NOI18N
+ }
+ checkStringCallArgs(#e, #args);
+ #e.setTypeInfo(PredefinedType.booleanType);
+ }
+ ;
+
+args
+ : (expression)*
+ ;
+
+qualifiedName returns [String name]
+ { name = null; }
+ : id1:IDENT
+ {
+ name = #id1.getText();
+ }
+ | #( d:DOT
+ name = qualifiedName
+ id2:IDENT
+ {
+ name += (#d.getText() + #id2.getText());
+ }
+ )
+ ;
+
+// ----------------------
+// types
+// ----------------------
+
+type
+ { String name = null; }
+ : name = qn:qualifiedName
+ {
+ // First check type name as it is
+ JavaType type = typeSupport.checkType(name);
+ if (type == null)
+ // Check type imports
+ type = typeNames.resolve(name);
+ if (type == null) {
+ // unknown type
+ errorMsg.error(#qn.getLine(), #qn.getColumn(),
+ msg.msg("EXC_UnknownType", name)); //NOI18N
+ }
+ TypeImpl typeNode = new TypeImpl();
+ typeNode.initialize(TYPE, type.getName(), type);
+ #type = typeNode;
+ }
+ | t:TYPE
+ {
+ Class clazz = ((NodeImpl)#t).getJavaClass();
+ if (clazz != null)
+ #t.setTypeInfo(typeSupport.checkType(clazz));
+ else
+ #t.setTypeInfo(typeSupport.checkType(#t.getText()));
+ }
+ | p:primitiveType
+ {
+ name = #p.getText();
+ TypeImpl typeNode = new TypeImpl();
+ typeNode.initialize(TYPE, name, typeSupport.checkType(name));
+ #type = typeNode;
+ }
+ ;
+
+primitiveType
+ : BOOLEAN
+ | BYTE
+ | CHAR
+ | SHORT
+ | INT
+ | FLOAT
+ | LONG
+ | DOUBLE
+ ;
Added: incubator/jdo/trunk/query20/src/java/org/apache/jdo/impl/jdoql/jdoqlc/SimpleFieldManager.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/query20/src/java/org/apache/jdo/impl/jdoql/jdoqlc/SimpleFieldManager.java?rev=171353&view=auto
==============================================================================
--- incubator/jdo/trunk/query20/src/java/org/apache/jdo/impl/jdoql/jdoqlc/SimpleFieldManager.java (added)
+++ incubator/jdo/trunk/query20/src/java/org/apache/jdo/impl/jdoql/jdoqlc/SimpleFieldManager.java Sun May 22 11:08:57 2005
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jdo.impl.jdoql.jdoqlc;
+
+import org.apache.jdo.state.FieldManager;
+
+/**
+ * This is the means by which a StateManager implementation's setXXXField()
+ * method (where XXX is e.g. Int) can give the value back to the object
+ *
+ * @author Michael Bouschen
+ */
+public class SimpleFieldManager implements FieldManager {
+
+ private Object value;
+
+ /**
+ * Provides the means by which the value of a boolean field can be given
+ * by a StateManager to an object that needs the value.
+ * @param fieldNum Field number of the field in the object whose value is
+ * given.
+ * @param value Boolean that is the value of a particular field.
+ */
+ public void storeBooleanField(int fieldNum, boolean value) {
+ this.value = new Boolean(value);
+ }
+
+ public boolean fetchBooleanField(int fieldNum) {
+ return ((Boolean)value).booleanValue();
+ }
+
+ /**
+ * Provides the means by which the value of a char field can be given
+ * by a StateManager to an object that needs the value.
+ * @param fieldNum Field number of the field in the object whose value is
+ * given.
+ * @param value Char that is the value of a particular field.
+ */
+ public void storeCharField(int fieldNum, char value){
+ this.value = new Character(value);
+ }
+
+ public char fetchCharField(int fieldNum) {
+ return ((Character)value).charValue();
+ }
+
+ /**
+ * Provides the means by which the value of a byte field can be given
+ * by a StateManager to an object that needs the value.
+ * @param fieldNum Field number of the field in the object whose value is
+ * given.
+ * @param value Byte that is the value of a particular field.
+ */
+ public void storeByteField(int fieldNum, byte value){
+ this.value = new Byte(value);
+ }
+
+
+ public byte fetchByteField(int fieldNum) {
+ return ((Byte)value).byteValue();
+ }
+
+ /**
+ * Provides the means by which the value of a short field can be given
+ * by a StateManager to an object that needs the value.
+ * @param fieldNum Field number of the field in the object whose value is
+ * given.
+ * @param value Short that is the value of a particular field.
+ */
+ public void storeShortField(int fieldNum, short value){
+ this.value = new Short(value);
+ }
+
+
+ public short fetchShortField(int fieldNum) {
+ return ((Short)value).shortValue();
+ }
+
+ /**
+ * Provides the means by which the value of a int field can be given
+ * by a StateManager to an object that needs the value.
+ * @param fieldNum Field number of the field in the object whose value is
+ * given.
+ * @param value Int that is the value of a particular field.
+ */
+ public void storeIntField(int fieldNum, int value){
+ this.value = new Integer(value);
+ }
+
+
+ public int fetchIntField(int fieldNum) {
+ return ((Integer)value).intValue();
+ }
+
+ /**
+ * Provides the means by which the value of a long field can be given
+ * by a StateManager to an object that needs the value.
+ * @param fieldNum Field number of the field in the object whose value is
+ * given.
+ * @param value Long that is the value of a particular field.
+ */
+ public void storeLongField(int fieldNum, long value){
+ this.value = new Long(value);
+ }
+
+
+ public long fetchLongField(int fieldNum) {
+ return ((Long)value).longValue();
+ }
+
+ /**
+ * Provides the means by which the value of a field can be given
+ * by a StateManager to an object that needs the value.
+ * @param fieldNum Field number of the field in the object whose value is
+ * given.
+ * @param value that is the value of a particular field.
+ */
+ public void storeFloatField(int fieldNum, float value){
+ this.value = new Float(value);
+ }
+
+
+ public float fetchFloatField(int fieldNum) {
+ return ((Float)value).floatValue();
+ }
+
+ /**
+ * Provides the means by which the value of a double field can be given
+ * by a StateManager to an object that needs the value.
+ * @param fieldNum Field number of the field in the object whose value is
+ * given.
+ * @param value Double that is the value of a particular field.
+ */
+ public void storeDoubleField(int fieldNum, double value){
+ this.value = new Double(value);
+ }
+
+
+ public double fetchDoubleField(int fieldNum) {
+ return ((Double)value).doubleValue();
+ }
+
+ /**
+ * Provides the means by which the value of a String field can be given
+ * by a StateManager to an object that needs the value.
+ * @param fieldNum Field number of the field in the object whose value is
+ * given.
+ * @param value String that is the value of a particular field.
+ */
+ public void storeStringField(int fieldNum, String value){
+ this.value = value;
+ }
+
+
+ public String fetchStringField(int fieldNum) {
+ return (String)value;
+ }
+
+ /**
+ * Provides the means by which the value of an Object field can be given
+ * by a StateManager to an object that needs the value.
+ * @param fieldNum Field number of the field in the object whose value is
+ * given.
+ * @param value Object that is the value of a particular field.
+ */
+ public void storeObjectField(int fieldNum, Object value){
+ this.value = value;
+ }
+
+
+ public Object fetchObjectField(int fieldNum) {
+ return value;
+ }
+
+}
Added: incubator/jdo/trunk/query20/src/java/org/apache/jdo/impl/jdoql/jdoqlc/TypeSupport.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/query20/src/java/org/apache/jdo/impl/jdoql/jdoqlc/TypeSupport.java?rev=171353&view=auto
==============================================================================
--- incubator/jdo/trunk/query20/src/java/org/apache/jdo/impl/jdoql/jdoqlc/TypeSupport.java (added)
+++ incubator/jdo/trunk/query20/src/java/org/apache/jdo/impl/jdoql/jdoqlc/TypeSupport.java Sun May 22 11:08:57 2005
@@ -0,0 +1,428 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * TypeTable.java
+ *
+ * Created on August 28, 2001
+ */
+
+package org.apache.jdo.impl.jdoql.jdoqlc;
+
+import java.util.*;
+import java.lang.Class;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Field;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import javax.jdo.JDOHelper;
+import javax.jdo.JDOException;
+import javax.jdo.JDOFatalInternalException;
+import javax.jdo.PersistenceManager;
+import javax.jdo.spi.PersistenceCapable;
+
+import org.apache.jdo.impl.model.java.ErrorType;
+import org.apache.jdo.impl.model.java.PredefinedType;
+import org.apache.jdo.impl.model.java.WrapperClassType;
+import org.apache.jdo.impl.model.java.reflection.ReflectionJavaField;
+import org.apache.jdo.impl.model.java.runtime.RuntimeJavaModelFactory;
+import org.apache.jdo.jdoql.JDOQueryException;
+import org.apache.jdo.model.ModelFatalException;
+import org.apache.jdo.model.java.JavaField;
+import org.apache.jdo.model.java.JavaModel;
+import org.apache.jdo.model.java.JavaType;
+import org.apache.jdo.model.jdo.JDOClass;
+import org.apache.jdo.model.jdo.JDOCollection;
+import org.apache.jdo.model.jdo.JDOField;
+import org.apache.jdo.model.jdo.JDORelationship;
+import org.apache.jdo.pm.PersistenceManagerInternal;
+import org.apache.jdo.state.FieldManager;
+import org.apache.jdo.state.StateManagerInternal;
+import org.apache.jdo.util.I18NHelper;
+
+
+/**
+ * Provides query convenience methods to deal with Java/JDO metadata.
+ *
+ * @author Michael Bouschen
+ * @since JDO 1.0.1
+ */
+public class TypeSupport
+{
+ /** The JavaModel for the class loader of the candidate class. */
+ protected JavaModel applicationJavaModel;
+
+ /** The runtime JavaModel factory. */
+ private static final RuntimeJavaModelFactory javaModelFactory =
+ (RuntimeJavaModelFactory) AccessController.doPrivileged(
+ new PrivilegedAction () {
+ public Object run () {
+ return RuntimeJavaModelFactory.getInstance();
+ }
+ }
+ );
+
+ /** I18N support */
+ private static final I18NHelper msg = I18NHelper.getInstance(
+ "org.apache.jdo.impl.jdoql.Bundle", TypeSupport.class.getClassLoader()); //NOI18N
+
+ /**
+ * This methods sets the application JavaModel to the JavaModel
+ * instance for the specified ClassLoader.
+ * @param classLoader the class loader of the candidate class.
+ */
+ public void initApplicationJavaModel(ClassLoader classLoader)
+ {
+ this.applicationJavaModel = javaModelFactory.getJavaModel(classLoader);
+ }
+
+ /**
+ * Returns the JavaType representation for the type with the specified
+ * name. This method uses the application JavaModel for the type
+ * lookup. This means any type is checked in the context of the
+ * JavaModel for the class loader of the candidate class.
+ * @param name the name of the type to be checked.
+ * @return the JavaType object representing the type with the
+ * specified name or null when the type was not found.
+ */
+ public JavaType checkType(String name)
+ {
+ return applicationJavaModel.getJavaType(name);
+ }
+
+ /**
+ * Checks for the Java Type with the specified class object.
+ * @param clazz the clazz object of the type to be checked.
+ * @return the TypeDescriptor object representing the type
+ */
+ public JavaType checkType(Class clazz)
+ {
+ if (clazz == null)
+ return null;
+ return javaModelFactory.getJavaType(clazz);
+ }
+
+ /**
+ * Implements binary numeric promotion as defined in the
+ * Java Language Specification section 5.6.2
+ */
+ public static JavaType binaryNumericPromotion(JavaType left, JavaType right)
+ {
+ if ((left == null) || (right == null))
+ return ErrorType.errorType;
+
+ if (isNumericType(left) && isNumericType(right)) {
+ if (left.equals(PredefinedType.doubleType) ||
+ right.equals(PredefinedType.doubleType)) {
+ return PredefinedType.doubleType;
+ }
+ else if (left.equals(PredefinedType.floatType) ||
+ right.equals(PredefinedType.floatType)) {
+ return PredefinedType.floatType;
+ }
+ else if (left.equals(PredefinedType.longType) ||
+ right.equals(PredefinedType.longType)) {
+ return PredefinedType.longType;
+ }
+ else {
+ return PredefinedType.intType;
+ }
+ }
+
+ return ErrorType.errorType;
+ }
+
+ /**
+ * Implements unray numeric promotion as defined in the
+ * Java Language Specification section 5.6.1
+ */
+ public static JavaType unaryNumericPromotion(JavaType type)
+ {
+ if (type == null)
+ return ErrorType.errorType;
+
+ if (isNumericType(type)) {
+ if (type.equals(PredefinedType.byteType) ||
+ type.equals(PredefinedType.shortType) ||
+ type.equals(PredefinedType.charType)) {
+ return PredefinedType.intType;
+ }
+ else {
+ return type;
+ }
+ }
+
+ return ErrorType.errorType;
+ }
+
+ /**
+ * Returns the java.lang.Class instance for the specified type.
+ * @param type the type to be checked
+ * @return the corresponding class instance.
+ */
+ public static Class getJavaClass(JavaType type)
+ {
+ return javaModelFactory.getJavaClass(type);
+ }
+
+ //========= Interrogative methods ==========
+
+ /**
+ * Returns <code>true</code> if the specified type is
+ * boolean or java.lang.Boolean.
+ * @param type the type to be checked
+ * @return <code>true</code> if type is boolean or java.lang.Boolean;
+ * <code>false</code> otherwise.
+ */
+ public static boolean isBooleanType(JavaType type)
+ {
+ return type.equals(PredefinedType.booleanType) ||
+ type.equals(PredefinedType.booleanClassType);
+ }
+
+ /**
+ * Returns <code>true</code> if the specified type is
+ * char or java.lang.Character
+ * @param type the type to be checked
+ * @return <code>true</code> if type is char or java.lang.Character
+ * <code>false</code> otherwise.
+ */
+ public static boolean isCharType(JavaType type)
+ {
+ return type.equals(PredefinedType.charType) ||
+ type.equals(PredefinedType.characterClassType);
+ }
+
+ /**
+ * Returns <code>true</code> if the specified type is an interal type
+ * or a Java wrapper class for an interal type.
+ * @param type the type to be checked
+ * @return <code>true</code> if type is an integral type or a Java
+ * wrapper for an integral type; <code>false</code> otherwise.
+ */
+ public static boolean isIntegralType(JavaType type)
+ {
+ return type.isIntegral() ||
+ (type.isWrapperClass() &&
+ ((WrapperClassType)type).getWrappedPrimitiveType().isIntegral());
+ }
+
+ /**
+ * Returns <code>true</code> if specified type is a number type:
+ * <br>
+ * a numeric primitive
+ * <br>
+ * a numeric wrapper class
+ * <br>
+ * java.math.BigDecimal, java.math.BigInteger.
+ * @param type the type to be checked
+ * @return <code>true</code> if type is a number type;
+ * <code>false</code> otherwise.
+ */
+ public static boolean isNumberType(JavaType type)
+ {
+ return isNumericType(type) ||
+ isNumericWrapperClassType(type) ||
+ isMathType(type);
+ }
+
+ /**
+ * Returns <code>true</code> if the specified type is a Java wrapper
+ * class type for a numeric primitive type.
+ * @param type the type to be checked
+ * @return <code>true</code> if type is a numeric wrapper class type;
+ * <code>false</code> otherwise.
+ */
+ public static boolean isNumericWrapperClassType(JavaType type)
+ {
+ return type.isWrapperClass() &&
+ isNumericType(((WrapperClassType)type).getWrappedPrimitiveType());
+ }
+
+ /**
+ * Returns <code>true</code> if the specified type is a either a
+ * integral or a floating point type.
+ * @param type the type to be checked
+ * @return <code>true</code> if type is a numeric type;
+ * <code>false</code> otherwise.
+ */
+ public static boolean isNumericType(JavaType type)
+ {
+ return type.isIntegral() || type.isFloatingPoint();
+ }
+
+ /**
+ * Returns <code>true</code> if the specified type is either
+ * java.math.BigDecimal or java.math.BigInteger.
+ * @param type the type to be checked
+ * @return <code>true</code> if type is BigDecimal or BigInteger;
+ * <code>false</code> otherwise.
+ */
+ public static boolean isMathType(JavaType type)
+ {
+ return PredefinedType.bigDecimalType.equals(type) ||
+ PredefinedType.bigIntegerType.equals(type);
+ }
+
+ //========= Field access methods ==========
+
+ /** */
+ public static boolean isStaticField(JavaField field)
+ {
+ int modifiers = field.getModifiers();
+ return Modifier.isStatic(modifiers);
+ }
+
+ /** */
+ public static JavaType getElementType(JavaField field)
+ {
+ JDOField jdoField = field.getJDOField();
+ if (jdoField != null) {
+ // check relationship
+ try {
+ JDORelationship jdoRelationship = jdoField.getRelationship();
+ if (jdoRelationship instanceof JDOCollection) {
+ return ((JDOCollection)jdoRelationship).getElementType();
+ }
+ }
+ catch (ModelFatalException ex) {
+ throw new JDOQueryException(ex.getMessage());
+ }
+ }
+ JavaType fieldType = field.getType();
+ if (fieldType.isJDOSupportedCollection())
+ return PredefinedType.objectType;
+
+ // If this code is reached the field is not specified as collection
+ // in the JDO metadata and does not have a JDO supported collection
+ // type => internal error.
+ String fieldName = field.getName();
+ String className = field.getDeclaringClass().getName();
+ throw new JDOFatalInternalException(
+ msg.msg("ERR_CollectionFieldExpected", //NOI18N
+ fieldName, className, fieldType.getName()));
+ }
+
+ /**
+ * field value of a managed field. Not using reflection.
+ */
+ public static Object getFieldValue(int fieldNumber,
+ PersistenceManagerInternal pm,
+ Object object)
+ {
+ PersistenceCapable pc = (PersistenceCapable)object;
+ StateManagerInternal sm = pm.findStateManager(pc);
+ FieldManager fm = new SimpleFieldManager();
+ // Call isLoaded to ensure the field with the specified
+ // field number is loaded.
+ // NOTE, this code is not StateManager implementation neutral,
+ // because it relies on the fact that isLoaded actually loads
+ // the field. A neutral implementation would check the returns
+ // value of isLoaded and if it returns false it would call a
+ // jdoGetXXX to load the field.
+ sm.isLoaded(pc, fieldNumber);
+ sm.provideField(fieldNumber, fm , false);
+ // NOTE, this call assumes that fetchObjectField return the
+ // field value regardless which type the field has. Only
+ // SimpleFieldManager from common.query.util.types has this
+ // behavior. This is a workaround and needs to be changed!
+ return fm.fetchObjectField(fieldNumber);
+ }
+
+ /**
+ * Get field value via reflection
+ */
+ public static Object getFieldValue(Field field, Object object)
+ {
+ try {
+ return field.get(object);
+ }
+ catch (IllegalAccessException ex) {
+ String fieldName = field.getName();
+ String className = field.getDeclaringClass().getName();
+ throw new JDOQueryException(
+ msg.msg("EXC_CannotAccessField", //NOI18N
+ fieldName, className, ex));
+ }
+ }
+
+ /**
+ * Returns the fieldNumber of the specified field.
+ * @return field number
+ */
+ public static int getFieldNumber(JDOField jdoField,
+ PersistenceManager pm,
+ Object object)
+ {
+ if ((object == null) || // null object means static field access
+ (jdoField == null) || // no jdo field info
+ !jdoField.isManaged()) { // field is not managed
+ return -1; // use reflection
+ }
+
+ PersistenceManager instancePM =
+ JDOHelper.getPersistenceManager(object);
+ if (instancePM == null) { // object is a transient instance
+ return -1; // use reflection
+ }
+ else if (!instancePM.equals(pm)) {
+ // instancePM is NOT the one from the query => exception
+ throw new JDOException(
+ msg.msg("EXC_InstanceBoundToDifferentPM", object)); //NOI18N
+ }
+
+ // return the field number from the JDOField
+ return jdoField.getFieldNumber();
+ }
+
+ /**
+ * Returns a accessible java.lang.reflect.Field instance for the
+ * specified JavaField. Note, this method gets a new Field instance
+ * from reflection and sets the accessibility. The method requires the
+ * caller to have the permission ReflectPermission("suppressAccessChecks").
+ * @param javaField the JavaField
+ * @return accessible Field instance
+ */
+ public static Field getAccessibleField(JavaField javaField)
+ {
+ // Get a new java.lang.reflect.Field instance. The query runtime
+ // needs an accessible Field instance.
+ Class clazz = javaModelFactory.getJavaClass(javaField.getDeclaringClass());
+ String fieldName = javaField.getName();
+ final Field field =
+ ReflectionJavaField.getDeclaredFieldPrivileged(clazz, fieldName);
+ if (field == null) {
+ throw new JDOQueryException(
+ msg.msg("EXC_CannotFindField", //NOI18N
+ fieldName, clazz.getName()));
+ }
+
+ // if the field is not accessible, try to set the accessible flag.
+ if (!field.isAccessible()) {
+ try {
+ field.setAccessible(true);
+ }
+ catch (SecurityException ex) {
+ throw new JDOQueryException(
+ msg.msg("EXC_CannotChangeAccessibility", //NOI18N
+ fieldName, clazz.getName()));
+ }
+ }
+ return field;
+ }
+
+}