Package org.openquark.cal.internal.javamodel

Source Code of org.openquark.cal.internal.javamodel.JavaSourceGenerator

/*
* Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED
* 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 Business Objects 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.
*/


/*
* JavaSourceGenerator.java
* Creation date: Sep 11, 2003.
* By: Edward Lam
*/
package org.openquark.cal.internal.javamodel;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import org.openquark.cal.compiler.StringEncoder;
import org.openquark.cal.internal.javamodel.JavaExpression.ArrayAccess;
import org.openquark.cal.internal.javamodel.JavaExpression.ArrayCreationExpression;
import org.openquark.cal.internal.javamodel.JavaExpression.ArrayLength;
import org.openquark.cal.internal.javamodel.JavaExpression.Assignment;
import org.openquark.cal.internal.javamodel.JavaExpression.CastExpression;
import org.openquark.cal.internal.javamodel.JavaExpression.ClassInstanceCreationExpression;
import org.openquark.cal.internal.javamodel.JavaExpression.ClassLiteral;
import org.openquark.cal.internal.javamodel.JavaExpression.InstanceOf;
import org.openquark.cal.internal.javamodel.JavaExpression.LiteralWrapper;
import org.openquark.cal.internal.javamodel.JavaExpression.LocalName;
import org.openquark.cal.internal.javamodel.JavaExpression.LocalVariable;
import org.openquark.cal.internal.javamodel.JavaExpression.MethodInvocation;
import org.openquark.cal.internal.javamodel.JavaExpression.MethodVariable;
import org.openquark.cal.internal.javamodel.JavaExpression.OperatorExpression;
import org.openquark.cal.internal.javamodel.JavaExpression.PlaceHolder;
import org.openquark.cal.internal.javamodel.JavaExpression.JavaField.Instance;
import org.openquark.cal.internal.javamodel.JavaExpression.JavaField.Static;
import org.openquark.cal.internal.javamodel.JavaExpression.JavaField.This;
import org.openquark.cal.internal.javamodel.JavaExpression.OperatorExpression.Binary;
import org.openquark.cal.internal.javamodel.JavaExpression.OperatorExpression.Ternary;
import org.openquark.cal.internal.javamodel.JavaExpression.OperatorExpression.Unary;
import org.openquark.cal.internal.javamodel.JavaStatement.AssertStatement;
import org.openquark.cal.internal.javamodel.JavaStatement.Block;
import org.openquark.cal.internal.javamodel.JavaStatement.ExpressionStatement;
import org.openquark.cal.internal.javamodel.JavaStatement.IfThenElseStatement;
import org.openquark.cal.internal.javamodel.JavaStatement.JavaDocComment;
import org.openquark.cal.internal.javamodel.JavaStatement.LabelledContinue;
import org.openquark.cal.internal.javamodel.JavaStatement.LineComment;
import org.openquark.cal.internal.javamodel.JavaStatement.LocalVariableDeclaration;
import org.openquark.cal.internal.javamodel.JavaStatement.MultiLineComment;
import org.openquark.cal.internal.javamodel.JavaStatement.ReturnStatement;
import org.openquark.cal.internal.javamodel.JavaStatement.SwitchStatement;
import org.openquark.cal.internal.javamodel.JavaStatement.SynchronizedMethodInvocation;
import org.openquark.cal.internal.javamodel.JavaStatement.ThrowStatement;
import org.openquark.cal.internal.javamodel.JavaStatement.UnconditionalLoop;
import org.openquark.cal.internal.javamodel.JavaStatement.SwitchStatement.IntCaseGroup;
import org.openquark.cal.util.ArrayStack;


/**
* A class to generate Java source code given an object representation.
* @author Edward Lam
*/
public class JavaSourceGenerator {

    /** The length of an indent. */
    private static final int INDENT_LENGTH = 4;

    /** Line separator. */
    private static final String EOL = System.getProperty("line.separator");

    /**
     * Constructor for a JavaSourceGenerator
     */
    JavaSourceGenerator() {
    }


    /**
     * Holds class names used, etc.
     * TODOEL: manage namespace collisions.
     * @author Edward Lam
     */
    static class GenerationContext {

        private final JavaTypeName classTypeName;

        private Map<String, JavaTypeName> unqualifiedNameToTypeNameMap = new TreeMap<String, JavaTypeName>();       // TODOEL: resolve conflicts.

        /**
         * Constructor for a GenerationContext
         * @param classTypeName
         */
        GenerationContext(JavaTypeName classTypeName) {
            this.classTypeName = classTypeName;
        }

        /**
         * Get the name that can be used in java source for a given type (class).
         * @param typeName
         * @return String
         */
        String getSourceName(JavaTypeName typeName) {
            // We can declare an import and return the short class name if:
            // 1) There isn't another class with the same name imported from another module.
            // 2) The imported class doesn't have the same name as the class we are creating.


            switch (typeName.getTag()) {

                case JavaTypeName.VOID_TAG:
                case JavaTypeName.BOOLEAN_TAG:
                case JavaTypeName.BYTE_TAG:
                case JavaTypeName.SHORT_TAG:
                case JavaTypeName.CHAR_TAG:
                case JavaTypeName.INT_TAG:
                case JavaTypeName.LONG_TAG:
                case JavaTypeName.DOUBLE_TAG:
                case JavaTypeName.FLOAT_TAG:
                    return typeName.getName();

                case JavaTypeName.OBJECT_TAG:
                {
                    JavaTypeName.Reference.Object referenceType = (JavaTypeName.Reference.Object)typeName;

                    // Same type name as this class.
                    if (typeName.equals(classTypeName)) {
                        return referenceType.getBaseName();
                    }

                    // if this type is in java.lang we will need to fully qualify it.
                    // This prevents conflicts with other classes with the same name
                    // ex. mypackage.String
                    if (typeName.getPackageName().equals("java.lang")) {
                        return referenceType.getFullJavaSourceName();
                    }

                    JavaTypeName.Reference.Object classType = (JavaTypeName.Reference.Object)classTypeName;
                    String importName = referenceType.getImportName();

                    // Same enclosing class (ie. for nested classes..).
                    if (importName.equals(classType.getImportName())) {
                        return referenceType.getBaseName();
                    }

                    String objectPackageName = referenceType.getPackageName();
                    String unqualifiedName = referenceType.getUnqualifiedJavaSourceName();

                    // Same unqualified name as this class, different module.
                    if (unqualifiedName.equals(classType.getUnqualifiedJavaSourceName())) {
                        return referenceType.getFullJavaSourceName();
                    }

                    // Inner class, where outer class has same name as this class.
                    String tempName = unqualifiedName;
                    int index = tempName.indexOf (".");
                    while (index >= 0) {
                        String tempUnqualifiedName = tempName.substring(0, index);
                        // Same unqualified name as this class, different module.
                        if (tempUnqualifiedName.equals(classType.getUnqualifiedJavaSourceName())) {
                            return referenceType.getFullJavaSourceName();
                        }
                        tempName = tempName.substring(index + 1);
                        index = tempName.indexOf (".");
                    }

                    // We want to key the unqualified name to typename map off of the containing class name
                    // in cases where we are dealing with an inner class.
                    int dotIndex = unqualifiedName.indexOf (".");
                    String lookupName = (dotIndex != -1) ? unqualifiedName.substring(0, dotIndex): unqualifiedName;
                    JavaTypeName existingTypeName = unqualifiedNameToTypeNameMap.get(lookupName);
                    if (existingTypeName == null) {
                        unqualifiedNameToTypeNameMap.put(lookupName, typeName);
                        return referenceType.getUnqualifiedJavaSourceName();
                    }

                    if (objectPackageName.equals(existingTypeName.getPackageName())) {
                        // There is already a match for this type, or the containing type if this is
                        // an inner class, simply return the unqualifiedName
                        return unqualifiedName;

                    }

                    // There is a match on the unqualifiedName but it is not the same type.
                    return referenceType.getFullJavaSourceName();
                }

                case JavaTypeName.ARRAY_TAG:
                {

                    JavaTypeName.Reference.Array arrayType = (JavaTypeName.Reference.Array)typeName;
                    int nDimensions = arrayType.getNDimensions();
                    JavaTypeName elementType = arrayType.getElementType();

                    // Get the source for the element type, and append "[]" for each dimension.
                    StringBuilder sb = new StringBuilder(getSourceName(elementType));
                    for (int i = 0; i < nDimensions; i++) {
                        sb.append("[]");
                    }

                    return sb.toString();

                }

                default:
                {
                    throw new NullPointerException("Unrecognized class: " + typeName.getName());
                }
            }

        }

        /**
         * Get the type names for which unqualified names were output in source.
         * @return (List of JavaTypeName)
         */
        List<JavaTypeName> getTypeNames() {
            return new ArrayList<JavaTypeName>(unqualifiedNameToTypeNameMap.values());
        }
    }

    /**
     * Emit import statements.
     * @param leccPackageName
     * @param topLevelClassName
     * @param context
     * @return String
     */
    private static String genS_Imports(String leccPackageName, String topLevelClassName, GenerationContext context) {

        // Get the sorted set of import names.
        Set<String> importNameSet = new TreeSet<String>();

        for (final JavaTypeName typeName : context.getTypeNames()) {
            String packageName = typeName.getPackageName();
            if (packageName.equals("") || packageName.equals("java.lang") || packageName.equals(leccPackageName)) {
                continue;
            }

            // Check to see if the class is an inner class of the current class.
            if (packageName.equals(topLevelClassName)) {
                continue;
            }
            importNameSet.add(((JavaTypeName.Reference.Object)typeName).getImportName());
        }

        // Emit the imports.
        StringBuilder sb = new StringBuilder();
        for (final String importName : importNameSet) {
            emitLine(sb, 0, "import " + importName + ";");
        }

        return sb.toString();
    }

    /**
     * Get the Java source code for the given top-level class.
     * @param classRep the representation for the top-level class.
     * @return the java source
     * @throws JavaGenerationException
     */
    public static String generateSourceCode(JavaClassRep classRep) throws JavaGenerationException {

        StringBuilder sb = new StringBuilder();

        JavaTypeName classTypeName = classRep.getClassName();
        String packageName = classTypeName.getPackageName();

        // Create a generation context..
        GenerationContext context = new GenerationContext(classTypeName);

        // Emit the class comemnt if any.
        if (classRep.getComment() != null) {
            sb.append(getSource(classRep.getComment(), context, 0));
            emitLine(sb);
        }

        // Emit SC package and banner
        emitLine(sb, 0, "package " + packageName + ";");
        emitLine(sb);

        // Next we need to walk the java model building up the import information.
        ImportGenerator ig = new ImportGenerator(context);
        classRep.accept(ig, null);

        // Emit imports and class comment.
        String imports = genS_Imports(packageName, classTypeName.getFullJavaSourceName(), context);
        sb.append(imports);
        emitLine(sb);

        sb.append(getSource(classRep, context, 0));

        return sb.toString();
    }

    /**
     * Get a String consisting of the opening line for a Java declaration for the given Java class rep.
     * eg. "public static class Foo extends Bar implements Baz {"
     *
     * @param classRep the Java class representation
     * @param context the generation context
     * @return the opening line of the Java class, ending in an open paren.
     */
    private static String getClassDeclaration(JavaClassRep classRep, GenerationContext context) {

        String unqualifiedClassName = ((JavaTypeName.Reference.Object)classRep.getClassName()).getBaseName();

        StringBuilder classDeclaration = new StringBuilder(Modifier.toString(classRep.getModifiers()));
        classDeclaration.append(" class " + unqualifiedClassName + " ");

        JavaTypeName superclassName = classRep.getSuperclassName();
        if (!superclassName.equals(JavaTypeName.OBJECT)) {
            classDeclaration.append("extends " + context.getSourceName(superclassName) + " ");
        }

        final int nInterfaces = classRep.getNInterfaces();
        if (nInterfaces > 0) {
            classDeclaration.append("implements ");
            for (int i = 0; i < nInterfaces; i++) {
                if (i > 0) {
                    classDeclaration.append(", ");
                }
                classDeclaration.append(context.getSourceName(classRep.getInterface(i)));
            }
            classDeclaration.append(" ");
        }

        classDeclaration.append("{");

        return classDeclaration.toString();
    }

    /**
     * Get a String consisting of the opening line for a Java declaration for the given Java constructor.
     * eg. "public static Bar() {"
     *
     * @param javaConstructor
     * @param context the generation context
     * @return the opening line of the Java constructor, ending in an open paren.
     */
    private static String getConstructorDeclaration(JavaConstructor javaConstructor, GenerationContext context) {

        // Initialize with access flags.
        StringBuilder constructorDeclaration = new StringBuilder(Modifier.toString(javaConstructor.getModifiers())).append(' ');

        // Method name
        constructorDeclaration.append(javaConstructor.getConstructorName() + "(");

        // Parameters
        final int nParams = javaConstructor.getNParams();
        for (int i = 0; i < nParams; i++) {
            if (i > 0) {
                constructorDeclaration.append(", ");
            }
            constructorDeclaration.append(context.getSourceName(javaConstructor.getParamType(i)) + " " + javaConstructor.getParamName(i));
        }

        // Close paren, open brace
        constructorDeclaration.append(") {");

        return constructorDeclaration.toString();
    }

    /**
     * Get a String consisting of the opening line for a Java declaration for the given Java method.
     * eg. "public static void foo throws Bar {"
     *
     * @param javaMethod the Java method representation
     * @param context the generation context
     * @return the opening line of the Java method, ending in an open paren.
     */
    private static String getMethodDeclaration(JavaMethod javaMethod, GenerationContext context) {

        // Initialize with access flags.
        StringBuilder methodDeclaration = new StringBuilder(Modifier.toString(javaMethod.getModifiers())).append(' ');

        // Return type
        methodDeclaration.append(context.getSourceName(javaMethod.getReturnType()) + " ");

        // Method name
        methodDeclaration.append(javaMethod.getMethodName() + "(");

        // Parameters
        for (int i = 0, nParams = javaMethod.getNParams(); i < nParams; i++) {
            if (i > 0) {
                methodDeclaration.append(", ");
            }

            if (javaMethod.isParamFinal(i)) {
                methodDeclaration.append("final ");
            }

            methodDeclaration.append(context.getSourceName(javaMethod.getParamType(i))).append(" ").append(javaMethod.getParamName(i));
        }

        // Close paren
        methodDeclaration.append(") ");

        // throws
        final int nThrownExceptions = javaMethod.getNThrownExceptions();
        if (nThrownExceptions > 0) {
            methodDeclaration.append("throws ");

            for (int i = 0; i < nThrownExceptions; ++i) {
                JavaTypeName thrownExceptionName = javaMethod.getThrownException(i);
                methodDeclaration.append(context.getSourceName(thrownExceptionName));

                if (i < nThrownExceptions - 1) {
                    methodDeclaration.append(",");
                }
                methodDeclaration.append(" ");
            }
        }

        // Open brace
        methodDeclaration.append("{");

        return methodDeclaration.toString();
    }

    /**
     * Get the Java source for a given java class representation.
     * @param classRep the java class representation
     * @param context the generation context
     * @param indent the indent level of the source
     * @return the java source
     * @throws JavaGenerationException
     */
    private static String getSource(JavaClassRep classRep, GenerationContext context, int indent) throws JavaGenerationException{
        StringBuilder sb = new StringBuilder();

        // Emit class JavaDoc
        if (classRep.getJavaDoc() != null) {
            sb.append(getSource(classRep.getJavaDoc(), context, indent).toString());
        }

        // Emit class declaration
        {
            emitLine(sb, indent, getClassDeclaration(classRep, context));
        }

        Iterator<Object> elements = classRep.getClassContent().iterator();
        while (elements.hasNext()) {
            Object element = elements.next();

            if (element instanceof JavaFieldDeclaration) {
                // Emit a field declaration.
                JavaFieldDeclaration fieldDeclaration = (JavaFieldDeclaration)element;
                sb.append(getSource(fieldDeclaration, context, indent+1));
            } else
            if (element instanceof JavaConstructor) {
                // Emit constructor
                JavaConstructor javaConstructor = (JavaConstructor)element;
                sb.append(getSource(javaConstructor, context, indent + 1));
                // extra EOL
                emitLine(sb);
            } else
            if (element instanceof JavaMethod) {
                // Emit method
                JavaMethod javaMethod = (JavaMethod)element;
                sb.append(getSource(javaMethod, context, indent + 1));

                // extra EOL
                emitLine(sb);
            } else
            if (element instanceof JavaClassRep) {
                // Emit inner classe
                JavaClassRep innerClassRep = (JavaClassRep)element;
                sb.append(getSource(innerClassRep, context, indent + 1));

                // extra EOL
            } else
            if (element instanceof MultiLineComment) {
                sb.append(getSource((MultiLineComment)element, context, indent + 1));
                emitLine(sb);
            } else {
                throw new JavaGenerationException ("Unhandled top level element in JavaClassRep: " + element.getClass().getName());
            }
        }

        // Close paren.
        {
            emitLine(sb, indent, "}");
        }

        return sb.toString();
    }

    /**
     * Get the Java source for a given java field declaration.
     * @param fieldDeclaration the java field declaration
     * @param context the generation context
     * @param indent the indent level of the source
     * @return String
     * @throws JavaGenerationException
     */
    private static String getSource(JavaFieldDeclaration fieldDeclaration, GenerationContext context, int indent) throws JavaGenerationException {
        StringBuilder block = new StringBuilder();

        JavaDocComment jdc = fieldDeclaration.getJavaDoc();
        if (jdc != null) {
            block.append(getSource(jdc, context, indent));
        }

        String fieldName = fieldDeclaration.getFieldName();
        JavaTypeName fieldType = fieldDeclaration.getFieldType();
        JavaExpression initializer = fieldDeclaration.getInitializer();

        // flags, type, name

        final StringBuilder declaration;
        {
            final String modifiers = Modifier.toString(fieldDeclaration.getModifiers());
            if (modifiers.length() > 0) {
                declaration = new StringBuilder(modifiers).append(' ');
            } else {
                declaration = new StringBuilder();
            }
        }

        declaration.append(context.getSourceName(fieldType)).append(' ').append(fieldName);

        // " = " , initializer.
        if (initializer != null) {
            declaration.append(" = ");
            declaration.append(getSource(initializer, indent, declaration.length(), context));
        }

        declaration.append(";");

        emitLine(block, indent, declaration.toString());
        emitLine(block);

        return block.toString();
    }

    /**
     * Get the Java source for a given java constructor.
     * @param javaConstructor the Java constructor representation
     * @param context the generation context
     * @param indent the indent level of the source
     * @return String
     * @throws JavaGenerationException
     */
    private static String getSource(JavaConstructor javaConstructor, GenerationContext context, int indent) throws JavaGenerationException {

        StringBuilder sb = new StringBuilder();

        emitLine(sb, indent, getConstructorDeclaration(javaConstructor, context));

        if (javaConstructor.getSuperConstructorParamValues().length > 0) {
            StringBuilder superCall = new StringBuilder();
            superCall.append("super (");
            for (int i = 0, n = javaConstructor.getSuperConstructorParamValues().length; i < n; ++i) {
                superCall.append(getSource(javaConstructor.getSuperConstructorParamValues()[i], indent + 1, superCall.length(), context));
                if (i < (n-1)) {
                    superCall.append(", ");
                }
            }
            superCall.append(");");
            emitLine(sb, indent+1, superCall.toString());
        }
        sb.append(getSource(javaConstructor.getBodyCode(), context, indent + 1));
        emitLine(sb, indent, "}");

        return sb.toString();
    }

    /**
     * Get the Java source for a given java method.
     * @param javaMethod the Java method representation
     * @param context the generation context
     * @param indent the indent level of the source
     * @return String
     * @throws JavaGenerationException
     */
    private static String getSource(JavaMethod javaMethod, GenerationContext context, int indent) throws JavaGenerationException {

        StringBuilder sb = new StringBuilder();

        if (javaMethod.getJavaDocComment() != null) {
            sb.append(getSource(javaMethod.getJavaDocComment(), context, indent));
        }

        emitLine(sb, indent, getMethodDeclaration(javaMethod, context));
        sb.append(getSource(javaMethod.getBodyCode(), context, indent + 1));
        emitLine(sb, indent, "}");

        return sb.toString();
    }

    /**
     * Get the Java source for a given java statement.
     * @param statement the java statement for which to generate source.
     * @param context the generation context.
     * @param indent the indent level of the source
     * @return StringBuilder
     * @throws JavaGenerationException
     */
    private static StringBuilder getSource(JavaStatement statement, GenerationContext context, int indent) throws JavaGenerationException {
        StringBuilder sb = new StringBuilder();

        if (statement instanceof LocalVariableDeclaration) {
            LocalVariableDeclaration declaration = (LocalVariableDeclaration)statement;
            LocalVariable localVariable = declaration.getLocalVariable();

            emitIndent(sb, indent);

            // final
            if (declaration.isFinal()) {
                sb.append("final ");
            }

            // variable type and name
            sb.append(context.getSourceName(localVariable.getTypeName()) + " " + localVariable.getName());

            // " = " + initializer
            JavaExpression initializer = declaration.getInitializer();
            if (initializer != null) {
                sb.append(" = ");
                sb.append(getSource(initializer, indent, sb.length(), context));
            }

            // ;
            sb.append(";" + EOL);


        } else if (statement instanceof ExpressionStatement) {
            ExpressionStatement expressionStatement = (ExpressionStatement)statement;
            String expressionSource = getSource(expressionStatement.getJavaExpression(), indent, 0, context);
            emitLine(sb, 0, expressionSource + ";");

        } else if (statement instanceof LineComment) {
            LineComment lineComment = (LineComment)statement;
            emitLine(sb, indent, "// " + lineComment.getCommentText());

        } else if (statement instanceof MultiLineComment) {
            boolean isJavaDoc = (statement instanceof JavaDocComment);
            MultiLineComment jdc = (MultiLineComment)statement;

            Iterator<String> it = jdc.getCommentLines().iterator();
            String firstLine = it.next();
            boolean nd = !firstLine.startsWith("/*");
            if (nd) {
                emitLine(sb, indent, isJavaDoc ? "/**" : "/*");
            }
            it = jdc.getCommentLines().iterator();
            while (it.hasNext()) {
                String line = it.next();
                if (nd) {
                    emitLine(sb, indent, " * " + line);
                } else {
                    emitLine(sb, indent, line);
                }
            }
            if (nd) {
                emitLine(sb, indent, " */");
            }

        } else if (statement instanceof ReturnStatement) {
            ReturnStatement returnStatement = (ReturnStatement)statement;
            if (returnStatement.getReturnExpression() == null) {
                emitLine(sb, indent, "return;");
            } else {
                emitLine(sb, indent, "return " + getSource(returnStatement.getReturnExpression(),indent, 7, context) ";");
            }
        } else if (statement instanceof IfThenElseStatement) {
            IfThenElseStatement iteStatement = (IfThenElseStatement)statement;

            emitLine(sb, indent, "if (" + getSource(iteStatement.getCondition(), indent, 4, context) + ") {");

            sb.append(getSource(iteStatement.getThenStatement(), context, indent + 1));

            // Emit the else clause only if there is one.
            StringBuilder elseClause = getSource(iteStatement.getElseStatement(), context, indent + 1);

            if (elseClause.length() > 0) {
                emitLine(sb, indent, "} else {");
                sb.append(elseClause);
            }

            emitLine(sb, indent, "}");

        } else if (statement instanceof SwitchStatement) {
            SwitchStatement switchStatement = (SwitchStatement)statement;

            JavaExpression condition = switchStatement.getCondition();
            List<IntCaseGroup> caseGroups = switchStatement.getCaseGroups();
            JavaStatement defaultStatementGroup = switchStatement.getDefaultStatement();

            // Sort cases by the first case label in each group.
            Collections.sort(caseGroups, new Comparator<IntCaseGroup>() {

                public int compare(IntCaseGroup o1, IntCaseGroup o2) {
                    int int1 = o1.getNthCaseLabel(0);
                    int int2 = o2.getNthCaseLabel(0);
                    if (int1 < int2) {
                        return -1;
                    }
                    if (int1 > int2) {
                        return 1;
                    }
                    return 0;
                }
            });

            emitIndent (sb, indent);
            int length = sb.length();

            sb.append("switch (");
            String conditionString = getSource(condition, 0, 0, context, true);
            conditionString = conditionString.replaceAll(EOL," ");
            sb.append(conditionString);
            if (sb.lastIndexOf(EOL, length) >= 0) {
                emitLine(sb);
                emitIndent(sb, indent);
                for (int i = 0; i < 7; ++i) {
                    sb.append(" ");
                }
                sb.append (") {");
                emitLine (sb);
            } else {
                sb.append(") {");
                emitLine(sb);
            }

            emitLine(sb);

            // case labels and their statement groups.
            for (final IntCaseGroup switchCaseGroup : caseGroups) {

                JavaStatement caseStatementGroup = switchCaseGroup.getStatement();

                int nCaseLabels = switchCaseGroup.getNCaseLabels();
                for (int i = 0; i < nCaseLabels - 1; i++) {
                    emitLine(sb, indent + 1, "case " + switchCaseGroup.getNthCaseLabel(i) + ":");
                }
                emitLine(sb, indent + 1, "case " + switchCaseGroup.getNthCaseLabel(nCaseLabels - 1) + ": {");

                sb.append(getSource(caseStatementGroup, context, indent + 2));
                emitLine(sb, indent + 1, "}");
                emitLine(sb);
            }

            // default label and statement group.
            if (defaultStatementGroup != null) {
                emitLine(sb, indent + 1, "default: {");
                sb.append(getSource(defaultStatementGroup, context, indent + 2));
                emitLine(sb, indent + 1, "}");
            }

            emitLine(sb, indent, "}");

        } else if (statement instanceof Block) {
            Block block = (Block)statement;

            int blockIndent = indent;
            List<JavaExceptionHandler> exceptionHandlers = block.getExceptionHandlers();
            if (!exceptionHandlers.isEmpty()) {
                emitLine(sb, indent, "try {");
                blockIndent++;
            }

            boolean doingLocalVarDeclarations = false;
            boolean wasLineComment = false;
            int nStatements = block.getNBlockStatements();
            for (int i = 0; i < nStatements; i++) {
                JavaStatement blockStatement = block.getNthBlockStatement(i);

                // Try to separate local var declarations from other types of block statements.
                boolean isLocalVarDeclaration = blockStatement instanceof LocalVariableDeclaration;
                if (!wasLineComment && isLocalVarDeclaration != doingLocalVarDeclarations && i > 0) {
                    sb.append(EOL);
                }
                doingLocalVarDeclarations = isLocalVarDeclaration;
                wasLineComment = blockStatement instanceof LineComment;

                // Now append the source.
                sb.append(getSource(blockStatement, context, blockIndent));
            }

            if (!exceptionHandlers.isEmpty()) {
                for (final JavaExceptionHandler eh : exceptionHandlers) {
                    emitLine(sb, indent, "} catch (" + fixupClassName(eh.getExceptionClass().getName()) + " " + eh.getExceptionVarName() + ") {");
                    sb.append(getSource(eh.getHandlerCode(), context, blockIndent));
                }
                emitLine(sb, indent, "}");

            }

        } else
        if (statement instanceof ThrowStatement) {
            ThrowStatement throwStatement = (ThrowStatement)statement;
            emitLine(sb, indent, "throw " + getSource(throwStatement.getThrownExpression(), indent, 6, context) + ";");
        } else
        if (statement instanceof UnconditionalLoop) {
            UnconditionalLoop whileStatement = (UnconditionalLoop)statement;
            emitLine (sb, indent, whileStatement.getLabel() + ": while (true) {");
            sb.append(getSource(whileStatement.getBody(), context, indent + 1));
            emitLine (sb, indent, "}");
        } else
        if (statement instanceof LabelledContinue) {
            LabelledContinue lc = (LabelledContinue)statement;
            emitLine (sb, indent, "continue " + lc.getLabel() + ";");
        } else
        if (statement instanceof SynchronizedMethodInvocation){
            // A method invocation wrapped in a synchronization block.
            SynchronizedMethodInvocation sof = (SynchronizedMethodInvocation)statement;

            // Start the synchronized block.
            emitLine (sb, indent, "synchronized (" + getSource(sof.getSynchronizingObject(), indent, 14, context) + ") {");

            // Add the method invocation.
            sb.append (getSource(new ExpressionStatement(sof.getMethodInvocation()), context, indent + 1));

            // Finish the block.
            emitLine (sb, indent, "}");
        } else
        if (statement instanceof AssertStatement) {
            AssertStatement ast = (AssertStatement)statement;
            JavaExpression conditionExpression = ast.getConditionExpr();
            JavaExpression failureExpression = ast.getOnFailureExpr();

            if (failureExpression != null) {
                emitIndent(sb, indent);
                sb.append("assert (");
                sb.append(getSource(conditionExpression, indent, 8, context));
                sb.append(") : (");
                sb.append(getSource(failureExpression, indent+1, 5, context));
                sb.append(");");
                emitLine(sb);
            } else {
                emitLine (sb, indent, "assert (" + getSource(conditionExpression, indent, 8, context) + ");");
            }

        } else {
            throw new JavaGenerationException("Unrecognized statement type: " + statement.getClass());
        }
        return sb;
    }


    /**
     * Get the Java source for a given java expression.
     * @param expression the java expression
     * @param context the generation context
     * @param indentLevel
     * @param startOffset
     * @return String
     */
    private static String getSource(JavaExpression expression, int indentLevel, int startOffset, GenerationContext context) {
        return getSource (expression, indentLevel, startOffset, context, false);
    }

    private static String getSource(JavaExpression expression, int indentLevel, int startOffset, GenerationContext context, boolean singleLine) {

        ExpressionTextGenerator etg = new ExpressionTextGenerator(context);
        expression.accept(etg, null);
        ExpressionTextNode rootNode = etg.getCurrentNode().getNthChild(0);
        StringBuilder sb = new StringBuilder();
        if (!singleLine) {
            rootNode.formatExpressionText(sb, indentLevel, startOffset);
        } else {
            rootNode.appendExpressionText(sb);
        }

        return sb.toString();

    }

    /**
     * Emit an indent.
     * @param sb the StringBuilder to which to add an indent.
     * @param indent the number of indents to add.
     * @return StringBuilder sb, returned for convenience.
     */
    private static StringBuilder emitIndent(StringBuilder sb, int indent) {
        // NOTE: we use the tab character '\t' instead of adding spaces
        // because of memory issues.
        // When using spaces instead of '\t' some of our generated java source
        // files were large enough to cause out-of-memory errors.
        for (int i = 0; i < indent; i++) {
            sb.append('\t');
        }
        return sb;
    }

    /**
     * Emit an empty line.
     * @param sb the StringBuilder to which to add the empty line.
     */
    private static void emitLine(StringBuilder sb) {
        sb.append(EOL);
    }

    /**
     * Emit an indent, some text, and an EOL.
     * @param sb the StringBuilder to which to add an indent.
     * @param text the text to add.
     * @param indent the number of indents to add.
     */
    private static void emitLine(StringBuilder sb, int indent,String text) {
        emitIndent(sb, indent);
        sb.append(text + EOL);
    }

    /**
     * A textual representation of a JavaClassRep to be used for debugging purposes only.
     * @param classRep
     * @return String
     */
    static String toDebugString(JavaClassRep classRep) {

        try {
            return generateSourceCode(classRep);
        } catch (JavaGenerationException e) {
            JavaTypeName classTypeName = classRep.getClassName();
            return "JavaGenerationException generating source for JavaClassRep: " + classTypeName + ".";
        }
    }

    /**
     * A textual representation of a JavaMethod to be used for debugging purposes only.
     * With respect to use of qualified symbols, the method is treated as being defined in the java.lang.Object class.
     * @param javaMethod
     * @return String
     */
    static String toDebugString(JavaMethod javaMethod) {

        try {
            return getSource(javaMethod, new GenerationContext(JavaTypeName.OBJECT), 0);
        } catch (JavaGenerationException cge) {
            return "JavaGenerationException generating source for JavaMethod: " + javaMethod.getMethodName() + ".";
        }
    }

    /**
     * A textual representation of a JavaConstructor to be used for debugging purposes only.
     * With respect to use of qualified symbols, the constructor is treated as being defined in the java.lang.Object class.
     * @param javaConstructor
     * @return String
     */
    static String toDebugString(JavaConstructor javaConstructor) {

        try {
            return getSource(javaConstructor, new GenerationContext(JavaTypeName.OBJECT), 0);
        } catch (JavaGenerationException cge) {
            return "JavaGenerationException generating source for JavaConstructor: " + javaConstructor.getConstructorName() + ".";
        }
    }

    /**
     * A textual representation of a JavaStatement to be used for debugging purposes only.
     * With respect to use of qualified symbols, the statement is treated as being defined in the java.lang.Object class.
     * @param javaStatement
     * @return String
     */
    static String toDebugString(JavaStatement javaStatement) {

        try {
            return getSource(javaStatement, new GenerationContext(JavaTypeName.OBJECT), 0).toString();
        } catch (JavaGenerationException cge) {
            return "JavaGenerationException generating source for JavaStatement: " + javaStatement.getClass() + ".";
        }
    }

    /**
     * A textual representation of a JavaExpression to be used for debugging purposes only.
     * With respect to use of qualified symbols, the expression is treated as being defined in the java.lang.Object class.
     * @param javaExpression
     * @return String
     */
    static String toDebugString(JavaExpression javaExpression) {

        return getSource(javaExpression, 0, 0, new GenerationContext(JavaTypeName.OBJECT));
    }

    /**
     * A textual representation of a JavaFieldDeclaration to be used for debugging purposes only.
     * With respect to use of qualified symbols, the field declaration is treated as being defined in the java.lang.Object class.
     * @param javaFieldDeclaration
     * @return String
     */
    static String toDebugString(JavaFieldDeclaration javaFieldDeclaration) {

        try {
            return getSource(javaFieldDeclaration, new GenerationContext(JavaTypeName.OBJECT), 0);
        } catch (JavaGenerationException cge) {
            return "JavaGenerationException generating source for JavaFieldDeclaration: " + javaFieldDeclaration.getClass() + ".";
        }
    }

    /**
     * Return the name of the class, in a form that can be used in source code.
     * eg. [[B ==> byte[][].
     *     CALExecutor$ForeignFunctionException ==> CALExecutor.ForiegnFunctionException.
     * @param name
     * @return String
     */
    static String fixupClassName (String name) {

        // Count the number of array dimensions (if any).
        int i = 0;
        while (name.startsWith("[")) {
            i++;
            name = name.substring(1);
        }

        if (name.startsWith ("L") && name.endsWith(";")) {
            // This is a fully qualified class name.
            name = name.substring (1, name.length() - 1);
        } else
        if (name.equals ("Z")) {
            // boolean
            name = "boolean";
        } else
        if (name.equals ("B")) {
            name = "byte";
        } else
        if (name.equals ("C")) {
            name = "char";
        } else
        if (name.equals("S")) {
            name = "short";

        } else
        if (name.equals ("I")) {
            name = "integer";
        } else
        if (name.equals ("J")) {
            name = "long";
        } else
        if (name.equals ("F")) {
            name = "float";
        } else
        if (name.equals("D")) {
            name = "double";
        }

        for (int j = 0; j < i; ++j) {
            name = name + "[]";
        }

        // Substitute . for $
        name = name.replace ('$', '.');

        return name;
    }

    /**
     * A class used for generating Java source code from a
     * JavaExpression.
     *
     * @author rcypher
     *
     */
    private static class ExpressionTextNode {
        /** Max length of a generated line of source. */
        private static final int MAX_LINE_LENGTH = 80;

        /** The text associated with this node. */
        private String myText = "";

        /** Child nodes. */
        private List<ExpressionTextNode> children = new ArrayList<ExpressionTextNode>();

        /** The type of formatting for this node. */
        private final FormatType formatType;

        /**
         * Create an ExpressionTextNode
         * @param formatType
         */
        ExpressionTextNode (FormatType formatType) {
            if (formatType == null) {
                throw new NullPointerException("Invalid null value for format type.");
            }

            this.formatType = formatType;
        }

        /**
         * Create an ExpressionTextNode
         * @param myText
         * @param formatType
         */
        ExpressionTextNode (String myText, FormatType formatType) {
            if (formatType == null) {
                throw new NullPointerException("Invalid null value for format type.");
            }

            this.myText = myText;
            this.formatType = formatType;
        }

        @Override
        public String toString () {
            StringBuilder sb = new StringBuilder();
            appendExpressionText(sb);
            return sb.toString();
        }

        String getThisText() {
            return myText;
        }

        void setThisText (String newText) {
            myText = newText;
        }

        int getNChildren () {
            return children.size();
        }

        ExpressionTextNode getNthChild (int index) {
            return children.get(index);
        }

        int getTotalTextLength () {
            int totalLength = getThisText().length();
            for (final ExpressionTextNode expressionTextNode : children) {
                totalLength += expressionTextNode.getTotalTextLength();
            }
            return totalLength;
        }

        void addChild (ExpressionTextNode node) {
            children.add(node);
        }

        /**
         * Prefix the supplied text to the text of
         * this node, if there is any.  Otherwise prefix
         * it to the leftmost child node.
         * @param prefixText
         */
        void prefixText (String prefixText) {
            if(myText != null && myText.length() > 0) {
                myText = prefixText + myText;
            } else
            if (getNChildren() > 0) {
                getNthChild(0).prefixText(prefixText);
            }
        }

        /**
         * Suffix the given text to this nodes text if
         * there are no children.  Otherwise suffix to the
         * rightmost child.
         * @param suffixText
         */
        void suffixText (String suffixText) {
            if (getNChildren() == 0) {
                myText = myText + suffixText;
            } else {
                children.get(children.size()-1).suffixText(suffixText);
            }
        }

        /**
         * Format the Java source expression represented by this ExpressionTextNode
         * @param sb - the StringBuilder to put the formatted source in.
         * @param indentLevel
         * @param offset - offset from the current indent
         */
        private void formatExpressionText (
                StringBuilder sb,
                int indentLevel,
                int offset) {

            // Update the offset if necessary.
            if (offset == 0) {
                offset = sb.length() - sb.lastIndexOf(EOL) - EOL.length();
                if (offset < 0) {
                    offset = 0;
                }
            }

            // Find starting position on current line.
            int startPos = offset + (indentLevel * INDENT_LENGTH);

            // If the expression fits on the remainder of the current line
            // we can simply append the text.
            if (getTotalTextLength() + startPos <= MAX_LINE_LENGTH) {

                // If we're at the beginning of a line we need to indent.
                if (offset == 0) {
                    emitIndent(sb, indentLevel);
                }

                appendExpressionText(sb);
                return;
            }

            // If switching to the next line will give more space
            // emit a line break and increase the indent level.
            if (offset > 0 && offset > INDENT_LENGTH) {
                emitLine(sb);
                formatExpressionText(sb, ++indentLevel, 0);
                return;
            }

            // We need to break up the expression.

            // The expression is broken up based on the format type
            if (getFormatType().equals(FormatType.ATOMIC)) {
                if (offset == 0) {
                    emitIndent(sb, indentLevel);
                }

                appendExpressionText(sb);
            } else
            if (getFormatType().equals(FormatType.CHILDREN_AT_SAME_LEVEL)) {
                if (getThisText().length() > 0) {
                    if (offset == 0) {
                        emitIndent(sb, indentLevel);
                    }
                    sb.append(getThisText());
                    emitLine(sb);
                    offset = 0;
                }
                for (int i = 0, n = getNChildren(); i < n; ++i) {
                    getNthChild(i).formatExpressionText(sb, indentLevel, i == 0 ? offset : 0);
                    if (i < n-1) {
                        emitLine(sb);
                    }
                }
            } else
            if (getFormatType().equals(FormatType.CHILDREN_IN_ONE_LEVEL)) {
                if (getThisText().length() > 0) {
                    if (offset == 0) {
                        emitLine(sb, indentLevel, getThisText());
                    } else {
                        sb.append(getThisText());
                        emitLine(sb);
                        offset = 0;
                    }
                }

                // Format each child on a new line.
                for (int i = 0, n = getNChildren(); i < n; ++i) {
                    getNthChild(i).formatExpressionText(sb, indentLevel+1, 0);
                    if (i < n-1) {
                        emitLine(sb);
                    }
                }
            } else
            if (getFormatType().equals(FormatType.FIRST_CHILD_AT_LEVEL)) {
                if (getThisText().length() > 0) {
                    if (offset == 0) {
                        emitLine(sb, indentLevel, getThisText());
                    } else {
                        sb.append(getThisText());
                        emitLine(sb);
                        offset = 0;
                    }
                }

                // Format first child at the same indent level.
                getNthChild(0).formatExpressionText(sb, indentLevel, offset);
                if (getNChildren() > 1) {
                    emitLine(sb);
                }

                // Format other children on new lines.
                for (int i = 1, n = getNChildren(); i < n; ++i) {
                    getNthChild(i).formatExpressionText(sb, indentLevel+1, 0);
                    if (i < n-1) {
                        emitLine(sb);
                    }
                }
            } else {
                // Default behaviour is to place this nodes text on the current line and then
                // format each child starting on a new line with the same indent.
                if (getThisText().length() > 0) {
                    if (offset == 0) {
                        emitIndent(sb, indentLevel);
                    }
                    sb.append(getThisText());
                    emitLine(sb);
                    offset = 0;
                }

                for (int i = 0, n = getNChildren(); i < n; ++i) {
                    ExpressionTextNode child = getNthChild(i);
                    child.formatExpressionText(sb, indentLevel, offset);
                    if (!sb.toString().endsWith(EOL)) {
                        sb.append(EOL);
                    }
                }
            }

        }

        /**
         * Build up the concatenation of the text contained in
         * this node and all its children.
         * @param sb
         */
        private void appendExpressionText (StringBuilder sb) {
            sb.append(getThisText());
            for (int i = 0, n = getNChildren(); i < n; ++i) {
                getNthChild(i).appendExpressionText(sb);
            }

        }

        private FormatType getFormatType () {
            return formatType;
        }

        /**
         * A class used to indicate the type of formatting associated
         * with an ExpressionTextNode.
         * @author rcypher
         */
        static class FormatType {
            /** The text of the sub-tree cannot be broken apart. */
            static final FormatType ATOMIC = new FormatType(1, "ATOMIC");

            /** The text of the sub-tree can be broken between nodes.
             *  The children should have the same indent level as the root. */
            static final FormatType CHILDREN_AT_SAME_LEVEL = new FormatType(2, "CHILDREN_AT_SAME_LEVEL");

            /** The text of the sub-tree can be broken between nodes.
             *  The children should indent one more level than the root. */
            static final FormatType CHILDREN_IN_ONE_LEVEL = new FormatType(3, "CHILDREN_IN_ONE_LEVEL");

            /** The text of the sub-tree can be broken between nodes.
             *  The first child should have the same indent level as the root.
             *  Other children should be indented an additional level.*/
            static final FormatType FIRST_CHILD_AT_LEVEL = new FormatType(4, "FIRST_CHILD_AT_LEVEL");

            private final int type;
            private final String name;

            private FormatType (int type, String name) {
                this.type = type;
                this.name = name;
            }

            @Override
            public boolean equals(Object other) {
                return (other != null &&
                        other instanceof FormatType &&
                        ((FormatType)other).type == this.type);
            }

            @Override
            public String toString () {
                return name;
            }
        }
    }

    /**
     * This class is an extension of the JavaModelTraverser which
     * is used in generating Java source for from a JavaExpression.
     *
     * The JavaExpression is traversed and a tree of ExpressionTextNode
     * instances is created.  The text in each ExpressionTextNode is
     * considered to be an atomic part of the generated source (i.e.
     * it cannot be broken apart for formatting purposes)
     *
     * @author rcypher
     *
     */
    private static class ExpressionTextGenerator extends JavaModelTraverser<Void, Void> {
        ArrayStack<ExpressionTextNode> stack = ArrayStack.make();
        GenerationContext context;

        ExpressionTextGenerator (GenerationContext context) {
            this.context = context;
            stack.push(new ExpressionTextNode(ExpressionTextNode.FormatType.ATOMIC));
        }

        ExpressionTextNode getCurrentNode () {
            return stack.peek();
        }

        boolean doOptionalParenthesis (JavaExpression expression) {
            if (expression instanceof PlaceHolder) {
                expression = ((PlaceHolder)expression).getActualExpression();
            }

            boolean parenthesize =
                    (expression instanceof Assignment) ||
                    (expression instanceof CastExpression) ||
                    (expression instanceof ClassInstanceCreationExpression) ||
                    (expression instanceof ArrayCreationExpression) ||
                    (expression instanceof InstanceOf) ||
                    (expression instanceof OperatorExpression);        // sometimes not parenthesizing these are ok too..

            // parenthesize if the literal is a -ve number.
            if (!parenthesize && expression instanceof LiteralWrapper) {
                Object literalObject = ((LiteralWrapper)expression).getLiteralObject();
                if (literalObject instanceof Number && ((Number)literalObject).doubleValue() < 0) {
                    parenthesize = true;
                }
            }

            // don't parenthesize:
            //   (expression instanceof UnknownJavaExpression)
            //   (expression instanceof MethodInvocation)
            //   (expression instanceof JavaTypeName)
            //   (expression instanceof JavaField)
            //   (expression instanceof LocalVariable)
            //   (expression instanceof MethodVariable)
            //   (expression instanceof ArrayAccess)

            return parenthesize;
        }

        /* (non-Javadoc)
         * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitArrayAccessExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.ArrayAccess, java.lang.Object)
         */
        @Override
        public Void visitArrayAccessExpression(ArrayAccess arrayAccess, Void arg) {
            ExpressionTextNode node =
                new ExpressionTextNode(ExpressionTextNode.FormatType.CHILDREN_IN_ONE_LEVEL);
            stack.peek().addChild(node);
            stack.push(node);

            super.visitArrayAccessExpression(arrayAccess, arg);

            assert node.getNChildren() == 2;

            if (doOptionalParenthesis(arrayAccess.getArrayReference())) {
                node.getNthChild(0).prefixText("(");
                node.getNthChild(0).suffixText(")");
            }
            node.getNthChild(0).suffixText("[");
            node.getNthChild(1).suffixText("]");

            stack.pop();
            return null;
        }

        /** {@inheritDoc} */
        @Override
        public Void visitArrayLengthExpression(
                ArrayLength arrayLength,
                Void arg) {

            ExpressionTextNode node =
                new ExpressionTextNode(ExpressionTextNode.FormatType.CHILDREN_AT_SAME_LEVEL);
            stack.peek().addChild(node);
            stack.push(node);

            super.visitArrayLengthExpression(arrayLength, arg);

            if (doOptionalParenthesis(arrayLength.getArrayReference())) {
                node.getNthChild(0).prefixText("(");
                node.getNthChild(0).suffixText(")");
            }

            node.getNthChild(0).suffixText(".length");

            stack.pop();

            return null;
        }

        /* (non-Javadoc)
         * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitArrayCreationExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.ArrayCreationExpression, java.lang.Object)
         */
        @Override
        public Void visitArrayCreationExpression(
                ArrayCreationExpression arrayCreation,
                Void arg) {

            ExpressionTextNode node =
                new ExpressionTextNode(ExpressionTextNode.FormatType.ATOMIC);
            stack.peek().addChild(node);
            stack.push(node);

            try {
                String elementClassNameString = context.getSourceName(arrayCreation.getArrayElementTypeName());

                node.setThisText("new " + elementClassNameString + "[] {");
            } catch (Exception e) {

            }

            super.visitArrayCreationExpression(arrayCreation, arg);

            for (int i = 0, n = node.getNChildren()-1; i < n; ++i) {
                node.getNthChild(i).suffixText(", ");
            }
            node.suffixText("}");

            stack.pop();

            return null;
        }

        /* (non-Javadoc)
         * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitAssignmentExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.Assignment, java.lang.Object)
         */
        @Override
        public Void visitAssignmentExpression(
                Assignment assignment,
                Void arg) {

            ExpressionTextNode node =
                new ExpressionTextNode(ExpressionTextNode.FormatType.CHILDREN_IN_ONE_LEVEL);
            stack.peek().addChild(node);
            stack.push(node);

            super.visitAssignmentExpression(assignment, arg);
            node.getNthChild(0).suffixText(" = ");

            if (doOptionalParenthesis(assignment.getValue())) {
                node.getNthChild(1).prefixText("(");
                node.getNthChild(1).suffixText(")");
            }
            stack.pop();

            return null;
        }

        /* (non-Javadoc)
         * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitCastExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.CastExpression, java.lang.Object)
         */
        @Override
        public Void visitCastExpression(
                CastExpression cast,
                Void arg) {

            // Check for redundant casts. i.e. (RTRecordValue)(RTRecordValue)expressionToCast

            if (cast.getExpressionToCast() instanceof CastExpression) {
                CastExpression innerCast = (CastExpression)cast.getExpressionToCast();
                if (cast.getCastType().equals(innerCast.getCastType())) {
                    return super.visitCastExpression(cast, arg);
                }
            }

            ExpressionTextNode node =
                new ExpressionTextNode(ExpressionTextNode.FormatType.CHILDREN_IN_ONE_LEVEL);
            stack.peek().addChild(node);
            stack.push(node);

            String classNameString = context.getSourceName(cast.getCastType());
            if (cast.getCastType() instanceof JavaTypeName.Primitive) {
                node.setThisText("((" + classNameString + ")");
            } else {
                // This is a reference/array type cast, and we need to first upcast to java.lang.Object
                // so that the Java compiler does not complain about incompatible types in situations like this:
                // Suppose there are classes A, B, and C, where B and C are subclasses of A.
                // The cast is from A to B. The actual expression being cast has a static Java type of C.
                // While the JVM allows such a checkcast operation, the Java language does not, because
                // C is not compatible with B for a cast operation.
                node.setThisText("((" + classNameString + ")(" + context.getSourceName(JavaTypeName.OBJECT) + ")");
            }
            super.visitCastExpression(cast, arg);

            if (doOptionalParenthesis(cast.getExpressionToCast())) {
                node.getNthChild(0).prefixText("(");
                node.getNthChild(0).suffixText(")");
            }
            node.suffixText(")");

            stack.pop();

            return null;
        }

        /* (non-Javadoc)
         * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitClassInstanceCreationExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.ClassInstanceCreationExpression, java.lang.Object)
         */
        @Override
        public Void visitClassInstanceCreationExpression(
                ClassInstanceCreationExpression instanceCreation,
                Void arg) {

            ExpressionTextNode node =
                new ExpressionTextNode(ExpressionTextNode.FormatType.CHILDREN_IN_ONE_LEVEL);
            stack.peek().addChild(node);
            stack.push(node);

            node.setThisText("new ");

            JavaTypeName className = instanceCreation.getClassName();

            if (instanceCreation.isArrayCreationExpression()) {
                // Creating an array.

                // Get the source for the array element type
                final JavaTypeName.Reference.Array arrayType = ((JavaTypeName.Reference.Array)className);
                final int nDims = arrayType.getNDimensions();
                final JavaTypeName arrayElementType = arrayType.getElementType();

                final String classNameString = context.getSourceName(arrayElementType);
                node.suffixText(classNameString);

                super.visitClassInstanceCreationExpression(instanceCreation, arg);

                final int nArgs = instanceCreation.getNArgs();

                // Get the source for the args whose dimensions are specified.
                for (int i = 0; i < nArgs; i++) {
                    node.getNthChild(i).prefixText("[");
                    node.getNthChild(i).suffixText("]");
                }

                //the renaming dimensions (needed to specify the array type correctly)
                for (int i = 0, nRemaining = nDims - nArgs; i < nRemaining; ++i) {
                    node.addChild(new ExpressionTextNode("[]",
                                       ExpressionTextNode.FormatType.ATOMIC));
                }


            } else {
                // Creating a non-array object.

                // Get the source for the object type.
                String classNameString = context.getSourceName(className);
                node.suffixText(classNameString + "(");

                // Get the source for the args.
                super.visitClassInstanceCreationExpression(instanceCreation, arg);

                for (int i = 0, nArgs = instanceCreation.getNArgs()-1; i < nArgs; i++) {
                    node.getNthChild(i).suffixText(", ");
                }
                node.suffixText(")");
            }

            stack.pop();

            return null;
        }

        /* (non-Javadoc)
         * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitInstanceOfExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.InstanceOf, java.lang.Object)
         */
        @Override
        public Void visitInstanceOfExpression(
                InstanceOf instanceOf,
                Void arg) {

            ExpressionTextNode node =
                new ExpressionTextNode(ExpressionTextNode.FormatType.CHILDREN_IN_ONE_LEVEL);
            stack.peek().addChild(node);
            stack.push(node);

            super.visitInstanceOfExpression(instanceOf, arg);

            JavaTypeName referenceType = instanceOf.getReferenceType();

            // We need to first upcast to java.lang.Object
            // so that the Java compiler does not complain about incompatible types in situations like this:
            // Suppose there are classes A, B, and C, where B and C are subclasses of A.
            // The instanceof foreign function takes an argument of CAL type JA, whose Java implementation type is A.
            // The instanceof is a check for type B. The actual expression being checked has a static Java type of C.
            // While the JVM allows such an instanceof check, the Java language does not, because
            // C is not compatible with B as B is neither a subclass nor a superclass of C.
            node.setThisText("((" + context.getSourceName(JavaTypeName.OBJECT) + ")");
            if (doOptionalParenthesis(instanceOf.getJavaExpression())) {
                node.getNthChild(0).prefixText("(");
                node.getNthChild(0).suffixText(")");
            }
            node.suffixText(")");
            node.addChild(
                    new ExpressionTextNode(
                            " instanceof " + context.getSourceName(referenceType),
                            ExpressionTextNode.FormatType.ATOMIC));

            stack.pop ();

            return null;
        }

        /* (non-Javadoc)
         * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitInstanceFieldExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.JavaField.Instance, java.lang.Object)
         */
        @Override
        public Void visitInstanceFieldExpression(
                Instance instanceField,
                Void arg) {

            ExpressionTextNode node =
                new ExpressionTextNode(ExpressionTextNode.FormatType.ATOMIC);
            stack.peek().addChild(node);
            stack.push(node);

            super.visitInstanceFieldExpression(instanceField, arg);

            String fieldName = instanceField.getFieldName();
            if (node.getNChildren() > 0) {
                if (doOptionalParenthesis(instanceField.getInstance())) {
                    node.getNthChild(0).prefixText("(");
                    node.getNthChild(0).suffixText(")");
                }
                node.suffixText("." + fieldName);
            } else {
                node.setThisText(fieldName);
            }

            stack.pop();

            return null;
        }

        /* (non-Javadoc)
         * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitStaticFieldExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.JavaField.Static, java.lang.Object)
         */
        @Override
        public Void visitStaticFieldExpression(
                Static staticField,
                Void arg) {

            ExpressionTextNode node =
                new ExpressionTextNode(
                        context.getSourceName(staticField.getInvocationClass()) + "." + staticField.getFieldName(),
                        ExpressionTextNode.FormatType.ATOMIC);

            stack.peek().addChild(node);

            return null;
        }

        /* (non-Javadoc)
         * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitThisFieldExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.JavaField.This, java.lang.Object)
         */
        @Override
        public Void visitThisFieldExpression(
                This thisField,
                Void arg) {

            ExpressionTextNode node =
                new ExpressionTextNode(
                        "this",
                        ExpressionTextNode.FormatType.ATOMIC);
            stack.peek().addChild(node);

            return null;
        }

        /* (non-Javadoc)
         * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitLiteralWrapperExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.LiteralWrapper, java.lang.Object)
         */
        @Override
        public Void visitLiteralWrapperExpression(
                LiteralWrapper literalWrapper,
                Void arg) {

            ExpressionTextNode node =
                new ExpressionTextNode(
                        ExpressionTextNode.FormatType.ATOMIC);

            stack.peek().addChild(node);
            stack.push(node);

            Object literalObject = literalWrapper.getLiteralObject();
            if (literalObject instanceof Integer || literalObject instanceof Double || literalObject instanceof Boolean) {
                node.setThisText(literalObject.toString());

            } else if (literalObject instanceof Byte) {
                node.setThisText("(byte)" + literalObject.toString());

            } else if (literalObject instanceof Short) {
                node.setThisText("(short)" + literalObject.toString());

            } else if (literalObject instanceof Long) {
                node.setThisText(literalObject.toString() + "L");

            } else if (literalObject instanceof Float) {
                node.setThisText(literalObject.toString() + "F");

            } else if (literalObject instanceof String) {
                node.setThisText(StringEncoder.encodeString(literalObject.toString()));

            } else if (literalObject instanceof Character) {
                node.setThisText(StringEncoder.encodeChar(((Character)literalObject).charValue()));

            } else if (literalObject == null) {
                node.setThisText("null");
            }

            stack.pop();

            return null;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public Void visitClassLiteralExpression(ClassLiteral classLiteral, Void arg) {

            ExpressionTextNode node =
                new ExpressionTextNode(
                        context.getSourceName(classLiteral.getReferentType()) + ".class",
                        ExpressionTextNode.FormatType.ATOMIC);

            stack.peek().addChild(node);

            return null;
        }

        /* (non-Javadoc)
         * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitLocalNameExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.LocalName, java.lang.Object)
         */
        @Override
        public Void visitLocalNameExpression(
                LocalName localName,
                Void arg) {

            ExpressionTextNode node =
                new ExpressionTextNode(
                        localName.getName(),
                        ExpressionTextNode.FormatType.ATOMIC);
            stack.peek().addChild(node);

            return null;
        }

        /* (non-Javadoc)
         * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitLocalVariableExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.LocalVariable, java.lang.Object)
         */
        @Override
        public Void visitLocalVariableExpression(
                LocalVariable localVariable,
                Void arg) {

            ExpressionTextNode node =
                new ExpressionTextNode(
                        localVariable.getName(),
                        ExpressionTextNode.FormatType.ATOMIC);

            stack.peek().addChild(node);

            return null;
        }

        /* (non-Javadoc)
         * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitInstanceMethodInvocationExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.MethodInvocation.Instance, java.lang.Object)
         */
        @Override
        public Void visitInstanceMethodInvocationExpression(
                MethodInvocation.Instance instanceInvocation,
                Void arg) {

            final ExpressionTextNode node;
            if (instanceInvocation.getInvocationTarget() == null) {
                node =
                    new ExpressionTextNode(ExpressionTextNode.FormatType.CHILDREN_IN_ONE_LEVEL);
            } else {
                node =
                    new ExpressionTextNode(ExpressionTextNode.FormatType.FIRST_CHILD_AT_LEVEL);
            }

            stack.peek().addChild(node);
            stack.push(node);


            super.visitInstanceMethodInvocationExpression(instanceInvocation, arg);

            int firstArg = 0;

            // The invocation target can be null, which means use "this" or "super" (depending on invocationType).
            // since "this" is optional in Java source in this situation, we just omit it.
            if (instanceInvocation.getInvocationTarget() == null) {
                if (instanceInvocation.getInvocationType() == MethodInvocation.InvocationType.SPECIAL) {
                    node.setThisText("super." + instanceInvocation.getMethodName() + "(");
                } else {
                    node.setThisText(instanceInvocation.getMethodName() + "(");
                }
            } else {
                if (doOptionalParenthesis(instanceInvocation.getInvocationTarget())) {
                    node.getNthChild(0).prefixText("(");
                    node.getNthChild(0).suffixText(")");
                }
                node.getNthChild(0).suffixText("." + instanceInvocation.getMethodName() + "(");
                firstArg++;
            }

            for(int i = firstArg, n = node.getNChildren() - 1; i < n; ++i) {
                node.getNthChild(i).suffixText(", ");
            }

            node.suffixText(")");

            stack.pop();

            return null;
        }

        /* (non-Javadoc)
         * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitStaticMethodInvocationExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.MethodInvocation.Static, java.lang.Object)
         */
        @Override
        public Void visitStaticMethodInvocationExpression(
                JavaExpression.MethodInvocation.Static staticInvocation,
                Void arg) {

            ExpressionTextNode node =
                new ExpressionTextNode(ExpressionTextNode.FormatType.CHILDREN_IN_ONE_LEVEL);
            stack.peek().addChild(node);
            stack.push(node);

            node.setThisText(context.getSourceName(staticInvocation.getInvocationClass()) + "." + staticInvocation.getMethodName() + "(");

            super.visitStaticMethodInvocationExpression(staticInvocation, arg);
            for (int i = 0, nArgs = staticInvocation.getNArgs() - 1; i < nArgs; i++) {
                node.getNthChild(i).suffixText(", ");
            }

            node.suffixText(")");

            stack.pop();

            return null;
        }

        /* (non-Javadoc)
         * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitMethodVariableExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.MethodVariable, java.lang.Object)
         */
        @Override
        public Void visitMethodVariableExpression(
                MethodVariable methodVariable,
                Void arg) {

            ExpressionTextNode node =
                new ExpressionTextNode(
                        methodVariable.getName(),
                        ExpressionTextNode.FormatType.ATOMIC);

            stack.peek().addChild(node);

            return null;
        }

        /* (non-Javadoc)
         * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitBinaryOperatorExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.OperatorExpression.Binary, java.lang.Object)
         */
        @Override
        public Void visitBinaryOperatorExpression(
                Binary binaryOperator,
                Void arg) {

            ExpressionTextNode node =
                new ExpressionTextNode(
                        ExpressionTextNode.FormatType.CHILDREN_AT_SAME_LEVEL);

            stack.peek().addChild(node);
            stack.push(node);

            super.visitBinaryOperatorExpression(binaryOperator, arg);

            if (doOptionalParenthesis(binaryOperator.getArgument(0))) {
                node.getNthChild(0).prefixText("(");
                node.getNthChild(0).suffixText(")");
            }
            if (doOptionalParenthesis(binaryOperator.getArgument(1))) {
                node.getNthChild(1).prefixText("(");
                node.getNthChild(1).suffixText(")");
            }
            node.getNthChild(0).suffixText(" " + binaryOperator.getJavaOperator().getSymbol() + " ");

            stack.pop();

            return null;
        }

        /* (non-Javadoc)
         * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitTernaryOperatorExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.OperatorExpression.Ternary, java.lang.Object)
         */
        @Override
        public Void visitTernaryOperatorExpression(
                Ternary ternaryOperator,
                Void arg) {

            ExpressionTextNode node =
                new ExpressionTextNode(
                        ExpressionTextNode.FormatType.CHILDREN_AT_SAME_LEVEL);

            stack.peek().addChild(node);
            stack.push(node);

            super.visitTernaryOperatorExpression(ternaryOperator, arg);
            if (doOptionalParenthesis(ternaryOperator.getArgument(0))) {
                node.getNthChild(0).prefixText("(");
                node.getNthChild(0).suffixText(")");
            }
            if (doOptionalParenthesis(ternaryOperator.getArgument(1))) {
                node.getNthChild(1).prefixText("(");
                node.getNthChild(1).suffixText(")");
            }
            if (doOptionalParenthesis(ternaryOperator.getArgument(2))) {
                node.getNthChild(2).prefixText("(");
                node.getNthChild(2).suffixText(")");
            }

            node.getNthChild(0).suffixText(" ? " );
            node.getNthChild(1).suffixText(" : ");

            stack.pop();

            return null;
        }

        /* (non-Javadoc)
         * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitUnaryOperatorExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.OperatorExpression.Unary, java.lang.Object)
         */
        @Override
        public Void visitUnaryOperatorExpression(
                Unary unaryOperator,
                Void arg) {

            ExpressionTextNode node =
                new ExpressionTextNode(
                        ExpressionTextNode.FormatType.CHILDREN_AT_SAME_LEVEL);

            stack.peek().addChild(node);
            stack.push(node);

            super.visitUnaryOperatorExpression(unaryOperator, arg);
            if (doOptionalParenthesis(unaryOperator.getArgument(0))) {
                node.getNthChild(0).prefixText("(");
                node.getNthChild(0).suffixText(")");
            }

            node.prefixText(unaryOperator.getJavaOperator().getSymbol());

            stack.pop();

            return null;
        }

    }

    /**
     * An extension of JavaModelTraverser which traverses a JavaModel
     * construct and builds up the context of imported classes.
     *
     * @author rcypher
     *
     */
    private static class ImportGenerator extends JavaModelTraverser<Void, Void> {

        /** The context of imported classes. */
        GenerationContext context;

        /**
         * Create an ImportGenerator.
         * @param context - the context of imported classes.
         */
        ImportGenerator (GenerationContext context) {
            this.context = context;
        }

        /* (non-Javadoc)
         * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitArrayCreationExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.ArrayCreationExpression, java.lang.Object)
         */
        @Override
        public Void visitArrayCreationExpression(
                ArrayCreationExpression arrayCreation, Void arg) {
            context.getSourceName(arrayCreation.getArrayElementTypeName());
            return super.visitArrayCreationExpression(arrayCreation, arg);
        }

        /* (non-Javadoc)
         * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitCastExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.CastExpression, java.lang.Object)
         */
        @Override
        public Void visitCastExpression(
                CastExpression cast,
                Void arg) {
            context.getSourceName(cast.getCastType());
            return super.visitCastExpression(cast, arg);
        }

        /* (non-Javadoc)
         * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitClassInstanceCreationExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.ClassInstanceCreationExpression, java.lang.Object)
         */
        @Override
        public Void visitClassInstanceCreationExpression(
                ClassInstanceCreationExpression instanceCreation,
                Void arg) {

            JavaTypeName className = instanceCreation.getClassName();

            if (instanceCreation.isArrayCreationExpression()) {
                // Creating an array.

                // Get the source for the array element type
                final JavaTypeName.Reference.Array arrayType = ((JavaTypeName.Reference.Array)className);
                final JavaTypeName arrayElementType = arrayType.getElementType();
                context.getSourceName(arrayElementType);
            } else {
                // Creating a non-array object.

                // Get the source for the object type.
                context.getSourceName(className);
            }

            return super.visitClassInstanceCreationExpression(instanceCreation, arg);
        }

        /* (non-Javadoc)
         * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitInstanceOfExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.InstanceOf, java.lang.Object)
         */
        @Override
        public Void visitInstanceOfExpression(
                InstanceOf instanceOf,
                Void arg) {

            JavaTypeName referenceType = instanceOf.getReferenceType();
            context.getSourceName(referenceType);

            return super.visitInstanceOfExpression(instanceOf, arg);
        }

        /* (non-Javadoc)
         * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitStaticFieldExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.JavaField.Static, java.lang.Object)
         */
        @Override
        public Void visitStaticFieldExpression(
                Static staticField,
                Void arg) {

            context.getSourceName(staticField.getInvocationClass());
            return super.visitStaticFieldExpression(staticField, arg);
        }

        /* (non-Javadoc)
         * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitStaticMethodInvocationExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.MethodInvocation.Static, java.lang.Object)
         */
        @Override
        public Void visitStaticMethodInvocationExpression(
                JavaExpression.MethodInvocation.Static staticInvocation,
                Void arg) {

            context.getSourceName(staticInvocation.getInvocationClass());

            return super.visitStaticMethodInvocationExpression(staticInvocation, arg);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public Void visitClassLiteralExpression(ClassLiteral classLiteral, Void arg) {

            context.getSourceName(classLiteral.getReferentType());
            return super.visitClassLiteralExpression(classLiteral, arg);
        }

        /* (non-Javadoc)
         * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitJavaClassRep(org.openquark.cal.internal.runtime.lecc.JavaClassRep, java.lang.Object)
         */
        @Override
        public Void visitJavaClassRep(JavaClassRep classRep, Void arg) {
            JavaTypeName superclassName = classRep.getSuperclassName();
            if (!superclassName.equals(JavaTypeName.OBJECT)) {
                context.getSourceName(superclassName);
            }

            for (int i = 0, n = classRep.getNInterfaces(); i < n; i++) {
                context.getSourceName(classRep.getInterface(i));
            }

            return super.visitJavaClassRep(classRep, arg);
        }

        /* (non-Javadoc)
         * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitLocalVariableDeclarationStatement(org.openquark.cal.internal.runtime.lecc.JavaStatement.LocalVariableDeclaration, java.lang.Object)
         */
        @Override
        public Void visitLocalVariableDeclarationStatement(
                LocalVariableDeclaration localVariableDeclaration, Void arg) {

            LocalVariable localVariable = localVariableDeclaration.getLocalVariable();
            context.getSourceName(localVariable.getTypeName());

            return super.visitLocalVariableDeclarationStatement(localVariableDeclaration, arg);
        }

        /* (non-Javadoc)
         * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitJavaFieldDeclaration(org.openquark.cal.internal.runtime.lecc.JavaFieldDeclaration, java.lang.Object)
         */
        @Override
        public Void visitJavaFieldDeclaration(
                JavaFieldDeclaration fieldDeclaration, Void arg) {

            JavaTypeName fieldType = fieldDeclaration.getFieldType();
            context.getSourceName(fieldType);

            return super.visitJavaFieldDeclaration(fieldDeclaration, arg);
        }

        /* (non-Javadoc)
         * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitJavaMethod(org.openquark.cal.internal.runtime.lecc.JavaMethod, java.lang.Object)
         */
        @Override
        public Void visitJavaMethod(JavaMethod javaMethod, Void arg) {
            // Return type
            context.getSourceName(javaMethod.getReturnType());

            // Parameters
            for (int i = 0, nParams = javaMethod.getNParams(); i < nParams; i++) {
                context.getSourceName(javaMethod.getParamType(i));
            }

            // throws
            for (int i = 0, n = javaMethod.getNThrownExceptions(); i < n; ++i) {
                JavaTypeName thrownExceptionName = javaMethod.getThrownException(i);
                context.getSourceName(thrownExceptionName);
            }

            return super.visitJavaMethod(javaMethod, arg);
        }

        /* (non-Javadoc)
         * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitJavaConstructor(org.openquark.cal.internal.runtime.lecc.JavaConstructor, java.lang.Object)
         */
        @Override
        public Void visitJavaConstructor(JavaConstructor javaConstructor,
                Void arg) {
            for (int i = 0, n = javaConstructor.getNParams(); i < n; ++i) {
                context.getSourceName(javaConstructor.getParamType(i));
            }
            return super.visitJavaConstructor(javaConstructor, arg);
        }
    }
}

TOP

Related Classes of org.openquark.cal.internal.javamodel.JavaSourceGenerator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.