
// Transmogrify License
// 
// Copyright (c) 2001, ThoughtWorks, Inc.
// All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// - Redistributions of source code must retain the above copyright notice,
//   this list of conditions and the following disclaimer.
// - Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// Neither the name of the ThoughtWorks, Inc. nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package com.puppycrawl.tools.checkstyle.checks.usage.transmogrify;



import java.util.Vector;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogConfigurationException;
import org.apache.commons.logging.LogFactory;

import com.puppycrawl.tools.checkstyle.api.TokenTypes;

/**
 * The resolver is responsible for traversing all the various
 * definitions in a symbol table and resolving references in them.
 *
 * @see SymbolTable
 */

public class Resolver extends DefinitionTraverser {

    /** true if the log factory has been initialized */
    private boolean mInitialized = false;

    /** Factory for creating org.apache.commons.logging.Log instances */
    private LogFactory mLogFactory;

    /**
     * constructor with <code>SymbolTable</code> to be resolved
     */
    public Resolver(SymbolTable symbolTable) {
        super(symbolTable);

        try {
            mLogFactory = LogFactory.getFactory();
        }
        catch (LogConfigurationException e) {
            System.out.println("log configuration exception" + e);
        }
        mInitialized = true;

    }

    /**
     * resolves the symbol table
     * @return <code>void</code>
     * @see #traverse()
     */
    public void resolve() {
        traverse();
    }

    protected void handleSList(SymTabAST node, Scope scope) {
        SymTabASTIterator iterator = node.getChildren();
        while (iterator.hasNext()) {
            SymTabAST current = iterator.nextChild();
            resolveExpression(current, scope, null, true);
        }
    }

    protected void handleAnonymousInnerClass(AnonymousInnerClass innerClass) {
        SymTabAST objblock = innerClass.getTreeNode();
        SymTabAST expression = (SymTabAST) objblock.getFirstChild();
        while (expression != null) {
            resolveExpression(expression, innerClass, null, true);
            expression = (SymTabAST) expression.getNextSibling();
        }
    }

    /**
     * processes a <code>ClassDef</code> and resolves references in it
     *
     * @param classDef the <code>ClassDef</code> to process
     */
    protected void handleClass(ClassDef classDef) {
        SymTabAST node = classDef.getTreeNode();

        if (node != null) {
            SymTabAST nameNode = node.findFirstToken(TokenTypes.IDENT);
            nameNode.setDefinition(classDef, classDef, true);

            SymTabAST extendsClause =
                node.findFirstToken(TokenTypes.EXTENDS_CLAUSE);
            SymTabAST extendedClassNode =
                (SymTabAST) extendsClause.getFirstChild();

            while (extendedClassNode != null) {
                IClass superClass =
                    resolveClass(extendedClassNode, classDef, null, true);
                extendedClassNode.setDefinition(superClass, classDef, true);
                extendedClassNode =
                    (SymTabAST) extendedClassNode.getNextSibling();
            }

            SymTabAST implementsNode =
                node.findFirstToken(TokenTypes.IMPLEMENTS_CLAUSE);

            if (implementsNode != null) {
                SymTabAST interfaceNode =
                    (SymTabAST) (implementsNode.getFirstChild());
                while (interfaceNode != null) {
                    resolveClass(interfaceNode, classDef, null, true);
                    interfaceNode =
                        (SymTabAST) (interfaceNode.getNextSibling());
                }
            }
        }
    }

    /**
     * processes a <code>MethodDef</code> and resolves references in it
     *
     * @param method the <code>MethodDef</code> to process
     */
    protected void handleMethod(MethodDef method) {
        SymTabAST node = method.getTreeNode();

        SymTabAST nameNode = node.findFirstToken(TokenTypes.IDENT);
        nameNode.setDefinition(method, method, true);

        // references to classes in return type
        SymTabAST returnTypeNode = node.findFirstToken(TokenTypes.TYPE);

        if (returnTypeNode != null) {
            // this is not a constructor
            resolveExpression(returnTypeNode, method, null, true);
        }

        SymTabAST throwsNode =
            node.findFirstToken(TokenTypes.LITERAL_THROWS);
        if (throwsNode != null) {
            SymTabAST exception = (SymTabAST) throwsNode.getFirstChild();
            while (exception != null) {
                // handle Checkstyle grammar
                if (exception.getType() != TokenTypes.COMMA) {
                    resolveClass(exception, method, null, true);
                }
                exception = (SymTabAST) exception.getNextSibling();
            }
        }

        // references to classes in parameters

        // the body -- this would be better its own function
        SymTabAST slist = node.findFirstToken(TokenTypes.SLIST);

        if (slist != null) {
            handleSList(slist, method);
        }
    }

    /**
     * processes a <code>BlockDef</code> and resolves references in it
     *
     * @param block the <code>BlockDef</code> to process
     */
    protected void handleBlock(BlockDef block) {
        SymTabAST node = block.getTreeNode();

        switch (node.getType()) {

            case TokenTypes.LITERAL_FOR :
                handleFor(block);
                break;

            case TokenTypes.LITERAL_IF :
                handleIf(block);
                break;

            case TokenTypes.LITERAL_WHILE :
                handleWhileAndSynchronized(block);
                break;

            case TokenTypes.LITERAL_DO :
                handleDoWhile(block);
                break;

            case TokenTypes.LITERAL_TRY :
            case TokenTypes.LITERAL_FINALLY :
                SymTabAST slist = node.findFirstToken(TokenTypes.SLIST);

                handleSList(slist, block);
                break;

            case TokenTypes.LITERAL_CATCH :
                handleCatch(block);
                break;

            case TokenTypes.LITERAL_SWITCH :
                handleSwitch(block);
                break;

            case TokenTypes.SLIST :
                handleSList(node, block);
                break;

            case TokenTypes.EXPR :
                resolveExpression(node, block, null, true);
                break;

            case TokenTypes.INSTANCE_INIT :
            case TokenTypes.STATIC_INIT :
                handleSList((SymTabAST) node.getFirstChild(), block);
                break;

            case TokenTypes.LITERAL_SYNCHRONIZED :
                handleWhileAndSynchronized(block);
                break;

            case TokenTypes.LITERAL_ASSERT :
                handleAssert(block);
                break;

            default :
                if (mInitialized) {
                    final Log log = mLogFactory.getInstance(this.getClass());
                    log.error(
                        "Unhandled block "
                            + block
                            + " of type "
                            + node.getType());
                }
        }
    }

    /**
     * @param block
     */
    private void handleAssert(BlockDef block) {
        SymTabAST node = block.getTreeNode();

        SymTabAST conditional =
            (node.findFirstToken(TokenTypes.EXPR));
        resolveExpression(conditional, block, null, true);

        SymTabAST message = (SymTabAST) conditional.getNextSibling();
        while ((message != null) && (message.getType() != TokenTypes.EXPR)) {
            message = (SymTabAST) message.getNextSibling();
        }
        if (message != null) {
            resolveExpression(message, block, null, true);
        }
    }

    /**
     * processes a switch statement and resolves references in it
     *
     * @param block the <code>BlockDef</code> to process
     */
    private void handleSwitch(BlockDef block) {
        SymTabAST node = block.getTreeNode();

        SymTabAST expr = node.findFirstToken(TokenTypes.EXPR);
        resolveExpression(expr, block, null, true);

        SymTabAST caseGroup = (SymTabAST) (expr.getNextSibling());
        while (caseGroup != null
            && (caseGroup.getType() != TokenTypes.CASE_GROUP)) {
            caseGroup = (SymTabAST) caseGroup.getNextSibling();
        }
        if (caseGroup != null) {
            while (caseGroup.getType() == TokenTypes.CASE_GROUP) {
                SymTabAST caseNode =
                    caseGroup.findFirstToken(TokenTypes.LITERAL_CASE);
                while (caseNode != null
                    && caseNode.getType() == TokenTypes.LITERAL_CASE) {
                    resolveExpression(
                        (SymTabAST) caseNode.getFirstChild(),
                        block,
                        null,
                        true);
                    caseNode = (SymTabAST) caseNode.getNextSibling();
                }

                SymTabAST caseSlist =
                    caseGroup.findFirstToken(TokenTypes.SLIST);
                handleSList(caseSlist, block);

                caseGroup = (SymTabAST) (caseGroup.getNextSibling());
            }
        }
    }

    /**
     * processes a catch block and resolves references in it
     *
     * @param block the <code>BlockDef</code> to process
     */
    private void handleCatch(BlockDef block) {
        SymTabAST node = block.getTreeNode();

        SymTabAST slist = node.findFirstToken(TokenTypes.SLIST);
        handleSList(slist, block);
    }

    /**
     * processes a for loop and resolves references in it
     *
     * @param block the <code>BlockDef</code> to process
     */
    private void handleFor(BlockDef block) {
        SymTabAST node = block.getTreeNode();

        SymTabAST body;
        SymTabAST forEach = node.findFirstToken(TokenTypes.FOR_EACH_CLAUSE);
        if (forEach == null) {
            SymTabAST init = node.findFirstToken(TokenTypes.FOR_INIT);
            // only need to handle the elist case.  if the init node is a variable
            // definition, the variable def will be handled later on in the resolution
            if (init.getFirstChild() != null) {
                if (init.getFirstChild().getType() == TokenTypes.ELIST) {
                    resolveExpression(
                        (SymTabAST) (init.getFirstChild()),
                        block,
                        null,
                        true);
                }
            }

            SymTabAST cond = node.findFirstToken(TokenTypes.FOR_CONDITION);
            if (cond.getFirstChild() != null) {
                resolveExpression(
                    (SymTabAST) (cond.getFirstChild()),
                    block,
                    null,
                    true);
            }

            SymTabAST iterator = node.findFirstToken(TokenTypes.FOR_ITERATOR);
            if (iterator.getFirstChild() != null) {
                resolveExpression(
                    (SymTabAST) (iterator.getFirstChild()),
                    block,
                    null,
                    true);
            }
            body = (SymTabAST) (iterator.getNextSibling());
        }
        else {
            resolveExpression(
                (forEach.findFirstToken(TokenTypes.EXPR)),
                block,
                null,
                true);
            body = (SymTabAST) (forEach.getNextSibling());
        }
        //could be an SLIST, EXPR or an EMPTY_STAT
        if (body.getType() == TokenTypes.RPAREN) {
            body = (SymTabAST) body.getNextSibling();
        }
        if (body.getType() == TokenTypes.SLIST) {
            handleSList(body, block);
        }
        else {
            resolveExpression(body, block, null, true);
        }

    }

    /**
     * processes an if statement and resolves references in it
     *
     * @param block the <code>BlockDef</code> to process
     */
    private void handleIf(BlockDef block) {
        SymTabAST node = block.getTreeNode();

        SymTabAST conditional =
            (node.findFirstToken(TokenTypes.EXPR));
        resolveExpression(conditional, block, null, true);

        SymTabAST body = (SymTabAST) conditional.getNextSibling();
        // Handle Checkstyle grammar
        if (body.getType() == TokenTypes.RPAREN) {
            body = (SymTabAST) body.getNextSibling();
        }
        if (body != null) {
        	if (body.getType() == TokenTypes.SLIST) {
            	handleSList(body, block);
        	}
        	else {
            	resolveExpression(body, block, null, true);
        	}

	        SymTabAST elseBody = (SymTabAST) body.getNextSibling();
	        //handle Checkstyle grammar
	        while ((elseBody != null)
	            && (elseBody.getType() != TokenTypes.LITERAL_ELSE)) {
	            elseBody = (SymTabAST) elseBody.getNextSibling();
	        }
	        /*
	         if (elseBody != null && elseBody.getType() == TokenTypes.SLIST) {
	             handleSList(elseBody, block);
	         }else{
	             resolveExpression(elseBody, block, null, true);
	         }
	         */
	        if (elseBody != null) {
	            elseBody = (SymTabAST) elseBody.getFirstChild();
	        }
	        if (elseBody != null) {
	            resolveExpression(elseBody, block.getParentScope(), null, true);
	        }
	    }
    }

    /**
     * processes a while loop and resolves references in it
     *
     * @param block the <code>BlockDef</code> to process
     */
    private void handleWhileAndSynchronized(BlockDef block) {
        SymTabAST node = block.getTreeNode();

        SymTabAST condition =
            (node.findFirstToken(TokenTypes.EXPR));
        SymTabAST slist = (SymTabAST) (condition.getNextSibling());
        // handle Checkstyle grammar
        if (slist.getType() == TokenTypes.RPAREN) {
            slist = (SymTabAST) slist.getNextSibling();
        }

        resolveExpression(condition, block, null, true);
        handleSList(slist, block);
    }

    private void handleDoWhile(BlockDef block) {
        SymTabAST node = block.getTreeNode();

        SymTabAST slist = (SymTabAST) node.getFirstChild();
        SymTabAST condition =
            node.findFirstToken(TokenTypes.EXPR);

        handleSList(slist, block);
        resolveExpression(condition, block, null, true);
    }

    /**
     * processes a variable definition and resolves references in it
     *
     * @param variable the <code>VariableDef</code> to process
     */
    protected void handleVariable(VariableDef variable) {
        SymTabAST node = variable.getTreeNode();
        Scope location = variable.getParentScope();

        SymTabAST nameNode = node.findFirstToken(TokenTypes.IDENT);
        nameNode.setDefinition(variable, location, true);

        SymTabAST typeNode = node.findFirstToken(TokenTypes.TYPE);
        resolveType(typeNode, location, null, true);

        SymTabAST assignmentNode = node.findFirstToken(TokenTypes.ASSIGN);
        if (assignmentNode != null) {
            resolveExpression(
                (SymTabAST) (assignmentNode.getFirstChild()),
                variable.getParentScope(),
                null,
                true);
        }
    }

    /**
     * processes a label and resolves references in it
     *
     * @param label the <code>LabelDef</code> to process
     */
    protected void handleLabel(LabelDef label) {
        SymTabAST node = label.getTreeNode();
        ((SymTabAST) node.getFirstChild()).setDefinition(
            label,
            label.getParentScope(),
            true);
    }

    /**
     * Resolves Java expressions, returning the type to which the expression
     * evalutes.  If this is the reference creation phase, any references found during resolution are created and
     * resolved.
     *
     * @param expression the <code>SymTabAST</code> representing the expression
     * @param location the <code>Scope</code> in which the expression occours.
     * @param context the <code>Scope</code> in which the search for the
     *                definition will start
     * @param referencePhase whether or not this is the reference phase of
     *                       table construction
     *
     * @return the <code>ClassDef</code> representing the type to which the
     *         expression evalutes.
     */
    public IClass resolveExpression(
        SymTabAST expression,
        Scope location,
        IClass context,
        boolean referencePhase) {
        IClass result = null;

        try {

            switch (expression.getType()) {

                case TokenTypes.TYPECAST :
                    result =
                        resolveTypecast(
                            expression,
                            location,
                            context,
                            referencePhase);
                    break;
                case TokenTypes.EXPR :
                case TokenTypes.LITERAL_RETURN :
                    if (expression.getFirstChild() != null) {
                        result =
                            resolveExpression(
                                (SymTabAST) expression.getFirstChild(),
                                location,
                                context,
                                referencePhase);
                    }
                    else {
                        // YOU WRITE BAD CODE!
                    }
                    break;

                case TokenTypes.ELIST :

                    SymTabAST child = (SymTabAST) (expression.getFirstChild());
                    while (child != null) {
                        if (child.getType() != TokenTypes.COMMA) {
                            resolveExpression(
                                child,
                                location,
                                context,
                                referencePhase);
                        }
                        child = (SymTabAST) (child.getNextSibling());
                    }
                    break;

                case TokenTypes.IDENT :
                    result =
                        resolveIdent(
                            expression,
                            location,
                            context,
                            referencePhase);
                    break;

                case TokenTypes.TYPE :
                    result =
                        resolveType(
                            expression,
                            location,
                            context,
                            referencePhase);
                    break;

                case TokenTypes.METHOD_CALL :
                //case TokenTypes.SUPER_CTOR_CALL :
                    result =
                        resolveMethod(
                            expression,
                            location,
                            context,
                            referencePhase);
                    break;

                case TokenTypes.LITERAL_THIS :
                    result = resolveLiteralThis(expression, location, context);
                    break;

                case TokenTypes.LITERAL_SUPER :
                    result = resolveLiteralSuper(expression, location, context);
                    break;

                case TokenTypes.DOT :
                    result =
                        resolveDottedName(
                            expression,
                            location,
                            context,
                            referencePhase);
                    break;

                case TokenTypes.LITERAL_NEW :
                case TokenTypes.CTOR_CALL :
                case TokenTypes.SUPER_CTOR_CALL :
                    result =
                        resolveNew(
                            expression,
                            location,
                            context,
                            referencePhase);
                    break;

                case TokenTypes.LITERAL_BOOLEAN :
                case TokenTypes.LITERAL_DOUBLE :
                case TokenTypes.LITERAL_FLOAT :
                case TokenTypes.LITERAL_LONG :
                case TokenTypes.LITERAL_INT :
                case TokenTypes.LITERAL_SHORT :
                case TokenTypes.LITERAL_BYTE :
                case TokenTypes.LITERAL_CHAR :
                    result =
                        resolvePrimitiveType(
                            expression,
                            location,
                            context,
                            referencePhase);
                    break;

                case TokenTypes.NUM_INT :
                case TokenTypes.NUM_LONG :
                    result = resolveNumInt(expression, location, context);
                    break;

                case TokenTypes.NUM_FLOAT :
                case TokenTypes.NUM_DOUBLE :
                    result = resolveNumFloat(expression, location, context);
                    break;

                case TokenTypes.STRING_LITERAL :
                    result =
                        resolveStringLiteral(expression, location, context);
                    break;

                case TokenTypes.CHAR_LITERAL :
                    result = resolveCharLiteral(expression, location, context);
                    break;

                case TokenTypes.ASSIGN :
                case TokenTypes.PLUS_ASSIGN :
                case TokenTypes.MINUS_ASSIGN :
                case TokenTypes.STAR_ASSIGN :
                case TokenTypes.DIV_ASSIGN :
                case TokenTypes.MOD_ASSIGN :
                case TokenTypes.SR_ASSIGN :
                case TokenTypes.BSR_ASSIGN :
                case TokenTypes.SL_ASSIGN :
                case TokenTypes.BAND_ASSIGN :
                case TokenTypes.BXOR_ASSIGN :
                case TokenTypes.BOR_ASSIGN :
                    resolveAssignment(
                        expression,
                        location,
                        context,
                        referencePhase);
                    break;

                case TokenTypes.LOR :
                case TokenTypes.LAND :
                case TokenTypes.NOT_EQUAL :
                case TokenTypes.EQUAL :
                case TokenTypes.LT :
                case TokenTypes.GT :
                case TokenTypes.LE :
                case TokenTypes.GE :
                    result =
                        resolveBooleanExpression(
                            expression,
                            location,
                            context,
                            referencePhase);
                    break;

                case TokenTypes.LITERAL_INSTANCEOF :
                    result =
                        resolveInstanceOf(
                            expression,
                            location,
                            context,
                            referencePhase);
                    break;

                case TokenTypes.LITERAL_TRUE :
                case TokenTypes.LITERAL_FALSE :
                    result =
                        resolveBooleanLiteral(expression, location, context);
                    break;

                case TokenTypes.LNOT :
                    result =
                        resolveBooleanUnary(
                            expression,
                            location,
                            context,
                            referencePhase);
                    break;

                case TokenTypes.INC :
                case TokenTypes.POST_INC :
                case TokenTypes.DEC :
                case TokenTypes.POST_DEC :
                case TokenTypes.UNARY_PLUS :
                case TokenTypes.UNARY_MINUS :
                    result =
                        resolveUnaryExpression(
                            expression,
                            location,
                            context,
                            referencePhase);
                    break;

                case TokenTypes.PLUS :
                case TokenTypes.MINUS :
                case TokenTypes.DIV :
                case TokenTypes.STAR :
                case TokenTypes.BAND :
                case TokenTypes.BOR :
                case TokenTypes.BXOR :
                case TokenTypes.MOD :
                    result =
                        resolveArithmeticExpression(
                            expression,
                            location,
                            context,
                            referencePhase);
                    break;

                case TokenTypes.LITERAL_BREAK :
                case TokenTypes.LITERAL_CONTINUE :
                    resolveGoto(expression, location, context, referencePhase);
                    break;

                case TokenTypes.LPAREN :
                    result = resolveExpression(
                        //TODO: child || sibling?
     (SymTabAST) (expression.getNextSibling()),
                        //(SymTabAST) (expression.getFirstChild()),
    location, context, referencePhase);
                    break;

                case TokenTypes.INDEX_OP :
                    result =
                        resolveArrayAccess(
                            expression,
                            location,
                            context,
                            referencePhase);
                    break;

                case TokenTypes.LITERAL_NULL :
                    result = new NullClass();
                    break;

                case TokenTypes.QUESTION :
                    result =
                        resolveQuestion(
                            expression,
                            location,
                            context,
                            referencePhase);
                    break;

                case TokenTypes.LITERAL_CLASS :
                    result = resolveLiteralClass();
                    break;

                case TokenTypes.ARRAY_INIT :
                    resolveArrayInitializer(
                        expression,
                        location,
                        context,
                        referencePhase);
                    break;

                case TokenTypes.LITERAL_THROW :
                    resolveThrowExpression(
                        expression,
                        location,
                        context,
                        referencePhase);
                    break;

                case TokenTypes.SL :
                case TokenTypes.SR :
                case TokenTypes.BSR :
                    result =
                        resolveShiftOperator(
                            expression,
                            location,
                            context,
                            referencePhase);
                    break;

                case TokenTypes.BNOT :
                    resolveBitwiseNot(
                        expression,
                        location,
                        context,
                        referencePhase);
                    break;

                case TokenTypes.LITERAL_ASSERT :
//                                        resolveAssert(
//                                            expression,
//                                            location,
//                                            context,
//                                            referencePhase);
                    break;

                case TokenTypes.RPAREN :
                case TokenTypes.EMPTY_STAT :
                    //    case TokenTypes.ML_COMMENT:
                    //    case TokenTypes.SL_COMMENT:
                case TokenTypes.VARIABLE_DEF :
                case TokenTypes.METHOD_DEF :
                case TokenTypes.CLASS_DEF :
                case TokenTypes.LITERAL_FOR :
                case TokenTypes.LITERAL_WHILE :
                case TokenTypes.LITERAL_IF :
                case TokenTypes.LITERAL_VOID :
                    //    case TokenTypes.LITERAL_INTERFACE:
                case TokenTypes.LITERAL_DO :
                case TokenTypes.LITERAL_SWITCH :
                case TokenTypes.LITERAL_STATIC :
                case TokenTypes.LITERAL_TRANSIENT :
                case TokenTypes.LITERAL_NATIVE :
                    //    case TokenTypes.LITERAL_threadsafe:
                case TokenTypes.LITERAL_SYNCHRONIZED :
                case TokenTypes.LITERAL_VOLATILE :
                case TokenTypes.LITERAL_TRY :
                case TokenTypes.LITERAL_CATCH :
                case TokenTypes.LITERAL_FINALLY :
                case TokenTypes.LABELED_STAT :
                case TokenTypes.LCURLY :
                case TokenTypes.RCURLY :
                case TokenTypes.SLIST :
                case TokenTypes.SEMI :
                case TokenTypes.COMMA :
                case TokenTypes.ARRAY_DECLARATOR :
                    break;

                default :
                //TODO: throw exception
                    if (mInitialized) {
                        final Log log =
                            mLogFactory.getInstance(this.getClass());
                        log.error(
                            "Unhandled expression type: "
                                + expression.getType());
                    }
                    break;
            }
        }
        catch (Exception e) {
            
            result = new UnknownClass(expression.getText(), expression);
//          TODO: This really should be logged
//            if (mInitialized) {
//                final Log log = mLogFactory.getInstance(this.getClass());
//                log.error("Error resolving near " + expression);
//            }
        }

        return result;
    }

    private IClass resolveTypecast(
        SymTabAST node,
        Scope location,
        IClass context,
        boolean referencePhase) {
        SymTabAST typeNode = (SymTabAST) node.getFirstChild();
        SymTabAST exprNode = (SymTabAST) typeNode.getNextSibling();
        //handle Checkstyle grammar
        if (exprNode.getType() == TokenTypes.RPAREN) {
            exprNode = (SymTabAST) exprNode.getNextSibling();
        }

        IClass type = null;

        final SymTabAST child = (SymTabAST) typeNode.getFirstChild();
        // TODO: Checkstyle change.
        // Do not create references from typecast.
        // Original transmogrify code is equivalent to
        // final boolean createReference = referencePhase;
        // which creates non-existant references for variables.
        final boolean createReference = false;
        if (child.getType()
            == TokenTypes.ARRAY_DECLARATOR) {
            type =
                new ArrayDef(
                    resolveType(
                        (SymTabAST) typeNode.getFirstChild(),
                        location,
                        context,
                        createReference));
        }
        else {
            type = resolveType(typeNode, location, context, createReference);
        }

        resolveExpression(exprNode, location, context, referencePhase);
        //TODO: Checkstyle change. Can this be ignored?
        if (type != null) {
            ((SymTabAST) typeNode.getFirstChild()).setDefinition(
                type,
                location,
                referencePhase);
        }

        return type;
    }

    private IClass resolveArrayAccess(
        SymTabAST node,
        Scope location,
        IClass context,
        boolean referencePhase) {

        SymTabAST arrayNode = (SymTabAST) (node.getFirstChild());
        SymTabAST exprNode = (SymTabAST) (arrayNode.getNextSibling());

        //resolve index expressions
        while (arrayNode.getType() == TokenTypes.INDEX_OP) {
            resolveExpression(exprNode, location, context, referencePhase);
            arrayNode = (SymTabAST) (arrayNode.getFirstChild());
            exprNode = (SymTabAST) (arrayNode.getNextSibling()); 
        }
        
        ArrayDef array =
            (ArrayDef) resolveExpression(arrayNode,
                location,
                context,
                referencePhase);

        resolveExpression(exprNode, location, context, referencePhase);

        return array.getType();
    }

    private IClass resolveLiteralClass() {
        return new ExternalClass(Class.class);
    }

    /**
     * Resolves any dotted reference, returning the <code>Scope</code>
     * identified by the reference.
     *
     * @param tree the root node of the dotted reference
     * @param location the <code>Scope</code> in which the expression occours.
     * @param context the <code>Scope</code> in which the search for the
     *                definition will start
     * @return the <code>Scope</code> indentified by the reference
     */
    private IClass resolveDottedName(
        SymTabAST tree,
        Scope location,
        IClass context,
        boolean referencePhase) {
        IClass result = null;

        IClass localContext = context;
        String name = null;

        DotIterator it = new DotIterator(tree);
        while (it.hasNext()) {
            SymTabAST node = it.nextNode();
            if (node.getType() != TokenTypes.COMMA) {
                localContext =
                    resolveExpression(
                        node,
                        location,
                        localContext,
                        referencePhase);
                if (localContext == null) {
                    node.setMeaningfulness(false);
                    name = node.getText();
                    while (localContext == null && it.hasNext()) {
                        SymTabAST next = it.nextNode();
                        name = name + "." + next.getText();
                        localContext = location.getClassDefinition(name);
                        if (localContext != null && referencePhase) {
                            next.setDefinition(
                                localContext,
                                location,
                                referencePhase);
                        }
                        else {
                            next.setMeaningfulness(false);
                        }
                    }
                }
            }
        }

        if (localContext != null) {
            result = localContext;
        }
        else {
            result = new UnknownClass(name, tree);
        }

        return result;
    }

    /**
     * Resolves a method call.
     *
     * @param methodNode the <code>SymTabAST</code> for the METHOD_CALL node
     * @param location the <code>Scope</code> where the expression occurs
     * @param context the <code>Scope</code> in which the expression occurs
     *                (where the search for a defintion begins)
     * @param referencePhase whether or not this is the reference phase of
     *                       table construction
     *
     * @return the <code>ClassDef</code> for the type returned by the method
     */
    private IClass resolveMethod(
        SymTabAST methodNode,
        Scope location,
        IClass context,
        boolean referencePhase) {
        IClass result = new UnknownClass(methodNode.getText(), methodNode);
        IClass newContext = null;

        if (context == null) {
            newContext = location.getEnclosingClass();
        }
        else {
            newContext = context;
        }

        String name = null;
        boolean createReference = true;

        SymTabAST nameNode = (SymTabAST) (methodNode.getFirstChild());
        SymTabAST parametersNode = (SymTabAST) (nameNode.getNextSibling());

        ISignature signature =
            resolveParameters(
                parametersNode,
                location,
                context,
                referencePhase);

        if (nameNode.getType() == TokenTypes.IDENT) {
            name = nameNode.getText();
        }
        else if (
            nameNode.getType() == TokenTypes.LITERAL_SUPER
                || (nameNode.getType() == TokenTypes.SUPER_CTOR_CALL)) {
            IClass superclass = location.getEnclosingClass().getSuperclass();
            newContext = superclass;
            name = superclass.getName();
            createReference = false;
        }
        else if (nameNode.getType() == TokenTypes.LITERAL_THIS) {
            newContext = location.getEnclosingClass();
            name = newContext.getName();
            createReference = false;
        }
        else {
            // REDTAG -- doing dotted name resolution on its own
            SymTabAST contextNode = (SymTabAST) (nameNode.getFirstChild());
            //TODO: handle Checkstyle grammar
            nameNode = (SymTabAST) contextNode.getNextSibling();
            //skip to IDENT
            while (nameNode.getType() != TokenTypes.IDENT) {
                nameNode = (SymTabAST) nameNode.getNextSibling();
            }
            
            name = nameNode.getText();
            newContext =
                resolveExpression(
                    contextNode,
                    location,
                    context,
                    referencePhase);
        }

        if (newContext != null) {
            IMethod method = newContext.getMethodDefinition(name, signature);

            if (method != null) {
                if (createReference && referencePhase) {
                    nameNode.setDefinition(method, location, referencePhase);
                }
                result = method.getType();
            }
        }

        if (result == null) {
            result = new UnknownClass(methodNode.getText(), methodNode);
        }

        return result;
    }

    /**
     * resolves a literal "this"
     *
     * @param expression the <code>SymTabAST</code> of the expression
     * @param location the <code>Scope</code> where the expression occurs
     * @param context the <code>Scope</code> in which the expression occurs
     *                (where the search for a defintion begins)
     *
     * @return the resulting scope of the expression (the type to which it evaluates)
     */
    private IClass resolveLiteralThis(
        SymTabAST thisNode,
        Scope location,
        IClass context) {
        return location.getEnclosingClass();
    }

    /**
     * resolves a literal "super"
     *
     * @param expression the <code>SymTabAST</code> of the expression
     * @param location the <code>Scope</code> where the expression occurs
     * @param context the <code>Scope</code> in which the expression occurs
     *                (where the search for a defintion begins)
     *
     * @return the resulting scope of the expression (the type to which it evaluates)
     */
    private IClass resolveLiteralSuper(
        SymTabAST superNode,
        Scope location,
        IClass context) {
        return location.getEnclosingClass().getSuperclass();
    }

    private boolean newIsConstructor(SymTabAST newNode) {
        boolean result = false;

        SymTabAST typeNode =
            (SymTabAST) (newNode.getFirstChild().getNextSibling());
        //handle Checkstyle grammar
        if (typeNode.getType() == TokenTypes.LPAREN) {
            typeNode = (SymTabAST) typeNode.getNextSibling();
        }
        if (typeNode.getType() == TokenTypes.ELIST) {
            result = true;
        }
        return result;

    }

    /**
     * resolves and expression of type TokenTypes.TYPE
     *
     * @param expression the <code>SymTabAST</code> of the expression
     * @param location the <code>Scope</code> where the expression occurs
     * @param context the <code>Scope</code> in which the expression occurs
     *                (where the search for a defintion begins)
     * @param referencePhase whether or not this is the reference phase of
     *                       table construction
     * @return the resulting scope of the expression (the type to which it evaluates)
     * @see #resolveDottedName(SymTabAST, Scope, IClass, boolean)
     * @see #resolveClassIdent(SymTabAST, Scope, IClass, boolean)
     */
    public IClass resolveType(
        SymTabAST expr,
        Scope location,
        IClass context,
        boolean referencePhase) {
        IClass result = null;
        SymTabAST nameNode = (SymTabAST) expr.getFirstChild();

        // TODO: Checkstyle change.
        // Do not create references from typecast.
        // Original transmogrify code is equivalent to
        // final boolean createReference = referencePhase;
        // which creates non-existant references for variables.
        final boolean createReference = false;
        if (nameNode.getType() == TokenTypes.DOT) {
            result =
                resolveDottedName(nameNode, location, context, createReference);
        }
        else {
            result =
                resolveClassIdent(nameNode, location, context, createReference);
        }

        return result;
    }

    /**
     * resolves Class type expression
     * @param expr node to be resolved
     * @param location scope of the <code>expr</code>
     * @param context context of the <code>expr</code> if exists
     * @param referencePhase <code>true</code> if this method is used to during
     *                                         finding reference phase
     *                       <code>false</code> otherwise
     * @return <code>IClass</code> representing the type to which the
     *         expression evalutes.
     * @see #resolveDottedName(SymTabAST, Scope, IClass, boolean)
     */
    public IClass resolveClass(
        SymTabAST expr,
        Scope location,
        IClass context,
        boolean referencePhase) {

        IClass result =
            resolveDottedName(expr, location, context, referencePhase);
        if (result != null && referencePhase) {
            expr.setDefinition(result, location, referencePhase);
        }

        return result;
    }

    /**
     * resolves expression with <code>JavaTokenTypes<code> other than <code>DOT</code>
     * @param expr expression to be resolved
     * @param location scope of the expression
     * @param context context of the expression if any
     * @param referencePhase <code>true</code> if this method is used to during
     *                                         finding reference phase
     *                       <code>false</code> otherwise
     * @return <code>IClass</code> representing the type to which the
     *         expression evalutes.
     */
    public IClass resolveClassIdent(
        SymTabAST expr,
        Scope location,
        IClass context,
        boolean referencePhase) {

        IClass result = location.getClassDefinition(expr.getText());
        if (result != null) {
            expr.setDefinition(result, location, referencePhase);
        }

        return result;
    }

    private IClass resolveNew(
        SymTabAST newNode,
        Scope location,
        IClass context,
        boolean referencePhase) {

        IClass result;

        if (newIsConstructor(newNode)) {
            result =
                resolveConstructor(newNode, location, context, referencePhase);
        }
        else {
            result =
                resolveNewArray(newNode, location, context, referencePhase);
        }

        return result;
    }

    private IClass resolveNewArray(
        SymTabAST newNode,
        Scope location,
        IClass context,
        boolean referencePhase) {
        IClass arrayType;

        SymTabAST typeNode = (SymTabAST) (newNode.getFirstChild());
        SymTabAST declaratorNode = (SymTabAST) (typeNode.getNextSibling());
        SymTabAST initializerNode =
            (SymTabAST) (declaratorNode.getNextSibling());

        arrayType = resolveClass(typeNode, location, context, referencePhase);

        if (declaratorNode.getFirstChild() != null) {
            resolveExpression(
                ((SymTabAST) declaratorNode.getFirstChild()),
                location,
                context,
                referencePhase);
        }

        if (initializerNode != null) {
            resolveArrayInitializer(
                initializerNode,
                location,
                context,
                referencePhase);
        }

        return new ArrayDef(arrayType);
    }

    private IClass resolveQuestion(
        SymTabAST question,
        Scope location,
        IClass context,
        boolean referencePhase) {
        SymTabAST test = (SymTabAST) question.getFirstChild();
        while (test.getType() == TokenTypes.LPAREN) {
            test = (SymTabAST) test.getNextSibling();
        }
        SymTabAST leftBranch = (SymTabAST) test.getNextSibling();
        while (leftBranch.getType() == TokenTypes.RPAREN) {
            leftBranch = (SymTabAST) leftBranch.getNextSibling();
        }
        SymTabAST rightBranch = (SymTabAST) leftBranch.getNextSibling();
        while (rightBranch.getType() != TokenTypes.COLON) {
            rightBranch = (SymTabAST) rightBranch.getNextSibling();
        }
        rightBranch = (SymTabAST) rightBranch.getNextSibling();

        resolveExpression(test, location, context, referencePhase);
        IClass leftClass =
            resolveExpression(leftBranch, location, context, referencePhase);
        IClass rightClass =
            resolveExpression(rightBranch, location, context, referencePhase);

        return moreGeneral(leftClass, rightClass);
    }

    private IClass moreGeneral(IClass a, IClass b) {
        return (a.isCompatibleWith(b)) ? b : a;
    }

    /**
     * Resolves a constructor call.
     *
     * @param tree the root node of the constructor call
     * @return the <code>ClassDef</code> for the class instantiated by the
     *         constructor
     */
    private IClass resolveConstructor(
        SymTabAST constructor,
        Scope location,
        IClass context,
        boolean referencePhase) {

        IClass classConstructed = null;

        SymTabAST nameNode = (SymTabAST) (constructor.getFirstChild());
        //SymTabAST parametersNode = (SymTabAST) (nameNode.getNextSibling());
        SymTabAST parametersNode =
            constructor.findFirstToken(TokenTypes.ELIST);
        SymTabAST nameIdent = null;
        if (nameNode.getType() == TokenTypes.IDENT) {
            nameIdent = nameNode;
        }
        else {
            nameIdent = (SymTabAST) nameNode.getFirstChild().getNextSibling();
        }

        classConstructed = resolveClass(nameNode, location, context, false);
        if (classConstructed != null) {
            MethodSignature signature =
                resolveParameters(
                    parametersNode,
                    location,
                    context,
                    referencePhase);

            IMethod constructorDef =
                classConstructed.getMethodDefinition(
                    nameIdent.getText(),
                    signature);

            if (constructorDef != null && referencePhase) {
                nameIdent.setDefinition(
                    constructorDef,
                    location,
                    referencePhase);
            }
        }

        return classConstructed;
    }

    /**
     * Resolves the types found in a method call. Any references found
     * in the process are created.  Returns a <code>MethodSignature</code> for
     * the types of the parameters.
     *
     * @param elist The <code>SymTabAST</code> for the list of parameters
     * @return the signature of the parameters
     */
    private MethodSignature resolveParameters(
        SymTabAST elist,
        Scope location,
        IClass context,
        boolean referencePhase) {
        Vector parameters = new Vector();

        SymTabAST expr = (SymTabAST) (elist.getFirstChild());
        while (expr != null) {
            if (expr.getType() != TokenTypes.COMMA) {
                IClass parameter =
                    resolveExpression((SymTabAST) (expr
				    .getFirstChild()),
				    location,
				    context,
				    referencePhase);
                parameters.add(parameter);
            }

            expr = (SymTabAST) (expr.getNextSibling());
        }

        return new MethodSignature(parameters);
    }

    /**
     * Resolves an IDENT node of an AST, creating the appropriate reference and
     * returning the scope of the identifer.
     *
     * @param ident the IDENT node
     * @param location the <code>Scope</code> in which the IDENT is found
     * @return the <code>Scope</code> the identifier identifies
     */
    private IClass resolveIdent(
        SymTabAST ident,
        Scope location,
        IClass context,
        boolean referencePhase) {

        IClass result = null;
        IDefinition def = null;
        String name = ident.getText();

        // look for var
        if (context != null) {
            def = context.getVariableDefinition(name);
        }
        else {
            def = location.getVariableDefinition(name);
        }

        if (def != null) {
            result = ((IVariable) def).getType();
        }
        else {
            // look for class
            if (context != null) {
                result = context.getClassDefinition(name);
            }
            else {
                result = location.getClassDefinition(name);
            }
            def = result;
        }

        if (def != null) {
            ident.setDefinition(def, location, referencePhase);
        }

        return result;
    }

    /**
     * Resolves a (binary) boolean expression.  The left and right sides of the
     * expression
     * are resolved in the process.
     *
     * @param expression the <code>SymTabAST</code> representing the boolean
     *                   expression.
     * @return the <code>Scope</code> for the boolean primitive type.
     */
    private IClass resolveBooleanExpression(
        SymTabAST expression,
        Scope location,
        IClass context,
        boolean referencePhase) {
        IClass result = null;

        SymTabAST leftChild = findLeftChild(expression);
        resolveExpression(leftChild, location, context, referencePhase);
        SymTabAST rightChild = findRightSibling(leftChild);

        resolveExpression(rightChild, location, context, referencePhase);

        result = LiteralResolver.getDefinition(TokenTypes.LITERAL_BOOLEAN);

        return result;
    }

    /**
     * resolves references in an assignment expression
     *
     * @param expression the <code>SymTabAST</code> of the expression
     * @param location the <code>Scope</code> where the expression occurs
     * @param context the <code>Scope</code> in which the expression occurs
     *                (where the search for a defintion begins)
     *
     * @return the resulting scope of the expression (the type to which it evaluates)
     */
    private IClass resolveAssignment(
        SymTabAST expression,
        Scope location,
        IClass context,
        boolean referencePhase) {
        IClass result = null;

        SymTabAST leftNode = (SymTabAST) (expression.getFirstChild());
        SymTabAST rightNode = (SymTabAST) (leftNode.getNextSibling());

        result = resolveExpression(leftNode, location, context, referencePhase);
        resolveExpression(rightNode, location, context, referencePhase);

        return result;
    }

    /**
     * Resolves a unary expression.  Returns the type of the expression,
     * creating any references found along the way.  Unary expressions are
     * increment (x++), decrement (x--), unary plus (+x), and unary minus (-x)
     *
     * @param expression the <code>SymTabAST</code> of the unary expression.
     * @return the <code>Scope</code> for the type to which the expression
     * evalutes.
     */
    private IClass resolveUnaryExpression(
        SymTabAST expression,
        Scope location,
        IClass context,
        boolean referencePhase) {
        SymTabAST operatee = (SymTabAST) (expression.getFirstChild());
        return resolveExpression(operatee, location, context, referencePhase);
    }

    /**
     * Resolves an arithmetic expression.  Returns the <code>Scope</code> for
     * the type to which the expression resolves.  Any references found during
     * resolution are created and resolved.
     *
     * @param expression the <code>SymTabAST</code> representing the arithmetic
     *                   expression.
     *
     * @return the <code>Scope</code> for the type to which the expression
     *         evaluates.
     */
    private IClass resolveArithmeticExpression(
        SymTabAST expression,
        Scope location,
        IClass context,
        boolean referencePhase) {
        IClass result = null;

        SymTabAST leftChild = findLeftChild(expression);
        
        IClass leftType =
            (resolveExpression(leftChild,
		    location,
		    context,
		    referencePhase));
                
        SymTabAST rightChild = findRightSibling(leftChild);

        IClass rightType =
                    (resolveExpression(rightChild,
		    location,
		    context,
		    referencePhase));

        result = binaryResultType(leftType, rightType);

        return result;
    }

    /**
     * Finds the left child of a binary operator, skipping parentheses.
     * @param aExpression the node for the binary operator.
     * @return the node for the left child.
     */  
    private SymTabAST findLeftChild(SymTabAST aExpression) {
        SymTabAST leftChild = (SymTabAST) (aExpression.getFirstChild());
        // handle Checkstyle grammar
        while (leftChild.getType() == TokenTypes.LPAREN) {
            leftChild = (SymTabAST) leftChild.getNextSibling();
        }
        return leftChild;
    }

    /**
     * Finds the right sibling of the left child of a binary operator,
     * skipping parentheses.
     * @param aLeftChild the left child of a binary operator.
     * @return the node of the right sibling.
     */   
    private SymTabAST findRightSibling(SymTabAST aLeftChild) {
        SymTabAST rightChild = (SymTabAST) (aLeftChild.getNextSibling());
        // handle Checkstyle grammar
        while ((rightChild != null)
            && (rightChild.getType() == TokenTypes.RPAREN))
        {
            rightChild = (SymTabAST) rightChild.getNextSibling();
        }
        return rightChild;
    }

    /**
     * Returns the <code>ClassDef</code> for the type to which arithmetic
     * expressions evaluate.
     *
     * @param a the <code>ClassDef</code> of the first operand.
     * @param b the <code>ClassDef</code> of the second operand.
     *
     * @return the <code>ClassDef</code> to which the expression evaluates.
     */
    private IClass binaryResultType(IClass a, IClass b) {

        IClass result = null;

        // These may or may not be in line with the rules set forth in the java
        // language specification.  Not being in line would be a BadThing(r).

        IClass string = new ExternalClass(java.lang.String.class);

        if (a.equals(string) || b.equals(string)) {
            result = string;
        }
        else if (a.equals(PrimitiveClasses.BOOLEAN)) {
            result = PrimitiveClasses.BOOLEAN;
        }
        else {
            result =
                PrimitiveClasses.binaryPromote(
                    a,
                    b);
        }

        return result;
    }

    /**
     * resolves references in an instanceof expression
     *
     * @param expression the <code>SymTabAST</code> of the expression
     * @param location the <code>Scope</code> where the expression occurs
     * @param context the <code>Scope</code> in which the expression occurs
     *                (where the search for a defintion begins)
     *
     * @return the resulting scope of the expression (the type to which it evaluates)
     */
    private IClass resolveInstanceOf(
        SymTabAST expression,
        Scope location,
        IClass context,
        boolean referencePhase) {
        SymTabAST leftNode = (SymTabAST) (expression.getFirstChild());
        SymTabAST rightNode = (SymTabAST) (leftNode.getNextSibling());

        resolveExpression(leftNode, location, context, referencePhase);

        SymTabAST classNameNode = (SymTabAST) (rightNode.getFirstChild());
        resolveClass(classNameNode, location, context, referencePhase);

        return LiteralResolver.getDefinition(TokenTypes.LITERAL_BOOLEAN);
    }

    /**
     * resolves references in a a break statement
     *
     * @param expression the <code>SymTabAST</code> for the expression
     * @param location the <code>Scope</code> where the expression occurs
     * @param context the <code>Scope</code> in which the expression occurs
     *                (where the search for a defintion begins)
     *
     * @return the <code>Scope</code> for the int primitive type
     */
    private IClass resolveGoto(
        SymTabAST expression,
        Scope location,
        IClass context,
        boolean referencePhase) {
        SymTabAST label = (SymTabAST) (expression.getFirstChild());
        // handle Checkstyle grammar
        if (label != null && (label.getType() != TokenTypes.SEMI)) {
            LabelDef def = location.getLabelDefinition(label.getText());
            if (def != null) {
                label.setDefinition(def, location, referencePhase);
            }
        }

        return null;
    }

    private IClass resolvePrimitiveType(
        SymTabAST primitive,
        Scope location,
        IClass context,
        boolean referencePhase) {
        IClass result =
            LiteralResolver.getDefinition(primitive.getType());

        primitive.setDefinition(result, location, referencePhase);
        return result;
    }

    /**
     * Returns the <code>ClassDef</code> of the int primitive type.  This may
     * need to be amended, based on the Java Language spec, to return a long
     * if the literal is larger than an int can hold.
     *
     * @param expression the <code>SymTabAST</code> for the integer literal
     * @param location the <code>Scope</code> where the expression occurs
     * @param context the <code>Scope</code> in which the expression occurs
     *                (where the search for a defintion begins)
     *
     * @return the <code>Scope</code> for the int primitive type
     */
    private IClass resolveNumInt(
        SymTabAST expression,
        Scope location,
        IClass context) {
        return PrimitiveClasses.INT;
    }

    /**
     * Returns the <code>ClassDef</code> type of the float primitive type.
     * This may need to be amended, based on the Java Language spec, to return
     * a double if the literal is larger than a float can hold.
     *
     * @param expression the <code>SymTabAST</code> for the floating point
      literal
     * @param location the <code>Scope</code> where the expression occurs
     * @param context the <code>Scope</code> in which the expression occurs
     *                (where the search for a defintion begins)
     *
     * @return the <code>Scope</code> for the float primitive type
     */
    private IClass resolveNumFloat(
        SymTabAST expression,
        Scope location,
        IClass context) {
        return PrimitiveClasses.DOUBLE;
    }

    /**
     * Returns the <code>ClassDef</code> type of a string literal
     *
     * @param expression the <code>SymTabAST</code> for a string literal
     * @param location the <code>Scope</code> where the expression occurs
     * @param context the <code>Scope</code> in which the expression occurs
     *                (where the search for a defintion begins)
     *
     * @return the <code>Scope</code> type of a string literal
     */
    private IClass resolveStringLiteral(
        SymTabAST expression,
        Scope location,
        IClass context) {
        return LiteralResolver.getDefinition(
            TokenTypes.STRING_LITERAL);
    }

    /**
     * Returns the <code>ClassDef</code> type of a character literal
     *
     * @param expression the <code>SymTabAST</code> for a string literal
     * @param location the <code>Scope</code> where the expression occurs
     * @param context the <code>Scope</code> in which the expression occurs
     *                (where the search for a defintion begins)
     *
     * @return the <code>Scope</code> type of a character literal
     */
    private IClass resolveCharLiteral(
        SymTabAST expression,
        Scope location,
        IClass context) {
        return LiteralResolver.getDefinition(
            TokenTypes.LITERAL_CHAR);
    }

    /**
     * Describe <code>resolveBooleanLiteral</code> method here.
     *
     * @param expression the <code>SymTabAST</code> of the expression
     * @param location the <code>Scope</code> where the expression occurs
     * @param context the <code>Scope</code> in which the expression occurs
     *                (where the search for a defintion begins)
     *
     * @return the <code>Scope</code> for the boolean primitive.
     */
    private IClass resolveBooleanLiteral(
        SymTabAST expression,
        Scope location,
        IClass context) {
        return LiteralResolver.getDefinition(TokenTypes.LITERAL_BOOLEAN);
    }

    private IClass resolveBooleanUnary(
        SymTabAST expression,
        Scope location,
        IClass context,
        boolean referencePhase) {
        SymTabAST child = (SymTabAST) expression.getFirstChild();
        resolveExpression(child, location, context, referencePhase);

        return LiteralResolver.getDefinition(TokenTypes.LITERAL_BOOLEAN);
    }

    /**
     * Resolves a constructor call.
     *
     * @param tree the root node of the constructor call
     * @return the <code>ClassDef</code> for the class instantiated by the
     *         constructor
     */
    private void resolveArrayInitializer(
        SymTabAST initializerNode,
        Scope location,
        IClass context,
        boolean referencePhase) {
        SymTabAST child = (SymTabAST) (initializerNode.getFirstChild());
        while (child != null) {
            if (child.getType() != TokenTypes.COMMA) {
                resolveExpression(child, location, context, referencePhase);
            }
            child = (SymTabAST) (child.getNextSibling());
        }
    }

    /**
     * Resolves a constructor call.
     *
     * @param tree the root node of the constructor call
     * @return the <code>ClassDef</code> for the class instantiated by the
     *         constructor
     */
    private void resolveThrowExpression(
        SymTabAST throwNode,
        Scope location,
        IClass context,
        boolean referencePhase) {

        SymTabAST nameNode = (SymTabAST) (throwNode.getFirstChild());
        resolveExpression(nameNode, location, context, referencePhase);
    }

    private IClass resolveShiftOperator(
        SymTabAST expression,
        Scope location,
        IClass context,
        boolean referencePhase) {
        IClass result = null;

        SymTabAST leftChild = findLeftChild(expression);
        SymTabAST rightChild = findRightSibling(leftChild);

        result =
            resolveExpression(leftChild, location, context, referencePhase);
        resolveExpression(rightChild, location, context, referencePhase);

        result = PrimitiveClasses.unaryPromote(result);

        return result;
    }

    private IClass resolveBitwiseNot(
        SymTabAST expression,
        Scope location,
        IClass context,
        boolean referencePhase) {
        IClass result = null;
        SymTabAST child = (SymTabAST) expression.getFirstChild();
        result = resolveExpression(child, location, context, referencePhase);

        result = PrimitiveClasses.unaryPromote(result);

        return result;
    }
}
