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;
+    }
+    
+}