Package org.openquark.cal.internal.javamodel

Source Code of org.openquark.cal.internal.javamodel.JavaBindingGenerator$JavaBindingClassChecker

/*
* 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.
*/


/*
* JavaBindingGenerator.java
* Creation date: Jul 12, 2006
* By: RCypher
*/

package org.openquark.cal.internal.javamodel;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;

import org.openquark.cal.caldoc.CALDocToJavaDocUtilities;
import org.openquark.cal.compiler.CALDocComment;
import org.openquark.cal.compiler.ClassMethod;
import org.openquark.cal.compiler.DataConstructor;
import org.openquark.cal.compiler.ForeignTypeInfo;
import org.openquark.cal.compiler.Function;
import org.openquark.cal.compiler.FunctionalAgent;
import org.openquark.cal.compiler.ModuleName;
import org.openquark.cal.compiler.ModuleTypeInfo;
import org.openquark.cal.compiler.QualifiedName;
import org.openquark.cal.compiler.SourceModel;
import org.openquark.cal.compiler.TypeClass;
import org.openquark.cal.compiler.TypeConsApp;
import org.openquark.cal.compiler.TypeConstructor;
import org.openquark.cal.compiler.TypeExpr;
import org.openquark.cal.compiler.UnableToResolveForeignEntityException;
import org.openquark.cal.internal.javamodel.JavaExpression.JavaField;
import org.openquark.cal.internal.javamodel.JavaExpression.LiteralWrapper;
import org.openquark.cal.internal.javamodel.JavaExpression.MethodInvocation;
import org.openquark.cal.internal.javamodel.JavaExpression.MethodVariable;
import org.openquark.cal.internal.javamodel.JavaStatement.JavaDocComment;
import org.openquark.cal.internal.javamodel.JavaStatement.MultiLineComment;
import org.openquark.cal.internal.javamodel.JavaStatement.ReturnStatement;
import org.openquark.cal.machine.Module;
import org.openquark.cal.module.Cal.Core.CAL_Prelude;


/**
* This class is used to generate the Java source code for a binding class
* for a CAL module.
*
* This class is the preferred means for client code to refer to CAL entities.
* For example rather than referring to List.sort by using literal strings the
* client code would use the field 'QualifiedName sort = new QualifiedName("List", "sort");'
* If at some point the name of the function is changed in the CAL code the
* binding class will be regenerated and compilation errors will show anywhere that
* client code was referring to this function.
*
* A binding class contains a field with the name of the corresponding CAL module.
* It contains four inner classes: Functions, DataConstructors, TypeConstructors, and TypeClasses.
*
* The inner classes contain a QualifiedName field corresponding to each entity of that type.
* In addition the Functions and DataConstructors classes contain helper methods which make
* it easier to generate SourceModel for applications of the function/data constructor.
*
* @author RCypher
*
*/
public class JavaBindingGenerator {
   
    static final JavaTypeName[] EMPTY_TYPE_NAME_ARRAY = new JavaTypeName[0];

    /**
     * The suffix to append to the binding class name when it is the binding class for
     * non public entities.
     */
    private static final String PRIVATE_BINDING_SUFFIX = "_internal";
   
    /**
     * The prefix applied to the name of the generated java binding class.
     */
    private static final String BINDING_CLASS_PREFIX = "CAL_";
   
    /** The name of the inner class for data constructors. */
    private static final String DATA_CONSTRUCTOR_CLASS_NAME = "DataConstructors";
   
    /** The name of the inner class for functions. */
    private static final String FUNCTION_CLASS_NAME = "Functions";
   
    /** The name of the inner class for type classes. */
    private static final String TYPECLASS_CLASS_NAME = "TypeClasses";
   
    /** The name of the inner classes for type constructors (i.e. data types). */
    private static final String TYPECONSTRUCTOR_CLASS_NAME = "TypeConstructors";
   
    /** The name of the field which will hold the module name. */
    private static final String MODULE_NAME_FIELD_NAME = "MODULE_NAME";
   
    /** Constant corresponding to Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL. */
    private static final int PUBLIC_STATIC_FINAL = Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL;
   
    /** The JavaTypeName for QualifiedName. */
    private static final JavaTypeName QUALIFIED_NAME_TYPE_NAME = JavaTypeName.make(QualifiedName.class);
   
    /** The JavaTypeName for SourceModel.Expr. */
    private static final JavaTypeName SOURCE_MODEL_EXPR_TYPE_NAME = JavaTypeName.make(SourceModel.Expr.class);
   
    /** The JavaTypeName for SourceModel.Expr.Var. */
    private static final JavaTypeName SOURCE_MODEL_EXPR_VAR_TYPE_NAME = JavaTypeName.make(SourceModel.Expr.Var.class);
   
    /** The JavaTypeName for sourceModel.Expr.DataCons. */
    private static final JavaTypeName SOURCE_MODEL_EXPR_DATA_CONS_TYPE_NAME = JavaTypeName.make(SourceModel.Expr.DataCons.class);
   
    /** The JavaTypeName for SourceModel.Expr.Application. */
    private static final JavaTypeName SOURCE_MODEL_EXPR_APPLICATION_TYPE_NAME =  JavaTypeName.make(SourceModel.Expr.Application.class);
   
    /** The name of the field used to store the hash value for the concatenated JavaDoc. */
    private static final String JAVADOC_HASH_FIELD_NAME = "javaDocHash";
   
    /** Set this flag to true if debugging output is desired. */
    private static final boolean SHOW_DEBUGGING_OUTPUT = false;
   
    /** The module for which a binding class is being generated. */
    private final Module module;
   
    /** The module type info for the module for which a binding class is being generated. */
    private final ModuleTypeInfo moduleTypeInfo;
   
    /** Flag indicating that the binding class is for public (vs. non public) entities. */
    private final boolean publicEntities;
   
    /** JavaField for referring to the module name field in the generated binding class. */
    private final JavaField moduleNameField;
   
    /** The JavaTypeName for the generated binding class. */
    private final JavaTypeName bindingClassTypeName;
   
    /** The unqualified name of the Java binding class. */
    private final String bindingClassName;

    /** The package in which generated binding classes are placed. */
    private final String bindingClassPackage;

    /**
     * Generate the source code for the Java binding class for the named module.
     * @param moduleTypeInfo
     * @param targetPackage
     * @param publicEntities
     * @return The source of the generated class.
     */
    public static final String getJavaBinding (ModuleTypeInfo moduleTypeInfo, String targetPackage, boolean publicEntities) throws UnableToResolveForeignEntityException {
        JavaBindingGenerator instance =
            new JavaBindingGenerator (moduleTypeInfo, targetPackage, publicEntities);
       
        return instance.buildJavaBinding();
    }

    /**
     * For the given module check that a binding class exists and that it
     * is current with the content of the CAL module. 
     * @param moduleTypeInfo
     * @param targetPackage
     * @param publicEntities
     * @param showFailureInfo if true, extra debug info will be output for any failed binding check
     * @return true if the binding class exists and is up-to-date, false otherwise.
     */
    public static final boolean checkJavaBindingClass (ModuleTypeInfo moduleTypeInfo, String targetPackage, boolean publicEntities, boolean showFailureInfo) throws UnableToResolveForeignEntityException {
        boolean showDebugOrFailureInfo = showFailureInfo || SHOW_DEBUGGING_OUTPUT;
        try {
            // Generate the Java model for a Java binding class based on the current CAL
            // module.
            JavaBindingGenerator instance =
                new JavaBindingGenerator (moduleTypeInfo, targetPackage, publicEntities);

            JavaClassRep bindingClassModel = instance.buildJavaBindingModel();

            // Check to see if the Java binding class already exists.
            Class<?> existingBindingClass = Class.forName(bindingClassModel.getClassName().getFullJavaSourceName(), true, moduleTypeInfo.getModule().getForeignClassLoader());

            if (bindingClassModel.getNInnerClasses() != existingBindingClass.getDeclaredClasses().length) {
                if (showDebugOrFailureInfo) {
                    System.out.println("bindingClassModel.getNInnerClasses() : " + bindingClassModel.getNInnerClasses() + " != existingBindingClass.getDeclaredClasses().length : " + existingBindingClass.getDeclaredClasses().length);
                }
                return false;
            }
           
            // Walk the Java model building up information about the QualifiedName
            // fields in the class.
            JavaBindingClassChecker bindingClassChecker = new JavaBindingClassChecker();
            bindingClassModel.accept(bindingClassChecker, null);
           
            if (!checkInnerBindingClass(
                    existingBindingClass,
                    FUNCTION_CLASS_NAME,
                    bindingClassChecker.functionFields,
                    bindingClassChecker.functions,
                    null,
                    showDebugOrFailureInfo)) {
                if (showDebugOrFailureInfo) {
                    System.out.println("Inner function binding class mismatch.");
                }
                return false;
            }

            if (!checkInnerBindingClass(
                    existingBindingClass,
                    DATA_CONSTRUCTOR_CLASS_NAME,
                    bindingClassChecker.dataConsFields,
                    bindingClassChecker.dataConsFunctions,
                    bindingClassChecker.dataConsOrdinalFields,
                    showDebugOrFailureInfo)) {
                if (showDebugOrFailureInfo) {
                    System.out.println("Inner data constructor binding class mismatch.");
                }
                return false;
            }

            if (!checkInnerBindingClass(
                    existingBindingClass,
                    TYPECLASS_CLASS_NAME,
                    bindingClassChecker.typeClassFields,
                    null,
                    null,
                    showDebugOrFailureInfo)) {
                if (showDebugOrFailureInfo) {
                    System.out.println("Inner type class binding class mismatch.");
                }
                return false;
            }

            if (!checkInnerBindingClass(
                    existingBindingClass,
                    TYPECONSTRUCTOR_CLASS_NAME,
                    bindingClassChecker.typeConstructorFields,
                    null,
                    null,
                    showDebugOrFailureInfo)) {
                if (showDebugOrFailureInfo) {
                    System.out.println("Inner type constructor binding class mismatch.");
                }
                return false;
            }
           
            // Check the JavaDoc has value computed for the generated class.
            int newJavaDocHash = 0;
            for (int i = 0, n = bindingClassModel.getNFieldDeclarations(); i < n; ++i) {
                JavaFieldDeclaration jfd = bindingClassModel.getFieldDeclaration(i);
                if (jfd.getFieldName().equals(JavaBindingGenerator.JAVADOC_HASH_FIELD_NAME)) {
                    newJavaDocHash = ((Integer)((LiteralWrapper)jfd.getInitializer()).getLiteralObject()).intValue();
                    break;
                }
            }

            try {
                Field existingJavaDocHashField =
                    existingBindingClass.getDeclaredField(JavaBindingGenerator.JAVADOC_HASH_FIELD_NAME);
                int existingJavaDocHash = existingJavaDocHashField.getInt(null);
                if (newJavaDocHash != existingJavaDocHash) {
                    if (showDebugOrFailureInfo) {
                        System.out.println("newJavaDocHash : " + newJavaDocHash + " != existingJavaDocHash : " + existingJavaDocHash);
                    }
                    return false;
                }
            } catch (NoSuchFieldException e) {
                if (showDebugOrFailureInfo) {
                    System.out.println("Exception: " + e);
                }
                return false;
            } catch (IllegalAccessException e) {
                if (showDebugOrFailureInfo) {
                    System.out.println("Exception: " + e);
                }
                return false;
            }
           
        } catch (ClassNotFoundException e) {
            if (showDebugOrFailureInfo) {
                System.out.println("Exception: " + e);
            }
            return false;
        }
       
        return true;
    }
   
    /**
     * Compare the existing inner binding class to what it should actually be.
     * @param bindingClass - the existing binding class.
     * @param innerClassName - the name of the inner class to check.
     * @param expectedQualifiedNameFields - Map of String -> JavaFieldDeclaration. (i.e. field name to field declaration)
     * @param expectedFunctions - Set of JavaMethod. (i.e. method name to method)
     * @param expectedIntFields - Map of String -> JavaFieldDeclaration. (i.e. field name to field declaration)
     * @param showDebugOrFailureInfo if true, extra debug info will be output for any failed binding check
     * @return true if the existing and expected inner classes match.
     */
    private static final boolean checkInnerBindingClass (
            Class<?> bindingClass,
            String innerClassName,
            Map<String, JavaFieldDeclaration> expectedQualifiedNameFields,
            Set<JavaMethod> expectedFunctions,
            Map<String, JavaFieldDeclaration> expectedIntFields,
            boolean showDebugOrFailureInfo) {

        // Get the inner classes from the existing binding class.
        Class<?> declaredClasses[] = bindingClass.getDeclaredClasses();
        Class<?> innerClass = null;
        for (int i = 0; i < declaredClasses.length; ++i) {
            String dcName = declaredClasses[i].getName();
            dcName = dcName.substring(dcName.lastIndexOf("$") + 1);
            if (dcName.equals(innerClassName)) {
                innerClass = declaredClasses[i];
                break;
            }
        }

        Map<String, Field> declaredQualifiedNameFields = new HashMap<String, Field>();
        SortedSet<String> declaredQualifiedNameFieldsNames = new TreeSet<String>();
        Map<String, Field> declaredIntFields = new HashMap<String, Field>();
        if (innerClass != null) {
            Field declaredFields[] = innerClass.getDeclaredFields();
            for (int i = 0, n = declaredFields.length; i < n; ++i) {
                Field declaredField = declaredFields[i];
                if (declaredField.getType().equals(QualifiedName.class)) {
                    declaredQualifiedNameFields.put(declaredField.getName(), declaredField);
                    declaredQualifiedNameFieldsNames.add(declaredField.getName());
                }
                if (declaredField.getType().equals(int.class)) {
                    declaredIntFields.put(declaredField.getName(), declaredField);
                }
            }
        }
       
        SortedSet<String> expectedQualifiedNameFieldsNames = new TreeSet<String>();
        for (final String expectedQualifiedNameFieldName : expectedQualifiedNameFields.keySet()) {
            expectedQualifiedNameFieldsNames.add(expectedQualifiedNameFieldName);
        }
       
        // Check the qualified name fields.
        if (!expectedQualifiedNameFieldsNames.equals(declaredQualifiedNameFieldsNames)) {
            // Different number of entities in the existing class and
            // the current module.
            if (showDebugOrFailureInfo) {
                System.out.println("expectedQualifiedNameFieldsNames: " + expectedQualifiedNameFieldsNames);
                System.out.println("... does not equal ...");
                System.out.println("declaredQualifiedNameFieldsNames: " + declaredQualifiedNameFieldsNames);
            }
            return false;
        }
       
        // check the ordinal fields.
        if (expectedIntFields == null) {
            expectedIntFields = new HashMap<String, JavaFieldDeclaration>();
        }
        if (expectedIntFields.size() != declaredIntFields.size()) {
            if (showDebugOrFailureInfo) {
                System.out.println("expectedIntFields.size() : " + expectedIntFields.size() + " != declaredIntFields.size() : " + declaredIntFields.size());
            }
            return false;
        }
        // Check the field names and values.
        for (final String fieldName : expectedIntFields.keySet()) {
            JavaFieldDeclaration jfd = expectedIntFields.get(fieldName);
           
            Field existingField = declaredIntFields.get(fieldName);
            if (existingField == null) {
                if (showDebugOrFailureInfo) {
                    System.out.println("Missing field named: " + fieldName);
                }
                return false;
            }
            try {
                int existingValue = existingField.getInt(null);
               
                JavaExpression init = jfd.getInitializer();
                if (!(init instanceof LiteralWrapper)) {
                    if (showDebugOrFailureInfo) {
                        System.out.println("Field " + fieldName + " does not equal " + existingValue);
                    }
                    return false;
                }
                Object value = ((LiteralWrapper)init).getLiteralObject();
                if (!(value instanceof Integer)) {
                    if (showDebugOrFailureInfo) {
                        System.out.println("Field " + fieldName + " does not equal " + existingValue);
                    }
                    return false;
                }
               
                if (existingValue != ((Integer)value).intValue()) {
                    if (showDebugOrFailureInfo) {
                        System.out.println("Field " + fieldName + " does not equal " + existingValue);
                    }
                    return false;
                }
            } catch (IllegalAccessException e) {
                if (showDebugOrFailureInfo) {
                    System.out.println("Exception: " + e);
                }
                return false;
            }
        }
       
       
        if (expectedFunctions == null) {
            expectedFunctions = new HashSet<JavaMethod>();
        }
       
        // Now check the functions.
        int nDeclaredMethods = 0;
        if (innerClass != null) {
            nDeclaredMethods = innerClass.getDeclaredMethods().length;
        }
       
        if (nDeclaredMethods != expectedFunctions.size()) {
            if (showDebugOrFailureInfo) {
                System.out.println("nDeclaredMethods : " + nDeclaredMethods + " != expectedFunctions.size() : " + expectedFunctions.size());
            }
            return false;
        }
       
        // Now we need to confirm that the functions are the same. 
        // i.e. same name and type.
        for (final JavaMethod bindingClassMethod : expectedFunctions) {
            String bindingMethodName = bindingClassMethod.getMethodName();
            int nParams = bindingClassMethod.getNParams();
            Class<?> bindingMethodParamTypes[] = new Class<?>[nParams];
            for (int iParam = 0; iParam < nParams; ++iParam) {
                JavaTypeName paramType = bindingClassMethod.getParamType(iParam);
                if (paramType instanceof JavaTypeName.Primitive) {
                    if (paramType instanceof JavaTypeName.Primitive.Boolean) {
                        bindingMethodParamTypes[iParam] = boolean.class;
                    } else
                    if (paramType instanceof JavaTypeName.Primitive.Byte) {
                        bindingMethodParamTypes[iParam] = byte.class;
                    } else
                    if (paramType instanceof JavaTypeName.Primitive.Char) {
                        bindingMethodParamTypes[iParam] = char.class;
                    } else
                    if (paramType instanceof JavaTypeName.Primitive.Double) {
                        bindingMethodParamTypes[iParam] = double.class;
                    } else
                    if (paramType instanceof JavaTypeName.Primitive.Float) {
                        bindingMethodParamTypes[iParam] = float.class;
                    } else
                    if (paramType instanceof JavaTypeName.Primitive.Int) {
                        bindingMethodParamTypes[iParam] = int.class;
                    } else
                    if (paramType instanceof JavaTypeName.Primitive.Long) {
                        bindingMethodParamTypes[iParam] = long.class;
                    } else
                    if (paramType instanceof JavaTypeName.Primitive.Short) {
                        bindingMethodParamTypes[iParam] = short.class;
                    } else {
                        if (showDebugOrFailureInfo) {
                            System.out.println("Unrecognized primitive param type: " + paramType);
                        }
                        return false;
                    }
                } else
                if (paramType.equals(JavaBindingGenerator.SOURCE_MODEL_EXPR_TYPE_NAME)) {
                    bindingMethodParamTypes[iParam] = SourceModel.Expr.class;
                } else
                if (paramType.equals(JavaTypeName.STRING)) {
                    bindingMethodParamTypes[iParam] = java.lang.String.class;
                } else {
                    if (showDebugOrFailureInfo) {
                        System.out.println("Unrecognized param type: " + paramType);
                    }
                    return false;
                }
            }
           
            if (innerClass != null) {
                try {
                    Method declaredMethod = innerClass.getDeclaredMethod(bindingMethodName, bindingMethodParamTypes);
                    if (declaredMethod == null) {
                        if (showDebugOrFailureInfo) {
                            System.out.println("Missing method: " + bindingMethodName + ", params: " + Arrays.asList(bindingMethodParamTypes));
                        }
                        return false;
                    }
                } catch (NoSuchMethodException e) {
                    // Mismatch so return false.
                    if (showDebugOrFailureInfo) {
                        System.out.println("Exception: " + e);
                    }
                    return false;
                }
            }
        }
       
        return true;
    }

    /**
     * Returns the full name of the package for the class generated for the given module.
     * For example, if the target package is foo.bar and the module's name is A.B.C, the returned
     * package name would be foo.bar.A.B (into which the class CAL_C would go).
     *
     * @param targetPackage the name of the target package.
     * @param moduleName the module name.
     * @return the full name of the package for the class generated for the given module.
     */
    public static String getPackageName(String targetPackage, ModuleName moduleName) {
        String basePackage = targetPackage == null ? "" : targetPackage;
        StringBuilder packageName = new StringBuilder(basePackage);
       
        // to the base package we add all the components but the last to the package name
        // the last component is to be transformed to a class name
        for (int i = 0, nMinusOne = moduleName.getNComponents() - 1; i < nMinusOne; i++) {
            if (packageName.length() > 0) {
                packageName.append('.');
            }
            packageName.append(moduleName.getNthComponent(i));
        }
       
        return packageName.toString();
    }
   
    /**
     * Returns the name of the class generated for the given module.
     * @param moduleName the module name.
     * @param publicEntities whether only references to public entities are to be included in the generated class.
     * @return the name of the class generated for the given module.
     */
    public static final String getClassName (ModuleName moduleName, boolean publicEntities) {
        String unqualifiedModuleName = moduleName.getLastComponent();
        return JavaSourceGenerator.fixupClassName(publicEntities ? (BINDING_CLASS_PREFIX + unqualifiedModuleName) : (BINDING_CLASS_PREFIX + unqualifiedModuleName + PRIVATE_BINDING_SUFFIX));
    }
   
    /**
     * Create an instance of JavaBindingGenerator.
     * @param moduleTypeInfo
     * @param targetPackage
     * @param publicEntities
     */
    private JavaBindingGenerator (ModuleTypeInfo moduleTypeInfo, String targetPackage, boolean publicEntities) {
        this.moduleTypeInfo = moduleTypeInfo;
        this.module = moduleTypeInfo.getModule();
        this.publicEntities = publicEntities;

        // First create the top level class.
        // This will be in the package org.openquark.cal.module.
        // The binding class for public members will have the same name as the module.
        // The binding class for non-public members will be named with the name of the
        // module with "_internal" appended.
        this.bindingClassName =
            JavaBindingGenerator.getClassName(moduleTypeInfo.getModuleName(), publicEntities);
       
        this.bindingClassPackage = getPackageName(targetPackage, moduleTypeInfo.getModuleName());
       
        this.bindingClassTypeName =
            JavaTypeName.make(bindingClassPackage + "." + bindingClassName, false);

        this.moduleNameField = new JavaExpression.JavaField.Static (this.bindingClassTypeName, MODULE_NAME_FIELD_NAME, JavaTypeName.MODULE_NAME);
    }
   
    /**
     * Generate the Java model for the Java binding class for the named module.
     * @return The model of the generated class.
     */
    private final JavaClassRep buildJavaBindingModel () throws UnableToResolveForeignEntityException {
        // We want to generate a Java class that makes it easier to bind
        // client Java code to the Java code generated from the CAL source
        // in a safe fashion.
       
        // First create the top level class.
        // This will be in the package org.openquark.cal.module.
        // The binding class for public members will have the same name as the module.
        // The binding class for non-public members will be named with the name of the
        // module with "_internal" appended.
        JavaClassRep bindingClass =
            new JavaClassRep (bindingClassTypeName, JavaTypeName.OBJECT, Modifier.PUBLIC | Modifier.FINAL, EMPTY_TYPE_NAME_ARRAY);
       
        // We want to insert the CALDoc comment for the module as a comment for the class.
        JavaDocComment jdc;
        CALDocComment moduleComment = moduleTypeInfo.getCALDocComment();
        if (moduleComment != null) {
            String convertedComment = calDocCommentToJavaComment (moduleComment, null, true);
            jdc = new JavaDocComment (convertedComment);
        } else {
            // If there is no module comment from the CAL source we should just
            // create a generic one.
            jdc = new JavaDocComment ("This class (" + bindingClassName + ") provides Java constants, methods, etc.");
            jdc.addLine("which make it easier to bind client Java code to the Java code generated");
            jdc.addLine("from CAL source in a safe fashion");
        }
       
        bindingClass.setJavaDoc(jdc);

        // Add a comment for the top of the source file.
        MultiLineComment mlc = new MultiLineComment("<!--");
        mlc.addLine(" ");
        mlc.addLine("**************************************************************");
        mlc.addLine("This Java source has been automatically generated.");
        mlc.addLine("MODIFICATIONS TO THIS SOURCE MAY BE OVERWRITTEN - DO NOT MODIFY THIS FILE");
        mlc.addLine("**************************************************************");
        mlc.addLine(" ");
        mlc.addLine(" ");
        mlc.addLine("This file (" + bindingClass.getClassName().getUnqualifiedJavaSourceName() + ".java)");
        mlc.addLine("was generated from CAL module: " + module.getName() + ".");
        mlc.addLine("The constants and methods provided are intended to facilitate accessing the");
        mlc.addLine(module.getName() + " module from Java code.");
        mlc.addLine(" ");
        mlc.addLine("Creation date: " + new Date());
        mlc.addLine("--!>");
        mlc.addLine(" ");
        bindingClass.setComment(mlc);
       
        // Add the module name field.
        JavaFieldDeclaration moduleNameDeclaration =
            new JavaFieldDeclaration (PUBLIC_STATIC_FINAL, JavaTypeName.MODULE_NAME, MODULE_NAME_FIELD_NAME,
                new JavaExpression.MethodInvocation.Static(
                    JavaTypeName.MODULE_NAME,
                    "make",
                    new JavaExpression[]{JavaExpression.LiteralWrapper.make(moduleTypeInfo.getModuleName().toSourceText())},
                    new JavaTypeName[]{JavaTypeName.STRING},
                    JavaTypeName.MODULE_NAME));
       
        bindingClass.addFieldDeclaration(moduleNameDeclaration);
       
        // Add the inner class for type classes.
        buildTypeClassInnerClass(bindingClass, moduleTypeInfo, bindingClassName);
       
        // Add the inner class for type constructors.
        buildTypeConstructorsInnerClass(bindingClass, moduleTypeInfo, bindingClassName);
       
        // Add the inner class for data constructors.
        buildDataConstructorsInnerClass(bindingClass, moduleTypeInfo, bindingClassName);
       
        // Add the inner class for functions.
        buildFunctionsInnerClass(bindingClass, moduleTypeInfo, bindingClassName);
       
        int javaDocHash = computeJavaDocHash(bindingClass);
       
        JavaFieldDeclaration javaDocHashField =
            new JavaFieldDeclaration(PUBLIC_STATIC_FINAL, JavaTypeName.INT, JavaBindingGenerator.JAVADOC_HASH_FIELD_NAME, LiteralWrapper.make(Integer.valueOf(javaDocHash)));
        JavaDocComment javaDocHashFieldComment = new JavaDocComment("A hash of the concatenated JavaDoc for this class (including inner classes).");
        javaDocHashFieldComment.addLine("This value is used when checking for changes to generated binding classes.");
        javaDocHashField.setJavaDoc(javaDocHashFieldComment);
        bindingClass.addFieldDeclaration(javaDocHashField);
       
        return bindingClass;
    }
   
    /**
     * Generate the source code for the Java binding class for the named module.
     * @return The source of the generated class.
     */
    private final String buildJavaBinding () throws UnableToResolveForeignEntityException {
   
        JavaClassRep bindingClass = buildJavaBindingModel();
       
        // Generate the source.
        try {
            String source = JavaSourceGenerator.generateSourceCode(bindingClass);
            return source;
        } catch (JavaGenerationException e) {
            throw new NullPointerException(e.getLocalizedMessage());
        }
    }

    /**
     * Generate the inner class for constants and helper methods relating to CAL functions.
     * @param bindingClass
     * @param moduleTypeInfo
     * @param bindingClassName
     */
    private final void buildFunctionsInnerClass (
            JavaClassRep bindingClass,
            ModuleTypeInfo moduleTypeInfo,
            String bindingClassName) throws UnableToResolveForeignEntityException {
       
        if (moduleTypeInfo.getNFunctions() + moduleTypeInfo.getNTypeClasses() == 0) {
            return;
        }
       
        // Create the Functions class.
        JavaTypeName functionsClassTypeName = JavaTypeName.make (bindingClassPackage + "." + bindingClassName + "." + FUNCTION_CLASS_NAME, false);
        JavaClassRep functionsClass =
            new JavaClassRep (
                    functionsClassTypeName,
                    JavaTypeName.OBJECT,
                    PUBLIC_STATIC_FINAL,
                    EMPTY_TYPE_NAME_ARRAY);

        // Add the class JavaDoc
        JavaDocComment jdc = new JavaDocComment("This inner class (" + FUNCTION_CLASS_NAME + ") contains constants");
        jdc.addLine("and methods related to binding to CAL functions in the " + moduleTypeInfo.getModuleName() + " module.");
        functionsClass.setJavaDoc(jdc);
       
        // Build up and sort the list of function names.
        Map<String, FunctionalAgent> nameToFuncInfo = new TreeMap<String, FunctionalAgent>();
        for (int i = 0, n = moduleTypeInfo.getNFunctions(); i < n; ++i) {
            Function fe = moduleTypeInfo.getNthFunction(i);
            if (fe.getScope().isPublic() != publicEntities) {
                continue;
            }
            nameToFuncInfo.put(fe.getName().getUnqualifiedName(), fe);
        }
       
        for (int i = 0, n = moduleTypeInfo.getNTypeClasses(); i < n; ++i) {
            TypeClass tc = moduleTypeInfo.getNthTypeClass(i);

           
            for (int j = 0, k = tc.getNClassMethods(); j < k; ++j) {
                ClassMethod cm = tc.getNthClassMethod(j);
                if (cm.getScope().isPublic() == publicEntities) {
                    nameToFuncInfo.put(cm.getName().getUnqualifiedName(), cm);
                }
            }
        }

        if (nameToFuncInfo.size() == 0) {
            return;
        }
       
        bindingClass.addInnerClass(functionsClass);
       
       
        //  Now go over the function names and generate the helper functions and
        // QualifiedName constant.
        for (final Map.Entry<String, FunctionalAgent> entry : nameToFuncInfo.entrySet()) {
          
            String calFuncName = entry.getKey();
            FunctionalAgent functionalAgent = entry.getValue();

            CALDocComment cdc = functionalAgent.getCALDocComment();

            // We need to make sure the name for the helper function/qualified name field is
            // a valid java name.
            String javaFuncName = fixupVarName(calFuncName);
           
            // Add a binding method for the CAL function.
            // Essentially this method builds an application
            // of the function to the supplied arguments using our
            // source model.
           
            // Build up the argument names and types.
            TypeExpr te = functionalAgent.getTypeExpr();
            int nArgs = te.getArity();
            int nNamedArgs = functionalAgent.getNArgumentNames();
           
            String paramNames[] = CALDocToJavaDocUtilities.getArgumentNamesFromCALDocComment(cdc, functionalAgent);
            if (paramNames.length != nArgs)  {
                throw new NullPointerException ("Mismatch between arity and number of arguments for " + calFuncName);
            }
            String origParamNames[] = new String[paramNames.length];
            System.arraycopy(paramNames, 0, origParamNames, 0, origParamNames.length);
           
            JavaTypeName paramTypes[] = new JavaTypeName [paramNames.length];
           
            // If we have named arguments we should use those names.
            Set<String> paramNamesSet = new HashSet<String>();
            for (int i = 0; i < paramNames.length; ++i) {
                String name = paramNames[i];

                if (name == null) {
                    if (i < nNamedArgs) {
                        name = functionalAgent.getArgumentName(i);
                    }
                   
                    if (name == null || name.equals("")) {
                        name = null;
                        // There is no name for this argument in the functional entity.  This
                        // usually occurs with foreign functions and class methods because
                        // you don't have to explicitly name the arguments.
                       
                        // Check to see if there was a CALDoc comment that named the arguments.
                        if (cdc != null && cdc.getNArgBlocks() == paramNames.length) {
                            name = cdc.getNthArgBlock(i).getArgName().getCalSourceForm();
                        }
                       
                        // If we still don't have an argument name just build one.
                        if (name == null) {
                            name = "arg_" + (i+1);
                        }
                    }
                   
                    // Make sure name is unique.
                    String baseName = name;
                    int suffix = 0;
                    while (paramNamesSet.contains(name)) {
                        name = baseName + "_" + suffix++;
                    }
                }
               
                origParamNames[i] = name;
                paramNames[i] = fixupVarName(name);
                paramTypes[i] = SOURCE_MODEL_EXPR_TYPE_NAME;
            }
           
            // Get the JavaDoc for the helper function.  If there is CALDoc
            // for the function we translate it to JavaDoc. 
            JavaDocComment funcComment;
            if (cdc != null) {
                funcComment = new JavaDocComment(calDocCommentToJavaComment(cdc, functionalAgent, false));
                funcComment = fixupJavaDoc(funcComment, origParamNames, paramNames);
            } else {
                funcComment = new JavaDocComment("Helper binding method for function: " + calFuncName + ". ");
                for (int iName = 0; iName < paramNames.length; ++iName) {
                    funcComment.addLine("@param " + paramNames[iName]);
                }
                funcComment.addLine("@return the SourceModule.expr representing an application of " + calFuncName);
            }
           
           
            // Create the method.
            JavaMethod bindingMethod =
                new JavaMethod(PUBLIC_STATIC_FINAL,
                               SOURCE_MODEL_EXPR_TYPE_NAME,
                               paramNames,
                               paramTypes,
                               null, javaFuncName);
            functionsClass.addMethod(bindingMethod);
           
            // Add the JavaDoc
            bindingMethod.setJavaDocComment(funcComment);
           
            // Build up the body of the method.
           
            // We want to us the QualifiedName field.
            JavaField nameField = new JavaField.Static(functionsClassTypeName, javaFuncName, QUALIFIED_NAME_TYPE_NAME);
           
            // Create an instance of SourceModel.Expr.Var for the CAL function.
            JavaExpression sourceModelVarCreation =
                new MethodInvocation.Static(
                        SOURCE_MODEL_EXPR_VAR_TYPE_NAME,
                        "make",
                        nameField,
                        QUALIFIED_NAME_TYPE_NAME,
                        SOURCE_MODEL_EXPR_VAR_TYPE_NAME);
           
            // For the comment for this method we want an @see referring to the previous method.
            String atSee = "@see #" + javaFuncName + "(";
            for (int iArg = 0; iArg < paramNames.length; ++iArg) {
                if (iArg == 0) {
                    atSee = atSee + paramTypes[iArg].getFullJavaSourceName();
                } else {
                    atSee = atSee + ", " + paramTypes[iArg].getFullJavaSourceName();
                }
            }
            atSee = atSee + ")";
           
            if (paramNames.length == 0) {
                // Simply return the Expr.Var
                bindingMethod.addStatement(new ReturnStatement(sourceModelVarCreation));
            } else {
                // Need to create an application.
               
                // Create an array of SourceModel.Expr.  The first element is the SourceModel.Expr.Var
                // for the CAL function the remaining elements are the function arguments.
                JavaExpression arrayElements[] = new JavaExpression[paramNames.length + 1];
                arrayElements[0] = sourceModelVarCreation;
               
                for (int i = 1; i <= paramNames.length; ++i) {
                    arrayElements[i] = new JavaExpression.MethodVariable(paramNames[i-1]);
                }
               
                // Build the array creation expression.
                JavaExpression arrayCreation =
                    new JavaExpression.ArrayCreationExpression(SOURCE_MODEL_EXPR_TYPE_NAME, arrayElements);
               
                // Invoke SourceModel.Expr.Application.make()
                MethodInvocation makeApply =
                    new MethodInvocation.Static(
                            SOURCE_MODEL_EXPR_APPLICATION_TYPE_NAME,
                            "make",
                            arrayCreation,
                            JavaTypeName.makeArrayType(SOURCE_MODEL_EXPR_TYPE_NAME),
                            SOURCE_MODEL_EXPR_APPLICATION_TYPE_NAME);
               
                bindingMethod.addStatement(new ReturnStatement (makeApply));
           
                // If any of the argument types correspond to a Java type.
                // (eg. Prelude.Int, Prelude.Long, etc. we can generate a version
                // of the binding function that takes these argument types.
                boolean primArgs = false;

                TypeExpr[] pieces = te.getTypePieces();
                for (int i = 0; i < paramNames.length; ++i) {
                    if (canTypeBeUnboxed(pieces[i])) {
                        primArgs = true;
                        // Change the param type.
                        paramTypes[i] = typeExprToTypeName(pieces[i]);
                       
                        // Since the parameter will now be an int, boolean, long, etc. we
                        // need to create the appropriate SourceModel wrapper around the
                        // value.
                        arrayElements[i+1] = wrapArgument(paramNames[i], pieces[i]);
                    }
                }
               
                if (primArgs) {
                    // Create the alternate helper method.
                    bindingMethod =
                        new JavaMethod(PUBLIC_STATIC_FINAL,
                                       SOURCE_MODEL_EXPR_TYPE_NAME,
                                       paramNames,
                                       paramTypes,
                                       null, javaFuncName);
                    functionsClass.addMethod(bindingMethod);
                   
                    JavaStatement.JavaDocComment comment = new JavaStatement.JavaDocComment(atSee);
                    for (int iArg = 0; iArg < paramNames.length; ++iArg) {
                        comment.addLine("@param " + paramNames[iArg]);
                    }                       
                    comment.addLine("@return the SourceModel.Expr representing an application of " + calFuncName);
                    bindingMethod.setJavaDocComment(comment);
                   
                    arrayCreation =
                        new JavaExpression.ArrayCreationExpression(SOURCE_MODEL_EXPR_TYPE_NAME, arrayElements);
                   
                    makeApply =
                        new MethodInvocation.Static(
                                SOURCE_MODEL_EXPR_APPLICATION_TYPE_NAME,
                                "make",
                                arrayCreation,
                                JavaTypeName.makeArrayType(SOURCE_MODEL_EXPR_TYPE_NAME),
                                SOURCE_MODEL_EXPR_APPLICATION_TYPE_NAME);
                   
                    bindingMethod.addStatement(new ReturnStatement (makeApply));
   
                }
            }
           
            // Add a field for the function name.
            // 'static final QualifiedName functionName = "functionName";'
            // We need to check for conflict between the functionName
            // and java keywords.  It is valid to have a CAL
            // function called assert but not valid to have java code
            // 'static final Qualifiedname assert = ...
            JavaFieldDeclaration jfd =
                makeQualifiedNameDeclaration(javaFuncName, calFuncName);
            JavaDocComment qfComment = new JavaDocComment("Name binding for function: " + calFuncName + ".");
            qfComment.addLine(atSee);
            jfd.setJavaDoc(qfComment);

           
            functionsClass.addFieldDeclaration(jfd);
        }
    }
   
    /**
     * Fixup the @param tags in a JavaDoc comment.  This is sometimes necessary when the original
     * param name in CAL conflicts with a reserved/key word in java, resulting in the actual
     * param name being updated.  ex. char would be changed to char_.
     * @param comment
     * @param originalNames
     * @param updatedNames
     * @return the updated JavaDocComment
     */
    private final JavaDocComment fixupJavaDoc (JavaDocComment comment, String[] originalNames, String[] updatedNames) {

        List<String> lines = new ArrayList<String> ()
        lines.addAll(comment.getCommentLines());
      
        boolean hasReturn = false;
        boolean explicitComment = false;
        for (int i = 0; i < lines.size(); ++i) {
            String line = lines.get(i);
            if (i == 0 && line.trim().startsWith("/*")) {
                explicitComment = true;
            }
            if (line.indexOf("@return") >= 0) {
                hasReturn = true;
            }
            for (int iName = 0; iName < originalNames.length; ++iName) {
                String originalName = originalNames[iName];
                String updatedName = updatedNames[iName];
                if (originalName.equals(updatedName)) {
                    continue;
                }
               
                String find = "@param " + originalName + " ";
                String replace = "@param " + updatedName + " ";
                lines.set(i, line.replaceFirst(find, replace));
            }
        }

        if (!hasReturn) {
            if (explicitComment) {
                lines.add(lines.size()-1, " * @return SourceModel.Expr");
            } else {
                lines.add("@return SourceModel.Expr");
            }
        }
       
        return new JavaDocComment(lines);
    }
   
    /**
     * Build the TypeConstructors inner class.
     * @param bindingClass
     * @param moduleTypeInfo
     * @param bindingClassName
     */
    private final void buildTypeConstructorsInnerClass (
            JavaClassRep bindingClass,
            ModuleTypeInfo moduleTypeInfo,
            String bindingClassName) {
       
        if (moduleTypeInfo.getNTypeConstructors() == 0) {
            return;
        }
       
        // Create the inner class.
        JavaTypeName typeConstructorsClassTypeName = JavaTypeName.make (bindingClassPackage + "." + bindingClassName + "." + TYPECONSTRUCTOR_CLASS_NAME, false);
        JavaClassRep typeConstructorsClass =
            new JavaClassRep (
                    typeConstructorsClassTypeName,
                    JavaTypeName.OBJECT,
                    PUBLIC_STATIC_FINAL,
                    EMPTY_TYPE_NAME_ARRAY);
       
       
        // Create the JavaDoc for the inner class.
        JavaDocComment jdc = new JavaDocComment("This inner class (" + TYPECONSTRUCTOR_CLASS_NAME + ") contains constants");
        jdc.addLine("and methods related to binding to CAL TypeConstructors in the " + moduleTypeInfo.getModuleName() + " module.");
        typeConstructorsClass.setJavaDoc(jdc);

        // Build up a list of type constructors and sort by name.
        List<String> typeConstructorNames = new ArrayList<String>();
        for (int i = 0, n = moduleTypeInfo.getNTypeConstructors(); i < n; ++i) {
            TypeConstructor typeCons = moduleTypeInfo.getNthTypeConstructor(i);
            if (typeCons.getScope().isPublic() != publicEntities) {
                continue;
            }
            typeConstructorNames.add(typeCons.getName().getUnqualifiedName());
        }
        Collections.sort(typeConstructorNames);
       
        if (typeConstructorNames.size() == 0) {
            return;
        }
       
        bindingClass.addInnerClass(typeConstructorsClass);
       
        // For each type constructor create a QualifiedName field.
        // The field will have the same name as the type constructor.
        for (final String typeConstructorName : typeConstructorNames) {
           
            TypeConstructor typeCons = moduleTypeInfo.getTypeConstructor(typeConstructorName);
            // Add a field for the typeConstructor name.
            // 'static final QualifiedName typeConstructorName = QualifiedName.make(moduleName, typeConstructorName);
            // We need to check for conflict between the functionName
            // and java keywords.  It is valid to have a CAL
            // function called assert but not valid to have java code
            // 'static final String assert = "assert";
            String fieldName = fixupVarName(typeConstructorName);
           
            // Since TypeConsApp names are capitalized it is possible to have
            // a conflict between the name of the field and the name of the top
            // level class.
            if (fieldName.equals(this.bindingClassName)) {
                fieldName = fieldName + "_";
            }
           
            JavaFieldDeclaration jfd =
                makeQualifiedNameDeclaration(fieldName, typeConstructorName);

            // Add JavaDoc for the field declaration.  We use the CALDoc if there is any.
            JavaDocComment typeConstructorComment;
            CALDocComment cdc = typeCons.getCALDocComment();
            if (cdc != null) {
                typeConstructorComment = new JavaDocComment(calDocCommentToJavaComment(cdc, null, false));
            } else {
                typeConstructorComment = new JavaDocComment("/** Name binding for TypeConsApp: " + typeConstructorName + ". */");
            }
            jfd.setJavaDoc(typeConstructorComment);
           
            typeConstructorsClass.addFieldDeclaration(jfd);
           
        }
    }
   
    /**
     * Create the inner class for data constructors.
     * @param bindingClass
     * @param moduleTypeInfo
     * @param bindingClassName
     */
    private final void buildDataConstructorsInnerClass (
            JavaClassRep bindingClass,
            ModuleTypeInfo moduleTypeInfo,
            String bindingClassName) throws UnableToResolveForeignEntityException {
       
        if (moduleTypeInfo.getNTypeConstructors() == 0) {
            return;
        }
       
        // Create the class.
        JavaTypeName dataConstructorsClassTypeName = JavaTypeName.make (bindingClassPackage + "." + bindingClassName + "." + DATA_CONSTRUCTOR_CLASS_NAME, false);
        JavaClassRep dataConstructorsClass =
            new JavaClassRep (
                    dataConstructorsClassTypeName,
                    JavaTypeName.OBJECT,
                    PUBLIC_STATIC_FINAL,
                    EMPTY_TYPE_NAME_ARRAY);
              

       
        // Add the class JavaDoc.
        JavaDocComment jdc = new JavaDocComment("/**");
        jdc.addLine(" * This inner class (" + DATA_CONSTRUCTOR_CLASS_NAME + ") contains constants");
        jdc.addLine(" * and methods related to binding to CAL DataConstructors in the " + moduleTypeInfo.getModuleName() + " module.");
        jdc.addLine(" */");
        dataConstructorsClass.setJavaDoc(jdc);

        // We want to build up and sort a list of type constructor names.
        List<String> typeConstructorNames = new ArrayList<String>();
        for (int i = 0, n = moduleTypeInfo.getNTypeConstructors(); i < n; ++i) {
            TypeConstructor typeCons = moduleTypeInfo.getNthTypeConstructor(i);
            typeConstructorNames.add(typeCons.getName().getUnqualifiedName());
        }
        Collections.sort(typeConstructorNames);

       
        /*
         * Create a method to generate a SourceModel expression which will result
         * in the creation of an instance of the DC.
         * Create a QualifiedName field for the name of the DC.
         *
         * The ordering will be by name of the type constructor and then by dc ordinal.
         */
        boolean addClass = false;
       
        // Set of String used to avoid naming conflicts in the generated fields.
        // We want to build up a set of all the names that will be used for the methods and
        // QualifiedName fields.  This will allow us to avoid naming conflicts when creating
        // the ordinal fields.
        Set<String> javaNames = new HashSet<String>();
        for (final String typeConstructorName : typeConstructorNames) {
            TypeConstructor typeCons = moduleTypeInfo.getTypeConstructor(typeConstructorName);
            for (int i = 0, n = typeCons.getNDataConstructors(); i < n; ++i) {
                DataConstructor dc = typeCons.getNthDataConstructor(i);
               
                // If we are showing public entities we only car about public data constructors.
                if (dc.getScope().isPublic() != publicEntities) {
                    continue;
                }

                // This is the name used to name the java function and the QualfiedName field
                // associated with this data constructor.
                String javaFuncName = fixupVarName(dc.getName().getUnqualifiedName());
                javaNames.add(javaFuncName);
            }
        }
       
        for (final String typeConstructorName : typeConstructorNames) {
            TypeConstructor typeCons = moduleTypeInfo.getTypeConstructor(typeConstructorName);
            boolean commentAdded = false;
            for (int i = 0, n = typeCons.getNDataConstructors(); i < n; ++i) {
                DataConstructor dc = typeCons.getNthDataConstructor(i);
               
                // If we are showing public entities we only car about public data constructors.
                if (dc.getScope().isPublic() != publicEntities) {
                    continue;
                }
               
                addClass = true;
               
                // If this is the first DC for the data type we want a non JavaDoc comment
                // indicating the data type.
                if (!commentAdded) {
                    MultiLineComment mlc = new MultiLineComment("DataConstructors for the " + typeCons.getName().getQualifiedName() + " data type.");
                    dataConstructorsClass.addComment(mlc);
                    commentAdded = true;
                }

                // Get the types of the data constructor fields.
                TypeExpr[] fieldTypes = getFieldTypesForDC(dc);
               
               
                String javaFuncName = fixupVarName(dc.getName().getUnqualifiedName());
               
                // Since data constructors are capitalized its possible to have a conflict between the name
                // of our field/helper function and the name of the containing class.
                if (javaFuncName.equals(this.bindingClassName)) {
                    javaFuncName = javaFuncName + "_";
                }
               
                // First generate a method that takes SourceModel.Expr instances for
                // each field value.
               
                // Build up the argument names and types.
                int nArgs = dc.getArity();
                JavaTypeName argTypes[] = new JavaTypeName[nArgs];
                Arrays.fill(argTypes, SOURCE_MODEL_EXPR_TYPE_NAME);
                String argNames[] = new String[nArgs];
                String origArgNames[] = new String[nArgs];
                for (int j = 0, k = argNames.length; j < k; ++j) {
                    argNames[j] = fixupVarName(dc.getArgumentName(j));
                    origArgNames[j] = dc.getArgumentName(j);
                }
               
                // Create the method.
                JavaMethod bindingFunction =
                    new JavaMethod(PUBLIC_STATIC_FINAL,
                                   SOURCE_MODEL_EXPR_TYPE_NAME,
                                   argNames,
                                   argTypes,
                                   null, javaFuncName);
                dataConstructorsClass.addMethod(bindingFunction);
               
                // Add JavaDoc for the method.
                JavaDocComment funcComment;
                CALDocComment cdc = dc.getCALDocComment();
                if (cdc != null) {
                    funcComment = new JavaDocComment(calDocCommentToJavaComment(cdc, dc, false, argNames));
                    funcComment = fixupJavaDoc(funcComment, origArgNames, argNames);
                } else {
                    funcComment = new JavaDocComment("Binding for DataConstructor: " + dc.getName().getQualifiedName() + ".");
                    for (int iName = 0; iName < argNames.length; ++iName) {
                        funcComment.addLine("@param " + argNames[iName]);
                    }
                    funcComment.addLine("@return the SourceModule.Expr representing an application of " + dc.getName().getQualifiedName());
                }
                bindingFunction.setJavaDocComment(funcComment);
               
                // Now we need to fill in the body.
               
                // Create an instance of SourceModel.Expr.DataCons for the data constructor.
                JavaField nameField = new JavaField.Static(dataConstructorsClassTypeName, javaFuncName, QUALIFIED_NAME_TYPE_NAME);
                JavaExpression sourceModelDataConsCreation =
                    new MethodInvocation.Static(
                            SOURCE_MODEL_EXPR_DATA_CONS_TYPE_NAME,
                            "make",
                            nameField,
                            QUALIFIED_NAME_TYPE_NAME,
                            SOURCE_MODEL_EXPR_DATA_CONS_TYPE_NAME);
               
                // Build up an @see tag for the function just created.
                String atSee = "@see #" + javaFuncName + "(";
                for (int iArg = 0; iArg < argNames.length; ++iArg) {
                    if (iArg == 0) {
                        atSee = atSee + argTypes[iArg].getFullJavaSourceName();
                    } else {
                        atSee = atSee + ", " + argTypes[iArg].getFullJavaSourceName();
                    }
                }
                atSee = atSee + ")";
               
                if (dc.getArity() == 0) {
                    // Simply return the Expr.DataCons.
                    bindingFunction.addStatement(new ReturnStatement(sourceModelDataConsCreation));
                } else {
                    // Need to build up an application.
                   
                    // Create an array of SourceModel.Expr where the first element is the
                    // SourceModel.Expr.DataCons instance and the following elements are
                    // the function arguments.
                    JavaExpression arrayElements[] = new JavaExpression[dc.getArity() + 1];
                    arrayElements[0] = sourceModelDataConsCreation;
                   
                    for (int j = 1; j <= argNames.length; ++j) {
                        arrayElements[j] = new JavaExpression.MethodVariable(argNames[j-1]);
                    }
                   
                    JavaExpression arrayCreation =
                        new JavaExpression.ArrayCreationExpression(SOURCE_MODEL_EXPR_TYPE_NAME, arrayElements);
                   
                    // Invoke SourceModle.Expr.Application.make()
                    MethodInvocation makeApply =
                        new MethodInvocation.Static(
                                SOURCE_MODEL_EXPR_APPLICATION_TYPE_NAME,
                                "make",
                                arrayCreation,
                                JavaTypeName.makeArrayType(SOURCE_MODEL_EXPR_TYPE_NAME),
                                SOURCE_MODEL_EXPR_APPLICATION_TYPE_NAME);
                   
                    bindingFunction.addStatement(new ReturnStatement (makeApply));
               
                    // If any of the argument types correspond to a Java type.
                    // (eg. Prelude.Int, Prelude.Long, etc. we can generate a version
                    // of the binding function that takes these argument types.
                    boolean primArgs = false;
                    for (int j = 0, k = fieldTypes.length; j < k; ++j) {
                        if (canTypeBeUnboxed(fieldTypes[j])) {
                            primArgs = true;
                            // Update the type of the argument.
                            argTypes[j] = typeExprToTypeName(fieldTypes[j]);
                           
                            // The argument to Application.make needs to be updated.
                            // We need to wrap the raw value (i.e. int, boolean, etc.)
                            // in the appropriate SourceModel construct.
                            arrayElements[j+1] = wrapArgument(argNames[j], fieldTypes[j]);
                        }
                    }

                   
                    if (primArgs) {
                        bindingFunction =
                            new JavaMethod(PUBLIC_STATIC_FINAL,
                                           SOURCE_MODEL_EXPR_TYPE_NAME,
                                           argNames,
                                           argTypes,
                                           null, javaFuncName);
                        dataConstructorsClass.addMethod(bindingFunction);
                       
                        // For the comment for this method we want an @see referring to the previous method.
                       
                        JavaStatement.JavaDocComment comment = new JavaStatement.JavaDocComment(atSee);
                        for (int iArg = 0; iArg < argNames.length; ++iArg) {
                            comment.addLine("@param " + argNames[iArg]);
                        }                       
                        comment.addLine("@return " + SOURCE_MODEL_EXPR_TYPE_NAME.getFullJavaSourceName());
                        bindingFunction.setJavaDocComment(comment);
                       
                        arrayCreation =
                            new JavaExpression.ArrayCreationExpression(SOURCE_MODEL_EXPR_TYPE_NAME, arrayElements);
                       
                        makeApply =
                            new MethodInvocation.Static(
                                    SOURCE_MODEL_EXPR_APPLICATION_TYPE_NAME,
                                    "make",
                                    arrayCreation,
                                    JavaTypeName.makeArrayType(SOURCE_MODEL_EXPR_TYPE_NAME),
                                    SOURCE_MODEL_EXPR_APPLICATION_TYPE_NAME);
                       
                        bindingFunction.addStatement(new ReturnStatement (makeApply));
       
                    }
                   
                }
               
                // Create a field declaration for the QualifiedName field.
                JavaFieldDeclaration jfd =
                    makeQualifiedNameDeclaration(javaFuncName, dc.getName().getUnqualifiedName());
               
                // If there is CALDoc for the DC add it as JavaDoc.
                JavaDocComment comment = new JavaDocComment("Name binding for DataConstructor: " + dc.getName().getQualifiedName() + ".");
                comment.addLine(atSee);
               
                jfd.setJavaDoc(comment);
               
                dataConstructorsClass.addFieldDeclaration(jfd);               
            
                // Now add an int field which is the ordinal of the data constructor.
                String ordinalFieldName = javaFuncName + "_ordinal";
                while (javaNames.contains(ordinalFieldName)) {
                    ordinalFieldName = ordinalFieldName + "_";
                }
                JavaFieldDeclaration ordinalFieldDec =
                    new JavaFieldDeclaration(PUBLIC_STATIC_FINAL, JavaTypeName.INT, ordinalFieldName, JavaExpression.LiteralWrapper.make(Integer.valueOf(dc.getOrdinal())));
                javaNames.add(ordinalFieldName);
                JavaDocComment ordinalComment = new JavaDocComment("Ordinal of DataConstructor " + dc.getName().getQualifiedName() + ".");
                ordinalComment.addLine(atSee);
                ordinalFieldDec.setJavaDoc(ordinalComment);
               
                dataConstructorsClass.addFieldDeclaration(ordinalFieldDec);
            }
        }
       
        if (addClass) {
            bindingClass.addInnerClass(dataConstructorsClass);
        }
    }
   
    /**
     * Build the inner class for TypeClasses
     * @param bindingClass
     * @param moduleTypeInfo
     * @param bindingClassName
     */
    private final void buildTypeClassInnerClass (
            JavaClassRep bindingClass,
            ModuleTypeInfo moduleTypeInfo,
            String bindingClassName) {
       
        if (moduleTypeInfo.getNTypeClasses() == 0) {
            return;
        }
       
        // Create the inner class.
        JavaTypeName typeClassClassTypeName =
            JavaTypeName.make (bindingClassPackage + "." + bindingClassName + "." + TYPECLASS_CLASS_NAME, false);
       
        JavaClassRep typeClassClass =
            new JavaClassRep (
                    typeClassClassTypeName,
                    JavaTypeName.OBJECT,
                    PUBLIC_STATIC_FINAL,
                    EMPTY_TYPE_NAME_ARRAY);
               
       
        // Set the JavaDoc for the class.
        JavaDocComment jdc = new JavaDocComment("This inner class (" + TYPECLASS_CLASS_NAME + ") contains constants");
        jdc.addLine("and methods related to binding to CAL TypeClasses in the " + moduleTypeInfo.getModuleName() + " module.");
        typeClassClass.setJavaDoc(jdc);

        // Build up a list of TypeClass names and sort.
        List<String> typeClassNames = new ArrayList<String>();
        for (int i = 0, n = moduleTypeInfo.getNTypeClasses(); i < n; ++i) {
            TypeClass tc = moduleTypeInfo.getNthTypeClass(i);
            if (tc.getScope().isPublic() != publicEntities) {
                continue;
            }
            typeClassNames.add(tc.getName().getUnqualifiedName());
        }
        Collections.sort(typeClassNames);
       
        if (typeClassNames.size() == 0) {
            return;
        }
       
        bindingClass.addInnerClass(typeClassClass);

        // Generate a QualifiedName field for each type class.
        for (final String typeClassName : typeClassNames) {
            TypeClass tc = moduleTypeInfo.getTypeClass(typeClassName);
            // Add a field for the TypeClass name.
            // 'static final String typeClassName = "typeClassName";'
            // We need to check for conflict between the functionName
            // and java keywords.  It is valid to have a CAL
            // function called assert but not valid to have java code
            // 'static final QualifiedName assert = ...
            String fieldName = fixupVarName(typeClassName);
           
            // Since TypeClass names are capitalized it's possible to have a conflict
            // between the name of the field and the name of the top level class.
            if (fieldName.equals(this.bindingClassName)) {
                fieldName = fieldName + "_";
            }
           
            JavaFieldDeclaration jfd =
                makeQualifiedNameDeclaration(fieldName, typeClassName);

            // Add JavaDoc.  We use any CALDoc for the type class if available.
            JavaDocComment comment;
            CALDocComment cdc = tc.getCALDocComment();
            if (cdc != null) {
                comment = new JavaDocComment(calDocCommentToJavaComment(cdc, null, false));
            } else {
                comment = new JavaDocComment("/** Name binding for TypeClass: " + tc.getName().getQualifiedName() + ". */");
            }
            jfd.setJavaDoc(comment);
           
            typeClassClass.addFieldDeclaration(jfd);
        }
    }
   
    /**
     * Convert a CALDocComment instance into JavaDoc.
     * @param comment
     * @param bindingEntity
     * @param isClassComment whether the Javadoc comment is a class comment.
     * @return the text of the JavaDoc comment.
     */
    private final String calDocCommentToJavaComment (CALDocComment comment, FunctionalAgent bindingEntity, boolean isClassComment) {
        return calDocCommentToJavaComment(comment, bindingEntity, isClassComment, null);
    }
   
    /**
     * Convert a CALDocComment instance into JavaDoc.
     * @param comment
     * @param bindingEntity
     * @param isClassComment whether the Javadoc comment is a class comment.
     * @param argNames the names of arguments to use. Can be null if the default mechanism for obtaining argument names is to be used.
     * @return the text of the JavaDoc comment.
     */
    private final String calDocCommentToJavaComment (CALDocComment comment, FunctionalAgent bindingEntity, boolean isClassComment, String[] argNames) {
        String text = CALDocToJavaDocUtilities.getJavadocFromCALDocComment(comment, isClassComment, bindingEntity, argNames);
        return text;
    }
   
    /**
     * Generate a JavaFieldDeclaration for a QualifiedName field.
     * @param fieldName
     * @param entityName
     * @return the field declaration.
     */
    private final JavaFieldDeclaration makeQualifiedNameDeclaration (String fieldName, String entityName) {
        JavaExpression initializer =
            new JavaExpression.MethodInvocation.Static (
                    QUALIFIED_NAME_TYPE_NAME,
                    "make",
                    new JavaExpression[]{moduleNameField, LiteralWrapper.make(entityName)},
                    new JavaTypeName[]{JavaTypeName.MODULE_NAME, JavaTypeName.STRING},
                    QUALIFIED_NAME_TYPE_NAME);
       
        return new JavaFieldDeclaration(PUBLIC_STATIC_FINAL, QUALIFIED_NAME_TYPE_NAME, fieldName, initializer);
    }
   
    /**
     * Returns true if the provided type expression corresponds
     * to an unboxable type.  Currently these types consist of
     * the CAL types corresponding to Java primitive types, excluding
     * void, and java.lang.String.
     * @param typeExpr
     * @return true if the type can be unboxed.
     */
    static private boolean canTypeBeUnboxed (TypeExpr typeExpr) throws UnableToResolveForeignEntityException {
        if (typeExpr == null) {
            return false;
        }
       
        TypeConsApp typeConsApp = typeExpr.rootTypeConsApp();
        if (typeConsApp != null && typeConsApp.getNArgs() == 0) {
       
            if (typeConsApp.isNonParametricType(CAL_Prelude.TypeConstructors.Boolean)) {               
                return true;
            }
   
            if(typeConsApp.getForeignTypeInfo() != null) {
                ForeignTypeInfo fti = typeConsApp.getForeignTypeInfo();
                Class<?> foreignClass = fti.getForeignType();
                if(foreignClass.isPrimitive() && foreignClass != void.class) {
                    return true;
                }
               
                if (foreignClass == java.lang.String.class) {
                    return true;
                }
            }              
        }
       
        return false;
    }
   
    /**
     * Generate the JavaModel which will wrap an unboxed value in the
     * appropriate SourceModel construct.
     * @param argName
     * @param argType
     * @return the JavaExpression which wraps the unboxed value.
     */
    static private JavaExpression wrapArgument (String argName, TypeExpr argType) throws UnableToResolveForeignEntityException {
        TypeConsApp typeConsApp = argType.rootTypeConsApp();
        if (typeConsApp != null && typeConsApp.getNArgs() == 0) {
           
            // boolean
            if (typeConsApp.isNonParametricType(CAL_Prelude.TypeConstructors.Boolean)) {               
                JavaExpression makeBoolean =
                    new MethodInvocation.Static(
                            SOURCE_MODEL_EXPR_TYPE_NAME,
                            "makeBooleanValue",
                            new MethodVariable(argName),
                            JavaTypeName.BOOLEAN,
                            SOURCE_MODEL_EXPR_TYPE_NAME);
               
                return makeBoolean;
            }

            if(typeConsApp.getForeignTypeInfo() != null) {
                ForeignTypeInfo fti = typeConsApp.getForeignTypeInfo();
                Class<?> foreignClass = fti.getForeignType();
               
                // We handle the primitive Java types, excluding void.
                if(foreignClass.isPrimitive() && foreignClass != void.class) {
                    MethodVariable mv = new MethodVariable(argName);
                    String funcName;
                    JavaTypeName valueType;
                    if (foreignClass == boolean.class) {
                        funcName = "makeBooleanValue";
                        valueType = JavaTypeName.BOOLEAN;
                    } else if (foreignClass == byte.class) {
                        funcName = "makeByteValue";
                        valueType = JavaTypeName.BYTE;
                    } else if (foreignClass == char.class) {
                        funcName = "makeCharValue";
                        valueType = JavaTypeName.CHAR;
                    } else if (foreignClass == double.class) {
                        funcName = "makeDoubleValue";
                        valueType = JavaTypeName.DOUBLE;
                    } else if (foreignClass == float.class) {
                        funcName = "makeFloatValue";
                        valueType = JavaTypeName.FLOAT;
                    } else if (foreignClass == int.class) {
                        funcName = "makeIntValue";
                        valueType = JavaTypeName.INT;
                    } else if (foreignClass == long.class) {
                        funcName = "makeLongValue";
                        valueType = JavaTypeName.LONG;
                    } else if (foreignClass == short.class) {
                        funcName = "makeShortValue";
                        valueType = JavaTypeName.SHORT;
                    } else {
                        throw new IllegalStateException("Unhandled primitive type " + foreignClass.getName());
                    }
                   
                    JavaExpression makeValue =
                        new MethodInvocation.Static(
                                SOURCE_MODEL_EXPR_TYPE_NAME,
                                funcName,
                                mv,
                                valueType,
                                SOURCE_MODEL_EXPR_TYPE_NAME);
                   
                    return makeValue;
                }
               
                // Handle java.lang.String.
                if (foreignClass == java.lang.String.class) {
                    return new MethodInvocation.Static(
                                    SOURCE_MODEL_EXPR_TYPE_NAME,
                                    "makeStringValue",
                                    new MethodVariable(argName),
                                    JavaTypeName.STRING,
                                    SOURCE_MODEL_EXPR_TYPE_NAME);
                }
            }              
        }
       
        // Not a type we handle.
        throw new IllegalStateException("Unable to wrap argument of type: " + argType.toString());
    }

    /**
     * Translate a TypeExpr to the corresponding JavaTypeName.
     * @param argType
     * @return the JavaTypeName.
     */
    private final static JavaTypeName typeExprToTypeName (TypeExpr argType) throws UnableToResolveForeignEntityException {
        TypeConsApp typeConsApp = argType.rootTypeConsApp();
        if (typeConsApp != null && typeConsApp.getNArgs() == 0) {
            if (typeConsApp.isNonParametricType(CAL_Prelude.TypeConstructors.Boolean)) {
                return JavaTypeName.BOOLEAN;
            }

            if(typeConsApp.getForeignTypeInfo() != null) {
                ForeignTypeInfo fti = typeConsApp.getForeignTypeInfo();
                Class<?> foreignClass = fti.getForeignType();
                if(foreignClass.isPrimitive() && foreignClass != void.class) {
                    if (foreignClass == boolean.class) {
                        return JavaTypeName.BOOLEAN;
                    } else if (foreignClass == byte.class) {
                        return JavaTypeName.BYTE;
                    } else if (foreignClass == char.class) {
                        return JavaTypeName.CHAR;
                    } else if (foreignClass == double.class) {
                        return JavaTypeName.DOUBLE;
                    } else if (foreignClass == float.class) {
                        return JavaTypeName.FLOAT;
                    } else if (foreignClass == int.class) {
                        return JavaTypeName.INT;
                    } else if (foreignClass == long.class) {
                        return JavaTypeName.LONG;
                    } else if (foreignClass == short.class) {
                        return JavaTypeName.SHORT;
                    } else {
                        throw new IllegalStateException("Unhandled primitive type " + foreignClass.getName());
                    }
                }
               
                if (foreignClass == java.lang.String.class) {
                    return JavaTypeName.STRING;
                }
            }              
        }
       
        throw new IllegalStateException("Unable to convert type: " + argType.toString());
       
    }
   
    /**
     * Use this function to get the field types for a data constructor.
     * Appropriate conversions are done based on whether or not unboxed DC fields are being used
     * and whether enums are treated as ints.
     * @param dc
     * @return an array of TypeExpr
     */
    private static TypeExpr[] getFieldTypesForDC (DataConstructor dc) {
        TypeExpr[] fieldTypes = new TypeExpr[dc.getArity()];
        TypeExpr dcType = dc.getTypeExpr();
        TypeExpr[] tft = dcType.getTypePieces(dc.getArity());
        System.arraycopy(tft, 0, fieldTypes, 0, fieldTypes.length);

        return fieldTypes;
    }
   
    /**
     * When deconstructing a data type the names assigned to the members
     * can have conflicts with java reserved words.  We fix this by
     * prepending a '$'.  The '$' is used to avoid creating a new conflict
     * with another CAL variable.
     * @param varName
     * @return String
     */
    private static String fixupVarName(String varName) {
        if (JavaReservedWords.javaLanguageKeywords.contains(varName)) {
            return varName + "_";
        }
       
        if (varName.equals("QualifiedName")) {
            varName = varName + "_";
        }
        varName = varName.replace ('.', '_');
        varName = varName.replace ('#', '_');
       
        return varName;
    }
   
    /**
     * @param classRep
     * @return a has code for the concatenated JavaDoc in the classRep.
     */
    private int computeJavaDocHash (JavaClassRep classRep){
        // We want to traverse the class collecting all the
        // JavaDoc comments.
       
        JavaDocCollector jdc = new JavaDocCollector();
        List<JavaDocComment> comments = new ArrayList<JavaDocComment>();
        classRep.accept(jdc, comments);
       
        StringBuilder sb = new StringBuilder();
        for (final JavaDocComment comment : comments) {
          
            for (final String line : comment.getCommentLines()) {
                sb.append(line);
            }
        }
       
        return sb.toString().hashCode();
    }
   
    private static class JavaBindingClassChecker extends JavaModelTraverser<Void, Void> {
        /** Map of String -> JavaFieldDeclaration.  Data constructor QualifiedName binding fields.*/
        Map<String, JavaFieldDeclaration> dataConsFields = new HashMap<String, JavaFieldDeclaration>();
       
        /** Map of String -> JavaFieldDeclaration.  DataConstructor ordinal value fields. */
        Map<String, JavaFieldDeclaration> dataConsOrdinalFields = new HashMap<String, JavaFieldDeclaration>();
       
        /** Set of JavaMethod. Methods in the DC inner class. */
        Set<JavaMethod> dataConsFunctions = new HashSet<JavaMethod>();
       
        /** Map of String -> JavaFieldDeclaration.  Function QualifiedName binding fields. */
        Map<String, JavaFieldDeclaration> functionFields = new HashMap<String, JavaFieldDeclaration>();
       
        /** Set of JavaMethod. Helper methods in the Functions inner class. */
        Set<JavaMethod> functions = new HashSet<JavaMethod>();
       
        /** Map of String -> JavaFieldDeclaration.  TypeClass QualifiedName binding fields. */
        Map<String, JavaFieldDeclaration> typeClassFields = new HashMap<String, JavaFieldDeclaration>();
       
        /** Map of String -> JavaFieldDeclaration.  TypeConstructor QualfiedName binding Fields. */
        Map<String, JavaFieldDeclaration> typeConstructorFields = new HashMap<String, JavaFieldDeclaration>();
       
        Map<String, JavaFieldDeclaration> currentFieldSet;
        Set<JavaMethod> currentFunctionSet;
        Map<String, JavaFieldDeclaration> currentIntFieldSet;
       
        /**
         * @param fieldDeclaration
         *          the java model element to be visited
         * @param arg
         *          additional argument for the visitation
         * @return the result from visiting the java model element
         */
        @Override
        public Void visitJavaFieldDeclaration (
                JavaFieldDeclaration fieldDeclaration, Void arg) {
            if (currentFieldSet != null && fieldDeclaration.getFieldType().equals(JavaTypeName.QUALIFIED_NAME)) {
                    currentFieldSet.put(fieldDeclaration.getFieldName(), fieldDeclaration);
            }
           
            if (currentIntFieldSet != null && fieldDeclaration.getFieldType().equals(JavaTypeName.INT)) {
                currentIntFieldSet.put(fieldDeclaration.getFieldName(), fieldDeclaration);
            }
            return null;
        }
       
        /**
         * @param javaMethod
         *          the java model element to be visited
         * @param arg
         *          additional argument for the visitation
         * @return the result from visiting the java model element
         */
        @Override
        public Void visitJavaMethod (
                JavaMethod javaMethod, Void arg) {
            if (currentFunctionSet != null) {
                currentFunctionSet.add(javaMethod);
            }
            return super.visitJavaMethod(javaMethod, arg);
        }
    
        /**
         * @param classRep
         *          the java model element to be visited
         * @param arg
         *          additional argument for the visitation
         * @return the result from visiting the java model element
         */
        @Override
        public Void visitJavaClassRep (
                JavaClassRep classRep, Void arg) {
           
            String className = classRep.getClassName().getUnqualifiedJavaSourceName();
            if (className.indexOf("$") >= 0) {
                className = className.substring(className.lastIndexOf("$") + 1);
            }
           
            if (className.equals(FUNCTION_CLASS_NAME)) {
                currentFieldSet = functionFields;
                currentFunctionSet = functions;
                currentIntFieldSet = null;
            } else
            if (className.equals(DATA_CONSTRUCTOR_CLASS_NAME)) {
                currentFieldSet = dataConsFields;
                currentFunctionSet = dataConsFunctions;
                currentIntFieldSet = dataConsOrdinalFields;
            } else
            if (className.equals(TYPECLASS_CLASS_NAME)) {
                currentFieldSet = typeClassFields;
                currentFunctionSet = null;
                currentIntFieldSet = null;
            } else
            if (className.equals(TYPECONSTRUCTOR_CLASS_NAME)) {
                currentFieldSet = typeConstructorFields;
                currentFunctionSet = null;
                currentIntFieldSet = null;
            } else {
                currentFieldSet = null;
                currentFunctionSet = null;
                currentIntFieldSet = null;
            }
           
            return super.visitJavaClassRep(classRep, arg);
        }
    }
   
    private static class JavaDocCollector extends JavaModelTraverser<List<JavaDocComment>, Void> {
        /* (non-Javadoc)
         * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitJavaDocCommentStatement(org.openquark.cal.internal.runtime.lecc.JavaStatement.JavaDocComment, java.lang.Object)
         */
        @Override
        public Void visitJavaDocCommentStatement(
                JavaStatement.JavaDocComment comment, List<JavaDocComment> arg) {
            arg.add(comment);
            return null;
        }
    }

}
TOP

Related Classes of org.openquark.cal.internal.javamodel.JavaBindingGenerator$JavaBindingClassChecker

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.