/*
* 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.
*/
/*
* IO_Source_Generator.java
*
* Creation date: May 17, 2007
* By: Raymond Cypher
*/
package org.openquark.cal.tools.iosourcegenerator;
import java.lang.reflect.Modifier;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import org.openquark.cal.compiler.ClassInstance;
import org.openquark.cal.compiler.DataConstructor;
import org.openquark.cal.compiler.FieldName;
import org.openquark.cal.compiler.ForeignTypeInfo;
import org.openquark.cal.compiler.Function;
import org.openquark.cal.compiler.ModuleName;
import org.openquark.cal.compiler.ModuleTypeInfo;
import org.openquark.cal.compiler.QualifiedName;
import org.openquark.cal.compiler.RecordType;
import org.openquark.cal.compiler.Scope;
import org.openquark.cal.compiler.SourceModel;
import org.openquark.cal.compiler.SourceModelCopier;
import org.openquark.cal.compiler.TypeApp;
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.compiler.SourceModel.CALDoc;
import org.openquark.cal.compiler.SourceModel.Expr;
import org.openquark.cal.compiler.SourceModel.Friend;
import org.openquark.cal.compiler.SourceModel.FunctionDefn;
import org.openquark.cal.compiler.SourceModel.FunctionTypeDeclaration;
import org.openquark.cal.compiler.SourceModel.Import;
import org.openquark.cal.compiler.SourceModel.InstanceDefn;
import org.openquark.cal.compiler.SourceModel.LocalDefn;
import org.openquark.cal.compiler.SourceModel.ModuleDefn;
import org.openquark.cal.compiler.SourceModel.Name;
import org.openquark.cal.compiler.SourceModel.Parameter;
import org.openquark.cal.compiler.SourceModel.TopLevelSourceElement;
import org.openquark.cal.compiler.SourceModel.TypeConstructorDefn;
import org.openquark.cal.compiler.SourceModel.TypeExprDefn;
import org.openquark.cal.compiler.SourceModel.TypeSignature;
import org.openquark.cal.internal.javamodel.JavaClassRep;
import org.openquark.cal.internal.javamodel.JavaConstructor;
import org.openquark.cal.internal.javamodel.JavaExpression;
import org.openquark.cal.internal.javamodel.JavaFieldDeclaration;
import org.openquark.cal.internal.javamodel.JavaMethod;
import org.openquark.cal.internal.javamodel.JavaOperator;
import org.openquark.cal.internal.javamodel.JavaReservedWords;
import org.openquark.cal.internal.javamodel.JavaStatement;
import org.openquark.cal.internal.javamodel.JavaTypeName;
import org.openquark.cal.internal.javamodel.JavaExpression.Assignment;
import org.openquark.cal.internal.javamodel.JavaExpression.CastExpression;
import org.openquark.cal.internal.javamodel.JavaExpression.JavaField;
import org.openquark.cal.internal.javamodel.JavaExpression.LiteralWrapper;
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.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.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.module.Cal.Core.CAL_Prelude;
import org.openquark.cal.runtime.CalValue;
/**
* This class generates I/O source code in the form of a set of Java classes and a CAL
* module. The Java classes are in the form of instances of JavaClassRep and the CAL module
* is produced in the form of an instance of SourceModel.Module. Client code is responsible for
* converting these forms into source code, byte code, etc.
*
* When working on a project which combines CAL and Java code there is often a need to
* marshal data types between CAL and Java. The mechanism which is generally used for
* this is to make a CAL data type an instance of the Inputable and Outputable type classes.
*
* To make a user defined type inputable and outputable, you would first decide what the
* Java representation of the Quark type will be. The input and output methods will convert
* between the Quark type and an appropriate instance of this Java class.
* A common approach is to define a foreign type for the target Java class, write CAL
* functions that convert to and from that type, using foreign functions, then convert
* to or from JObject using a foreign function that merely casts between the target Java
* class and Object. For more information please see the two documents �CAL User�s Guide�
* and �Java Meets Quark�.
*
* The Input/Output code generation tool seeks to streamline the process of making a CAL
* data type an instance of the Inputable and Outputable type classes by generating the
* majority of the necessary CAL and Java code.
*
* The code generation tool takes an existing CAL data type and will generate a corresponding
* Java class. It will then generate the necessary CAL code to refer to the generated Java
* class as a CAL foreign type, generate the CAL functions needed to convert between the original
* CAL data type and the foreign type, and generate the type class instance declarations to make the
* original data type an instance of the Inputable and Outputable type classes.
*
* @author rcypher
*
*/
public class IO_Source_Generator {
private static final JavaTypeName CAL_VALUE = JavaTypeName.make(CalValue.class);
/**
* These are not valid file names in Windows. In addition there is the com1, com2, ..., lpt1, lpt2, ... family of names that are
* parameterized by an integer. Note that Windows is case-insensitive, so check that the lower case of your string is not in this
* set.
*/
static private final Set<String> windowsReservedWords = new LinkedHashSet<String>();
static {
windowsReservedWords.add ("clock$");
windowsReservedWords.add ("con");
windowsReservedWords.add ("prn");
windowsReservedWords.add ("nul");
windowsReservedWords.add ("config$");
windowsReservedWords.add ("aux");
}
private static final JavaTypeName[] EMPTY_TYPE_NAME_ARRAY = new JavaTypeName[0];
private static final JavaTypeName UNSUPPORTED_OPERATION_TYPE_NAME = JavaTypeName.make(UnsupportedOperationException.class);
private static final String GET_DC_ORDINAL_METHOD_NAME = "getDCOrdinal";
private static final String GET_DC_NAME_METHOD_NAME = "getDCName";
private static final String ORDINAL_FIELD_NAME = "ordinal$";
private static final String DC_NAME_FIELD_NAME = "dcName$";
private static final String HASH_CODE_FIELD_NAME = "hashCode";
private static final TypeExprDefn.TypeCons INT_TYPE_EXPR_DEFN =
TypeExprDefn.TypeCons.make(Name.TypeCons.make(CAL_Prelude.TypeConstructors.Int));
private static final TypeExprDefn.TypeCons JOBJECT_TYPE_EXPR_DEFN =
TypeExprDefn.TypeCons.make(Name.TypeCons.make(CAL_Prelude.TypeConstructors.JObject));
public static final class GeneratedIO {
/** QualifiedName -> JavaClassRep */
private final Map<QualifiedName, JavaClassRep> javaClasses;
/** QualifiedName -> (List of LogRecord) */
private final Map<QualifiedName, List<LogRecord>> javaClassLogMessages;
/** SourceModel.ModuleDefn for CAL I/O code. */
private final SourceModel.ModuleDefn ioModule;
/** List of LogRecord for the module definition. */
private final List<LogRecord> moduleLogMessages;
private final List<LogRecord> ioLogMessages;
GeneratedIO (Map<QualifiedName, JavaClassRep> javaClasses,
Map<QualifiedName, List<LogRecord>> javaClassLogMessages,
ModuleDefn ioModule,
List<LogRecord> moduleLogMessages,
List<LogRecord> ioLogMessages) {
this.javaClasses = javaClasses;
this.javaClassLogMessages = javaClassLogMessages;
this.ioModule = ioModule;
this.moduleLogMessages = moduleLogMessages;
this.ioLogMessages = ioLogMessages;
}
/**
* @return the ioModule
*/
public SourceModel.ModuleDefn getIoModule() {
return ioModule;
}
/**
* @return the javaClasses
*/
public Map<QualifiedName, JavaClassRep> getJavaClasses() {
return javaClasses;
}
/**
* @return the ioLogMessages
*/
public List<LogRecord> getIoLogMessages() {
return ioLogMessages;
}
/**
* @return the javaClassLogMessages
*/
public Map<QualifiedName, List<LogRecord>> getJavaClassLogMessages() {
return javaClassLogMessages;
}
/**
* @return the moduleLogMessages
*/
public List<LogRecord> getModuleLogMessages() {
return moduleLogMessages;
}
}
final GeneratedIO generateIO (
ModuleTypeInfo module,
String targetPackage,
Set<QualifiedName> typeConstructorsToIgnore,
Map<QualifiedName, JavaTypeName> typeToClassMappings,
Map<ModuleName, String>moduleToPackageMappings) throws UnableToResolveForeignEntityException {
/** Maps names of CAL types to the corresponding generated Java class. */
Map<QualifiedName, JavaClassRep> classReps =
new LinkedHashMap<QualifiedName, JavaClassRep>();
Map<QualifiedName, List<LogRecord>> classLogMessages =
new LinkedHashMap<QualifiedName, List<LogRecord>>();
List<TopLevelSourceElement> allTopLevelElements =
new ArrayList<TopLevelSourceElement>();
List<LogRecord> calLogMessages = new ArrayList<LogRecord>();
Set<ModuleName> mustHaveImports = new LinkedHashSet<ModuleName>();
Map<String, TypeSignature> castFunctions = new LinkedHashMap<String, TypeSignature>();
Map<QualifiedName, TypeConstructor> additionalForeignTypeDeclarations = new LinkedHashMap<QualifiedName, TypeConstructor>();
for (int i = 0, n = module.getNTypeConstructors(); i < n; ++i) {
TypeConstructor tc = module.getNthTypeConstructor(i);
if (typeConstructorsToIgnore.contains(tc.getName()) ||
tc.getForeignTypeInfo() != null) {
logMessage(Level.INFO, "Skipped type constructor " + tc.getName().getUnqualifiedName() + " because of ignore directive.");
continue;
}
// If the type constructor is declared as inputable/outputable in the containing module
// we skip it because generating inputable/outputable instances would conflict.
boolean alreadyInputableOutputable = false;
for (int j = 0, k = module.getNClassInstances(); j < k; ++j) {
ClassInstance ci = module.getNthClassInstance(j);
if (ci.getTypeClass().getName().equals(CAL_Prelude.TypeClasses.Inputable) ||
ci.getTypeClass().getName().equals(CAL_Prelude.TypeClasses.Outputable)) {
// Check the instance type
TypeExpr instanceTypeExpr = ci.getType();
TypeConsApp tca = instanceTypeExpr.rootTypeConsApp();
if (tca != null) {
if (tca.getRoot().getName().equals(tc.getName())) {
alreadyInputableOutputable = true;
}
}
}
}
if (alreadyInputableOutputable) {
logMessage(Level.INFO, "Skipped type constructor " + tc.getName().getUnqualifiedName() + " because it is alreayd an instance of Inputable or Outputable.");
continue;
}
// Build up info about the type constructor and data constructors.
TypeConstructorInfo typeConstructorInfo =
getTypeConstructorInfo (tc, typeToClassMappings, moduleToPackageMappings, module, targetPackage);
// Now we want to generate the Java class.
JavaDataClassGenerator javaDataClassGenerator =
new JavaDataClassGenerator(
targetPackage,
typeConstructorInfo);
JavaClassRep javaClass = javaDataClassGenerator.generateClassForType();
classReps.put(tc.getName(), javaClass);
classLogMessages.put(tc.getName(), javaDataClassGenerator.logMessages);
// Generate the CAL I/O
CAL_IO_Generator ioGenerator =
new CAL_IO_Generator (
typeConstructorInfo,
module,
javaClass);
Collection<TopLevelSourceElement> topLevelElements = ioGenerator.generateCAL_IO ();
allTopLevelElements.addAll(topLevelElements);
calLogMessages.addAll(ioGenerator.logMessages);
mustHaveImports.addAll(ioGenerator.getInputableImports());
castFunctions.putAll(ioGenerator.castFunctionNameToTypeSignature);
additionalForeignTypeDeclarations.putAll(ioGenerator.additionalForeignTypeDeclarations);
}
SourceModel.ModuleDefn moduleDefn = null;
if (allTopLevelElements.size() > 0) {
ModuleName newModuleName = ModuleName.make(module.getModuleName().toString() + "_JavaIO");
for (Map.Entry<String, TypeSignature> entry : castFunctions.entrySet()) {
String castFunctionName = (String)entry.getKey();
SourceModel.TypeSignature typeSignature = (TypeSignature)entry.getValue();
TypeExprDefn domain = ((TypeExprDefn.Function)typeSignature.getTypeExprDefn()).getDomain();
TypeExprDefn coDomain = ((TypeExprDefn.Function)typeSignature.getTypeExprDefn()).getCodomain();
SourceModel.CALDoc.TextSegment.Plain textSegment =
SourceModel.CALDoc.TextSegment.Plain.make(
"Cast an instance of " + domain.toString() + " to " + coDomain.toString());
SourceModel.CALDoc.TextBlock textBlock =
SourceModel.CALDoc.TextBlock.make(new SourceModel.CALDoc.TextSegment.TopLevel[]{textSegment});
SourceModel.CALDoc.Comment.Function functionComment =
SourceModel.CALDoc.Comment.Function.make(
textBlock,
null);
SourceModel.FunctionDefn castFunction =
FunctionDefn.Foreign.make(
functionComment,
castFunctionName,
Scope.PRIVATE,
true,
"cast",
typeSignature);
allTopLevelElements.add(castFunction);
}
for (Map.Entry<QualifiedName, TypeConstructor>entry : additionalForeignTypeDeclarations.entrySet()) {
QualifiedName typeConstructorName = (QualifiedName)entry.getKey();
TypeConstructor typeConstructor = (TypeConstructor)entry.getValue();
// Declare the java class as a foreign type.
// ex. data foreign unsafe import jvm "org.olap.CubeType" public JCubeType;
SourceModel.TypeConstructorDefn.ForeignType foreignType =
TypeConstructorDefn.ForeignType.make(
typeConstructorName.getUnqualifiedName(),
Scope.PRIVATE,
ForeignTypeInfo.getCalSourceName(typeConstructor.getForeignTypeInfo().getForeignType()),
Scope.PRIVATE,
null);
allTopLevelElements.add(foreignType);
}
SourceModel.CALDoc.TextSegment.Plain textSegment =
SourceModel.CALDoc.TextSegment.Plain.make(
"\nThis module, " + newModuleName + ", implements input and output\n" +
"functions for the type constructors contained in the " + module.getModuleName() + " module.\n" +
"\n" +
"***************************************************************************************\n\n" +
"NOTE: The code in this module is automatically generated.\n" +
" MODIFICATIONS TO THIS SOURCE MAY BE OVERWRITTEN - DO NOT MODIFY THIS FILE\n\n" +
"***************************************************************************************\n\n" +
"The Java classes corresponding to the type constructors are also automatically generated.\n"
);
SourceModel.CALDoc.TextBlock textBlock =
SourceModel.CALDoc.TextBlock.make(new SourceModel.CALDoc.TextSegment.TopLevel[]{textSegment});
SourceModel.CALDoc.Comment.Module moduleComment =
SourceModel.CALDoc.Comment.Module.make(
textBlock,
null);
moduleDefn =
SourceModel.ModuleDefn.make(
moduleComment,
newModuleName,
new SourceModel.Import[0],
(SourceModel.TopLevelSourceElement[])allTopLevelElements.toArray(new SourceModel.TopLevelSourceElement[0]));
CAL_Module_ImportGenerator<Void> importGenerator = new CAL_Module_ImportGenerator<Void>(moduleDefn, mustHaveImports);
moduleDefn = importGenerator.generateImports(moduleDefn);
}
GeneratedIO generatedIO =
new GeneratedIO(
classReps,
classLogMessages,
moduleDefn,
calLogMessages,
ioLogMessages);
return generatedIO;
}
/**
* Build up information about a type constructor that will be
* used for generating Java and CAL sources.
*
* @param typeConstructor
* @param typeToClassMappings
* @param moduleToPackageMappings
* @param module
* @param targetPackage
* @return the info about the type constructor
* @throws UnableToResolveForeignEntityException
*/
private TypeConstructorInfo getTypeConstructorInfo (
TypeConstructor typeConstructor,
Map<QualifiedName, JavaTypeName> typeToClassMappings,
Map<ModuleName, String> moduleToPackageMappings,
ModuleTypeInfo module,
String targetPackage) throws UnableToResolveForeignEntityException {
Set<FieldName> allFieldNames = new LinkedHashSet<FieldName>();
Map<FieldName, Set<JavaTypeName>> allFieldTypes = new LinkedHashMap<FieldName, Set<JavaTypeName>>();
int nEnumDCs = 0;
for (int i = 0, n = typeConstructor.getNDataConstructors(); i < n; ++i) {
DataConstructor dc = typeConstructor.getNthDataConstructor(i);
if (dc.getArity() == 0) {
nEnumDCs++;
}
TypeExpr[] fieldTypes = getFieldTypesForDC(dc);
for (int j = 0, k = dc.getArity(); j < k; ++j) {
FieldName fn = dc.getNthFieldName(j);
allFieldNames.add(fn);
JavaTypeName jtn = typeExprToTypeName(fieldTypes[j], typeToClassMappings, moduleToPackageMappings, targetPackage);
Set<JavaTypeName> javaTypeNames = (Set<JavaTypeName>)allFieldTypes.get(fn);
if (javaTypeNames == null) {
javaTypeNames = new LinkedHashSet<JavaTypeName>();
allFieldTypes.put(fn, javaTypeNames);
}
javaTypeNames.add(jtn);
}
}
boolean enumDataType = nEnumDCs == typeConstructor.getNDataConstructors();
Map<FieldName, String> fieldJavaNames = new LinkedHashMap<FieldName, String>();
Map<FieldName, String> fieldAccessorMethodNames = new LinkedHashMap<FieldName, String>();
Map<FieldName, Map<JavaTypeName, String>> calFieldForeignTypes = new LinkedHashMap<FieldName, Map<JavaTypeName, String>>();
for (FieldName fn : allFieldNames) {
String javaName = getJavaFieldNameFromFieldName(fn);
fieldJavaNames.put(fn, javaName);
Set<JavaTypeName> javaTypes = (Set<JavaTypeName>)allFieldTypes.get(fn);
String updatedFieldName = javaName.substring(1);
char[] ln = updatedFieldName.toCharArray();
ln[0] = Character.toUpperCase(ln[0]);
updatedFieldName = new String (ln);
String methodName = "get" + updatedFieldName;
fieldAccessorMethodNames.put(fn, methodName);
Map<JavaTypeName, String> calForeignTypes = new LinkedHashMap<JavaTypeName, String>();
calFieldForeignTypes.put(fn, calForeignTypes);
for (JavaTypeName jtn : javaTypes) {
String calForeignTypeName = getNameOfCALForeignType(jtn, module);
calForeignTypes.put(jtn, calForeignTypeName);
}
}
// We want to avoid a situation where the outer class and inner class have the same name
// since in Java a nested type can't hide an enclosing type. To avoid this we
// append an '_' to the outer class name.
String javaClassName = getClassName(typeConstructor);
if (!enumDataType) {
for (int i = 0, n = typeConstructor.getNDataConstructors(); i < n; ++i) {
DataConstructor dc = typeConstructor.getNthDataConstructor(i);
String innerClassName = fixupClassName(dc.getName().getUnqualifiedName());
if (innerClassName.equals(javaClassName)) {
javaClassName = javaClassName + "_";
break;
}
}
}
Set<FieldName> commonFieldNames = getCommonFieldNames(typeConstructor);
Set<FieldName> finalCommonFieldNames = new LinkedHashSet<FieldName>();
for (FieldName fn : commonFieldNames) {
Set<JavaTypeName> fieldTypes = (Set<JavaTypeName>)allFieldTypes.get(fn);
if (fieldTypes.size() == 1) {
finalCommonFieldNames.add(fn);
}
}
TypeConstructorInfo tci =
new TypeConstructorInfo(
typeConstructor,
javaClassName,
enumDataType,
allFieldNames,
allFieldTypes,
finalCommonFieldNames,
fieldJavaNames,
fieldAccessorMethodNames,
calFieldForeignTypes,
typeToClassMappings);
for (int i = 0, n = typeConstructor.getNDataConstructors(); i < n; ++i) {
DataConstructor dc = typeConstructor.getNthDataConstructor(i);
DataConstructorInfo dci = getDataConstructorInfo (dc, tci, typeToClassMappings, moduleToPackageMappings, targetPackage);
tci.dataConstructorInfo.put(dc, dci);
}
return tci;
}
private final DataConstructorInfo getDataConstructorInfo(
DataConstructor dataConstructor,
TypeConstructorInfo typeConstructorInfo,
Map<QualifiedName, JavaTypeName> typeToClassMappings,
Map<ModuleName, String> moduleToPackageMappings,
String targetPackage) throws UnableToResolveForeignEntityException {
Map<FieldName, String> fieldJavaAccessorMethodNames = new LinkedHashMap<FieldName, String>();
Map<FieldName, JavaTypeName> fieldTypeNames = new LinkedHashMap<FieldName, JavaTypeName>();
TypeExpr fieldTypes[] = getFieldTypesForDC(dataConstructor);
boolean containsFields = false;
for (int i = 0, n = dataConstructor.getArity(); i < n; ++i) {
FieldName fn = dataConstructor.getNthFieldName(i);
if (!typeConstructorInfo.commonFieldNames.contains(fn)) {
containsFields = true;
}
JavaTypeName javaType = typeExprToTypeName(fieldTypes[i], typeToClassMappings, moduleToPackageMappings, targetPackage);
fieldTypeNames.put(fn, javaType);
String accessorName = (String)typeConstructorInfo.fieldJavaAccessorMethodNames.get(fn);
fieldJavaAccessorMethodNames.put(fn, accessorName);
}
return new DataConstructorInfo(
dataConstructor,
fieldJavaAccessorMethodNames,
fieldTypeNames,
containsFields);
}
private String getNameOfCALForeignType (JavaTypeName javaType, ModuleTypeInfo module) throws UnableToResolveForeignEntityException {
// First try to find a type constructor for each java type;
TypeConstructor typeConstructor = findTypeConstructorForJavaClass(javaType, module);
if (typeConstructor != null) {
if (typeConstructor.getScope() == Scope.PUBLIC) {
return typeConstructor.getName().toString();
} else {
return typeConstructor.getName().getUnqualifiedName();
}
} else
if (javaType.equals(JavaTypeName.BOOLEAN)){
return CAL_Prelude.TypeConstructors.Boolean.toString();
} else {
// Couldn't find a type constructor for the Java class.
// Build up a Name.TypeCons using our naming pattern.
// try to find a module containing the base CAL type.
ModuleName moduleName = findModuleContainingType(javaType.getUnqualifiedJavaSourceName(), module);
if (moduleName == null) {
return "J" + javaType.getUnqualifiedJavaSourceName();
} else {
moduleName = ModuleName.make(moduleName.toString() + "_JavaIO");
return moduleName.toString() + ".J" + javaType.getUnqualifiedJavaSourceName();
}
}
}
private final ModuleName findModuleContainingType (String typeName, ModuleTypeInfo module) {
return findModuleContainingType (new LinkedHashSet<ModuleName>(), typeName, module);
}
private final ModuleName findModuleContainingType(Set<ModuleName> visitedModules, String typeName, ModuleTypeInfo module) {
if (visitedModules.contains(module.getModuleName())) {
return null;
}
visitedModules.add(module.getModuleName());
for (int i = 0, n = module.getNTypeConstructors(); i < n; ++i) {
if (module.getNthTypeConstructor(i).getName().getUnqualifiedName().equals(typeName)) {
return module.getModuleName();
}
}
for (int i = 0, n = module.getNImportedModules(); i < n; ++i) {
ModuleTypeInfo importedModule = module.getNthImportedModule(i);
ModuleName mn = findModuleContainingType(visitedModules, typeName, importedModule);
if (mn != null) {
return mn;
}
}
return null;
}
/**
* Try to find a CAL type constructor corresponding to the named java type.
* @param javaType
* @param module
* @return TypeConstructor of foreign type, null if not found.
*/
private final TypeConstructor findTypeConstructorForJavaClass (JavaTypeName javaType, ModuleTypeInfo module) throws UnableToResolveForeignEntityException {
return findTypeConstructorForJavaClass (javaType, module, new LinkedHashSet<ModuleTypeInfo>());
}
/**
* Try to find a CAL type constructor corresponding to the named java type.
* @param javaType
* @param module
* @param visitedModules
* @return TypeConstructor of foreign type, null if not found.
*/
private final TypeConstructor findTypeConstructorForJavaClass (JavaTypeName javaType, ModuleTypeInfo module, Set<ModuleTypeInfo> visitedModules) throws UnableToResolveForeignEntityException {
if (visitedModules.contains(module)) {
return null;
}
visitedModules.add(module);
for (int i = 0, n = module.getNTypeConstructors(); i < n; ++i) {
TypeConstructor tc = module.getNthTypeConstructor(i);
ForeignTypeInfo fti = tc.getForeignTypeInfo();
if (fti == null) {
continue;
}
JavaTypeName foreignType = JavaTypeName.make(fti.getForeignType());
if (javaType.equals(foreignType)) {
return tc;
}
}
for (int i = 0, n = module.getNImportedModules(); i < n; ++i) {
ModuleTypeInfo importedModule = module.getNthImportedModule(i);
TypeConstructor tc = findTypeConstructorForJavaClass(javaType, importedModule, visitedModules);
if (tc != null) {
return tc;
}
}
return null;
}
private final class CAL_IO_Generator {
private ModuleTypeInfo module;
private final TypeConstructorInfo typeConstructorInfo;
private final TypeConstructor typeConstructor;
private final JavaClassRep javaClass;
private final Set<ModuleName> inputableImports = new LinkedHashSet<ModuleName>();
/* String -> TypeSignature */
private final Map<String, TypeSignature>
castFunctionNameToTypeSignature = new LinkedHashMap<String, TypeSignature>();
/* QualifiedName -> TypeConstructor */
private final Map<QualifiedName, TypeConstructor>
additionalForeignTypeDeclarations = new LinkedHashMap<QualifiedName, TypeConstructor>();
/** List of LogRecord */
private final List<LogRecord> logMessages = new ArrayList<LogRecord>();
private CAL_IO_Generator (
TypeConstructorInfo typeConstructorInfo,
ModuleTypeInfo module,
JavaClassRep javaClass) {
assert (module != null && typeConstructorInfo != null && javaClass != null) : "Null argument to IO_Source_Generator.generateJavaType().";
this.module = module;
this.typeConstructorInfo = typeConstructorInfo;
this.javaClass = javaClass;
this.typeConstructor = typeConstructorInfo.typeConstructor;
}
private void logMessage (Level level, String text) {
logMessages.add(new LogRecord(level, text));
}
/**
*
* @return a Collection of SourceModel.TopLevelSourceElement
*/
Collection<TopLevelSourceElement> generateCAL_IO () throws UnableToResolveForeignEntityException {
List<TopLevelSourceElement> topLevelDefnsList = new ArrayList<TopLevelSourceElement>();
// We have three different scenarios to deal with:
// 1) The CAL type is an enum
// 2) The CAL type has only one data constructor and is not an enum and the dat constructor has the same name as the type constructor.
// 3) The CAL type has more than one data constructor and is not an enum.
if (typeConstructorInfo.isEnumerationType) {
generateEnumerationIO (topLevelDefnsList);
} else {
generateMultiClassIO (topLevelDefnsList);
}
return topLevelDefnsList;
}
/**
*
* @param dc
* @return QualifiedName of the make function, null if none found.
*/
private QualifiedName findMakeDCName (DataConstructor dc) {
TypeExpr dcTypeExpr = dc.getTypeExpr();
String probableFunctionName = "make" + dc.getName().getUnqualifiedName();
for (int i = 0; i < module.getNFunctions(); ++i) {
Function function = module.getNthFunction(i);
if (function.getName().getUnqualifiedName().equals(probableFunctionName)) {
if (dcTypeExpr.sameType(function.getTypeExpr())) {
return QualifiedName.make(module.getModuleName(), probableFunctionName);
}
}
}
return null;
}
private void generateFieldAccessor (
String instanceForeignTypeName,
FieldName fn,
JavaTypeName fieldType,
String foreignFieldTypeString,
DataConstructor dc,
List<TopLevelSourceElement> topLevelDefns) {
TypeExprDefn jInstanceClassTypeExpr = TypeExprDefn.TypeCons.make(Name.TypeCons.makeUnqualified(instanceForeignTypeName));
Name.TypeCons typeConsName;
if (foreignFieldTypeString.indexOf('.') > -1) {
typeConsName = Name.TypeCons.make(QualifiedName.makeFromCompoundName(foreignFieldTypeString));
} else {
typeConsName = Name.TypeCons.makeUnqualified(foreignFieldTypeString);
}
TypeExprDefn fieldTypeForGetter = TypeExprDefn.TypeCons.make(typeConsName);
String functionName =
(String)(typeConstructorInfo.calFieldAccessorFunctionNames.get(fn)).get(dc);
String methodName =
(String)typeConstructorInfo.fieldJavaAccessorMethodNames.get(fn);
SourceModel.CALDoc.TextSegment.Plain textSegment =
SourceModel.CALDoc.TextSegment.Plain.make(
"Retrieve the " + fn.toString() + " field from an instance of " + instanceForeignTypeName + ".");
SourceModel.CALDoc.TextBlock textBlock =
SourceModel.CALDoc.TextBlock.make(new SourceModel.CALDoc.TextSegment.TopLevel[]{textSegment});
SourceModel.CALDoc.Comment.Function functionComment =
SourceModel.CALDoc.Comment.Function.make(
textBlock,
null);
SourceModel.FunctionDefn getter =
FunctionDefn.Foreign.make(
functionComment,
functionName,
Scope.PRIVATE,
true,
"method " + methodName,
TypeSignature.make(TypeExprDefn.Function.make(jInstanceClassTypeExpr, fieldTypeForGetter)));
topLevelDefns.add(getter);
}
private void generateMultiClassIO (
List<TopLevelSourceElement> topLevelDefnsList) throws UnableToResolveForeignEntityException {
QualifiedName typeConstructorName = typeConstructorInfo.typeConstructor.getName();
// Import the top level class as a foreign type.
// ex. data foreign unsafe import jvm "org.olap.CubeType" public JCubeType;
makeForeignClassDeclaration(topLevelDefnsList);
for (int i = 0, n = typeConstructor.getNDataConstructors(); i < n; ++i) {
DataConstructor dc = typeConstructor.getNthDataConstructor(i);
makeForeignClassDeclaration(dc, topLevelDefnsList);
}
TypeExprDefn.TypeCons jClassTypeExpr =
TypeExprDefn.TypeCons.make(Name.TypeCons.makeUnqualified(typeConstructorInfo.calForeignTypeName));
// Import the constructors for each inner class (i.e. each data constructor)
// Pull in the constructor for the Java class.
for (int i = 0, n = javaClass.getNInnerClasses(); i < n; ++i) {
JavaClassRep innerClass = javaClass.getInnerClass(i);
assert (innerClass.getNConstructors() == 1);
JavaConstructor constructor = innerClass.getConstructor(0);
topLevelDefnsList.add(importJavaConstructor(constructor, true));
}
// Create accessors for fields in the top level class.
for (FieldName fn : typeConstructorInfo.commonFieldNames) {
Set<JavaTypeName> fieldTypes = typeConstructorInfo.allFieldTypeNames.get(fn);
// There will only be one type for common fields.
Iterator<JavaTypeName> types = fieldTypes.iterator();
JavaTypeName fieldType = types.next();
String foreignFieldTypeSting = typeConstructorInfo.calFieldForeignTypes.get(fn).get(fieldType);
generateFieldAccessor (
typeConstructorInfo.calForeignTypeName,
fn,
fieldType,
foreignFieldTypeSting,
typeConstructor.getNthDataConstructor(0),
topLevelDefnsList);
}
// Create accessors for the fields in the inner classes.
for (int i = 0, n = typeConstructor.getNDataConstructors(); i < n; ++i) {
DataConstructor dc = typeConstructor.getNthDataConstructor(i);
DataConstructorInfo dcInfo = (DataConstructorInfo)typeConstructorInfo.dataConstructorInfo.get(dc);
for (FieldName fn : dcInfo.allFieldNames) {
if (typeConstructorInfo.commonFieldNames.contains(fn)) {
continue;
}
JavaTypeName fieldType = (JavaTypeName)dcInfo.fieldTypeNames.get(fn);
String foreignFieldTypeSting = typeConstructorInfo.calFieldForeignTypes.get(fn).get(fieldType);
generateFieldAccessor (
dcInfo.calForeignTypeName,
fn,
fieldType,
foreignFieldTypeSting,
dc,
topLevelDefnsList);
}
}
// Bring in the 'getDCOrdinal()' method of the Java class.
String getDCOrdinalName = "j" + javaClass.getClassName().getUnqualifiedJavaSourceName() + "_getDCOrdinal";
SourceModel.TypeSignature getDCOrdinalTypeSignature =
TypeSignature.make(
TypeExprDefn.Function.make(
jClassTypeExpr,
INT_TYPE_EXPR_DEFN));
SourceModel.CALDoc.Comment.Function getDCOrdinalComment;
{
SourceModel.CALDoc.TextSegment.Plain textSegment =
SourceModel.CALDoc.TextSegment.Plain.make(
"\nRetrieve the ordinal value from an instance of " + typeConstructorInfo.calForeignTypeName + ".\n" +
"The ordinal can be used to determine which data constructor the " + typeConstructorInfo.calForeignTypeName + "\n" +
"instance corresponds to.");
SourceModel.CALDoc.TextBlock textBlock =
SourceModel.CALDoc.TextBlock.make(new SourceModel.CALDoc.TextSegment.TopLevel[]{textSegment});
getDCOrdinalComment =
SourceModel.CALDoc.Comment.Function.make(
textBlock,
null);
}
SourceModel.FunctionDefn getDCOrdinal =
FunctionDefn.Foreign.make(
getDCOrdinalComment,
getDCOrdinalName,
Scope.PRIVATE,
true,
"method getDCOrdinal",
getDCOrdinalTypeSignature);
topLevelDefnsList.add(getDCOrdinal);
// Create input function.
// inputCube :: JCube -> Cube;
// inputCube jCube =
// case (jCube_getDCOrdinal JCube) of
// 0 -> ...;
// 1 -> ...;
String inputFunctionName =
"input" + typeConstructor.getName().getUnqualifiedName();
String inputFunctionArgName = "j" + typeConstructorInfo.calForeignTypeName.substring(1);
Expr.Var inputFunctionArg = Expr.Var.make(Name.Function.makeUnqualified(inputFunctionArgName));
// Do the function type declaration.
// JCube -> Cube
TypeExprDefn inputFunctionType =
TypeExprDefn.Function.make(
TypeExprDefn.TypeCons.make(Name.TypeCons.makeUnqualified(typeConstructorInfo.calForeignTypeName)),
TypeExprDefn.TypeCons.make(Name.TypeCons.makeUnqualified(typeConstructorName.getUnqualifiedName())));
SourceModel.CALDoc.Comment.Function inputFunctionComment;
{
SourceModel.CALDoc.TextSegment.Plain textSegment =
SourceModel.CALDoc.TextSegment.Plain.make(
"\nInput an instance of " + typeConstructor.getName().getUnqualifiedName() + ".\n" +
"Translates an instance of " + typeConstructorInfo.calForeignTypeName + " to\n" +
"an instance of "+ typeConstructor.getName().getUnqualifiedName() + ".");
SourceModel.CALDoc.TextBlock textBlock =
SourceModel.CALDoc.TextBlock.make(new SourceModel.CALDoc.TextSegment.TopLevel[]{textSegment});
inputFunctionComment =
SourceModel.CALDoc.Comment.Function.make(
textBlock,
null);
}
SourceModel.FunctionTypeDeclaration inputFunctionTypeDecl =
FunctionTypeDeclaration.make(
inputFunctionComment,
inputFunctionName,
TypeSignature.make(inputFunctionType));
topLevelDefnsList.add(inputFunctionTypeDecl);
// build up the function body
SourceModel.Expr condition =
Expr.Application.make(
new Expr[]{Expr.Var.make(Name.Function.makeUnqualified(getDCOrdinalName)),
inputFunctionArg});
SourceModel.Expr.Case.Alt caseAlts[] =
new SourceModel.Expr.Case.Alt[typeConstructor.getNDataConstructors()];
for (int i = 0, n = caseAlts.length; i < n; ++i) {
DataConstructor dc = typeConstructor.getNthDataConstructor(i);
// Build up an application of the data constructor.
SourceModel.Expr dcApplication =
makeDCCall(dc, inputFunctionArg);
caseAlts[i] =
Expr.Case.Alt.UnpackInt.make(
new BigInteger[]{BigInteger.valueOf(dc.getOrdinal())},
dcApplication);
}
SourceModel.Expr body =
Expr.Case.make(condition, caseAlts);
SourceModel.FunctionDefn.Algebraic inputFunction =
FunctionDefn.Algebraic.make(
inputFunctionName,
Scope.PUBLIC,
new SourceModel.Parameter[]{Parameter.make(inputFunctionArgName, false)},
body);
topLevelDefnsList.add(inputFunction);
// Create an inputCubeFromJObject function.
String inputFromJObjectFunctionName = inputFunctionName + "FromJObject";
createInputFromJObjectFunction(inputFromJObjectFunctionName, inputFunctionName, topLevelDefnsList);
// Create Inputable instance.
SourceModel.InstanceDefn instanceDefn =
InstanceDefn.make(
Name.TypeClass.make(CAL_Prelude.TypeClasses.Inputable),
InstanceDefn.InstanceTypeCons.TypeCons.make(Name.TypeCons.make(typeConstructorName), null),
null,
new InstanceDefn.InstanceMethod[]{
InstanceDefn.InstanceMethod.make(CAL_Prelude.Functions.input.getUnqualifiedName(), Name.Function.makeUnqualified(inputFromJObjectFunctionName))
});
topLevelDefnsList.add(instanceDefn);
// Now we want to make the outputable instance.
// Build up a function body that converts
// from 'Cube' to 'JCube'.
Expr.Var dcInstance = Expr.Var.makeUnqualified("dcInstance");
caseAlts =
new SourceModel.Expr.Case.Alt[typeConstructor.getNDataConstructors()];
for (int i = 0, n = caseAlts.length; i < n; ++i) {
DataConstructor dc = typeConstructor.getNthDataConstructor(i);
SourceModel.Pattern patterns[] = new SourceModel.Pattern[dc.getArity()];
Arrays.fill(patterns, SourceModel.Pattern.Wildcard.make());
caseAlts[i] =
Expr.Case.Alt.UnpackDataCons.make(
Name.DataCons.make(dc.getName()),
patterns,
makeConstructorCall(dc, dcInstance, true));
}
SourceModel.Expr.Case conversionFunctionBody =
Expr.Case.make(dcInstance, caseAlts);
makeOutputableInstance(typeConstructor, conversionFunctionBody, topLevelDefnsList);
}
private void makeOutputableInstance (
TypeConstructor typeConstructor,
SourceModel.Expr outputFunctionBody,
List<TopLevelSourceElement> topLevelDefnsList) {
// Create an output function.
// outputCube :: Cube -> JCube;
// public outputCube dcInstance =
// case dcInstanceOf
// DC1 -> jDC1_new dcInstance.DC1.field1 (jObjectToJ... (output (dcInstance.DC1.field2)));
// ...
//
// outputCubeToJObject :: Cube -> JObject;
// private outputCubeToJObject dcInstance =
// output (outputCube dcInstance);
String outputFunctionName =
"output" + typeConstructor.getName().getUnqualifiedName();
// Do the function type declaration.
TypeExprDefn outputFunctionType =
TypeExprDefn.Function.make(
TypeExprDefn.TypeCons.make(Name.TypeCons.makeUnqualified(typeConstructorInfo.typeConstructor.getName().getUnqualifiedName())),
TypeExprDefn.TypeCons.make(Name.TypeCons.makeUnqualified(typeConstructorInfo.calForeignTypeName)));
SourceModel.CALDoc.Comment.Function outputFunctionComment;
{
SourceModel.CALDoc.TextSegment.Plain textSegment =
SourceModel.CALDoc.TextSegment.Plain.make(
"\nOutput an instance of " + typeConstructor.getName().getUnqualifiedName() + ".\n" +
"Translates an instance of " + typeConstructor.getName().getUnqualifiedName() + " to\n" +
"an instance of "+ typeConstructorInfo.calForeignTypeName + ".");
SourceModel.CALDoc.TextBlock textBlock =
SourceModel.CALDoc.TextBlock.make(new SourceModel.CALDoc.TextSegment.TopLevel[]{textSegment});
outputFunctionComment =
SourceModel.CALDoc.Comment.Function.make(
textBlock,
null);
}
SourceModel.FunctionTypeDeclaration outputFunctionTypeDecl =
FunctionTypeDeclaration.make(
outputFunctionComment,
outputFunctionName,
TypeSignature.make(outputFunctionType));
topLevelDefnsList.add(outputFunctionTypeDecl);
SourceModel.FunctionDefn.Algebraic outputFunction =
FunctionDefn.Algebraic.make(
outputFunctionName,
Scope.PUBLIC,
new SourceModel.Parameter[]{Parameter.make("dcInstance", true)},
outputFunctionBody);
topLevelDefnsList.add(outputFunction);
// Now create the conversion function. i.e. a private function that outputs to a JObject.
String conversionFunctionName = outputFunctionName + "ToJObject";
// Do the function type declaration.
TypeExprDefn conversionFunctionType =
TypeExprDefn.Function.make(
TypeExprDefn.TypeCons.make(Name.TypeCons.makeUnqualified(typeConstructorInfo.typeConstructor.getName().getUnqualifiedName())),
JOBJECT_TYPE_EXPR_DEFN);
SourceModel.CALDoc.Comment.Function conversionFunctionComment;
{
SourceModel.CALDoc.TextSegment.Plain textSegment =
SourceModel.CALDoc.TextSegment.Plain.make(
"\nOutput an instance of " + typeConstructor.getName().getUnqualifiedName() + ".\n" +
"Translates an instance of " + typeConstructor.getName().getUnqualifiedName() + " to\n" +
"an instance of JObject.");
SourceModel.CALDoc.TextBlock textBlock =
SourceModel.CALDoc.TextBlock.make(new SourceModel.CALDoc.TextSegment.TopLevel[]{textSegment});
conversionFunctionComment =
SourceModel.CALDoc.Comment.Function.make(
textBlock,
null);
}
SourceModel.FunctionTypeDeclaration conversionFunctionTypeDecl =
FunctionTypeDeclaration.make(
conversionFunctionComment,
conversionFunctionName,
TypeSignature.make(conversionFunctionType));
topLevelDefnsList.add(conversionFunctionTypeDecl);
SourceModel.Expr conversionFunctionBody =
SourceModel.Expr.Application.make(
new SourceModel.Expr[]{
SourceModel.Expr.Var.make(Name.Function.makeUnqualified(outputFunctionName)),
SourceModel.Expr.Var.make(Name.Function.makeUnqualified("dcInstance"))
});
conversionFunctionBody =
SourceModel.Expr.Application.make(
new SourceModel.Expr[]{
SourceModel.Expr.Var.make(Name.Function.make(CAL_Prelude.Functions.output)),
conversionFunctionBody
});
// Now do the body of the conversion function.
SourceModel.FunctionDefn.Algebraic conversionFunction =
FunctionDefn.Algebraic.make(
conversionFunctionName,
Scope.PRIVATE,
new SourceModel.Parameter[]{Parameter.make("dcInstance", true)},
conversionFunctionBody);
topLevelDefnsList.add(conversionFunction);
// Create Outputable instance.
SourceModel.InstanceDefn outputInstanceDefn =
InstanceDefn.make(
Name.TypeClass.make(CAL_Prelude.TypeClasses.Outputable),
InstanceDefn.InstanceTypeCons.TypeCons.make(Name.TypeCons.make(typeConstructorInfo.typeConstructor.getName()), null),
null,
new InstanceDefn.InstanceMethod[]{
InstanceDefn.InstanceMethod.make(CAL_Prelude.Functions.output.getUnqualifiedName(), Name.Function.makeUnqualified(conversionFunctionName))
});
topLevelDefnsList.add(outputInstanceDefn);
}
private SourceModel.Expr makeConstructorCall (
DataConstructor dc,
SourceModel.Expr.Var dcVar,
boolean isInnerClass) throws UnableToResolveForeignEntityException {
SourceModel.Name.DataCons dataConsSourceModelName = Name.DataCons.make(dc.getName());
String javaConstructorName = fixupClassName(dc.getName().getUnqualifiedName());
String calConstructorName = "j" + javaConstructorName + "_new";
TypeExpr[] dcFieldTypeExprs = getFieldTypesForDC(dc);
JavaConstructor javaConstructor = null;
if (isInnerClass) {
for (int i = 0, n = javaClass.getNInnerClasses(); i < n; ++i) {
JavaClassRep innerClass = javaClass.getInnerClass(i);
if (innerClass.getClassName().getUnqualifiedJavaSourceName().endsWith("." + javaConstructorName)) {
javaConstructor = innerClass.getConstructor(0);
break;
}
}
} else {
javaConstructor = javaClass.getConstructor(0);
}
if (javaConstructor == null) {
throw new NullPointerException ("Unable to find Java constructor for data constructor " + dc.getName());
}
int nArgs = javaConstructor.getNParams();
if (nArgs == 0) {
return SourceModel.Expr.Var.makeUnqualified(calConstructorName);
}
TypeExprDefn argTypes[] = new TypeExprDefn[nArgs];
for (int i = 0; i < nArgs; ++i) {
argTypes[i] = getTypeExprDefn(new JavaTypeName[]{javaConstructor.getParamType(i)});
}
SourceModel.Expr applicationExpressions[] = new SourceModel.Expr[nArgs+ 1];
SourceModel.Expr.Var output = Expr.Var.make(CAL_Prelude.Functions.output);
applicationExpressions[0] = SourceModel.Expr.Var.makeUnqualified(calConstructorName);
Expr.Var unsafeCoerce = Expr.Var.make(CAL_Prelude.Functions.unsafeCoerce);
for (int i = 0, n = dc.getArity(); i < n; ++i) {
FieldName fn = dc.getNthFieldName(i);
SourceModel.Expr argValue = Expr.SelectDataConsField.make(dcVar, dataConsSourceModelName, SourceModel.Name.Field.make(fn));
// If the type of the dc field matches the type of the constructor argument
// we can simply use the field accessor. Otherwise we need to apply output to
// resulting value.
TypeExpr fieldTypeExpr = dcFieldTypeExprs[i];
updateInputableImports(fieldTypeExpr);
String dcFieldTypeConsName = "";
TypeConsApp typeConsApp = fieldTypeExpr.rootTypeConsApp();
if (typeConsApp != null) {
TypeConstructor tc = typeConsApp.getRoot();
dcFieldTypeConsName = tc.getName().toString();
}
String constructorArgTypeConsName = ((TypeExprDefn.TypeCons)argTypes[i]).getTypeConsName().toString();
if (!constructorArgTypeConsName.equals(dcFieldTypeConsName)) {
// We don't need to apply 'output' if the result type of the field accessor is CALValue
if (!constructorArgTypeConsName.equals(CAL_Prelude.TypeConstructors.CalValue.getQualifiedName())) {
argValue = Expr.Application.make(new Expr[]{output, argValue});
// We want to cast the result
String castFunctionName = makeCastFunctionFromJObject(((TypeExprDefn.TypeCons)argTypes[i]).getTypeConsName().getUnqualifiedName());
argValue = Expr.Application.make(new Expr[]{Expr.Var.make(Name.Function.makeUnqualified(castFunctionName)), argValue});
}
else {
argValue = Expr.Application.make(new Expr[]{unsafeCoerce, argValue});
}
}
applicationExpressions[i+1] = argValue;
}
return SourceModel.Expr.Application.make(applicationExpressions);
}
private SourceModel.Expr makeDCCall (
DataConstructor dc,
Expr.Var argCastToTopLevelJType
) throws UnableToResolveForeignEntityException {
// We want to generate something along the lines of:
// let
// jInnerClassName = jObjectToJInnerClassName jObject;
// in
// DataConstructorName
// input field 1...
DataConstructorInfo dcInfo = (DataConstructorInfo)typeConstructorInfo.dataConstructorInfo.get(dc);
// Build up the values for each field in the data constructor.
SourceModel.Expr dcApplicationExpressions[] = new SourceModel.Expr[dc.getArity() + 1];
QualifiedName makeFunctionName = findMakeDCName(dc);
if (makeFunctionName != null) {
dcApplicationExpressions[0] = SourceModel.Expr.Var.make (Name.Function.make(makeFunctionName));
logMessage(Level.INFO, "Using function " + makeFunctionName.getUnqualifiedName() + " instead of data constructor " + dc.getName().getUnqualifiedName());
} else {
dcApplicationExpressions[0] = SourceModel.Expr.DataCons.make(dc.getName());
}
TypeExpr[] dcFieldTypes = getFieldTypesForDC(dc);
SourceModel.Expr.Var input = Expr.Var.make(CAL_Prelude.Functions.input);
Expr unsafeCoerce = Expr.Var.make(Name.Function.make(CAL_Prelude.Functions.unsafeCoerce));
// We need a cast function from JObject to the foreign inner class if there are fields
// in the inner class.
SourceModel.LocalDefn localDefns[] = null;
Expr.Var argCastToInnerJType = null;
if (dcInfo.containsFields) {
// We want to create a let var that is the cast of the
// JObject parameter to the J... foreign type.
String letVarName = "j" + dcInfo.calForeignTypeName.substring(1);
localDefns =
makeOuterForeignTypeToInnerForeignTypeLetVar(
argCastToTopLevelJType.getVarName().getUnqualifiedName(),
typeConstructorInfo.calForeignTypeName,
letVarName,
dcInfo.calForeignTypeName);
argCastToInnerJType = Expr.Var.makeUnqualified(letVarName);
}
for (int i = 0, n = dc.getArity(); i < n; ++i) {
FieldName fn = dc.getNthFieldName(i);
JavaTypeName fieldType = (JavaTypeName)dcInfo.fieldTypeNames.get(fn);
String fieldAccessorName = typeConstructorInfo.calFieldAccessorFunctionNames.get(fn).get(dc);
// If the type of the retrieved field matches the type of the dc field
// we can simply use the field accessor. Otherwise we need to apply input to
// resulting value.
SourceModel.Expr.Var accessor = Expr.Var.make(Name.Function.makeUnqualified(fieldAccessorName));
SourceModel.Expr accessApplication;
if (typeConstructorInfo.commonFieldNames.contains(fn)) {
accessApplication =
Expr.Application.make(new SourceModel.Expr[]{accessor, argCastToTopLevelJType});
} else {
accessApplication =
Expr.Application.make(new SourceModel.Expr[]{accessor, argCastToInnerJType});
}
TypeExpr fieldTypeExpr = dcFieldTypes[i];
updateInputableImports(fieldTypeExpr);
TypeExprDefn fieldTypeForGetter = getTypeExprDefn(new JavaTypeName[]{fieldType});
String fieldTypeForGetterTypeConsName = ((TypeExprDefn.TypeCons)fieldTypeForGetter).getTypeConsName().toString();
String dcFieldTypeConsName = "";
TypeConsApp typeConsApp = fieldTypeExpr.rootTypeConsApp();
if (typeConsApp != null) {
TypeConstructor tc = typeConsApp.getRoot();
dcFieldTypeConsName = tc.getName().toString();
}
if (!fieldTypeForGetterTypeConsName.equals(dcFieldTypeConsName)) {
// apply input and usafe coerce.
// We don't need to apply 'input' if the result type of the field accessor is CALValue
if (!fieldTypeForGetterTypeConsName.equals(CAL_Prelude.TypeConstructors.CalValue.getQualifiedName())) {
String castFunctionName = makeCastFunctionToJobject(((TypeExprDefn.TypeCons)fieldTypeForGetter).getTypeConsName().getUnqualifiedName());
accessApplication = Expr.Application.make(new Expr[]{input, Expr.Application.make(new Expr[]{Expr.Var.make(Name.Function.makeUnqualified(castFunctionName)), accessApplication})});
} else {
// Inputing to a function type. Use unsafeCoerce.
accessApplication = Expr.Application.make(new Expr[]{unsafeCoerce, accessApplication});
}
}
accessApplication = Expr.Application.make(new Expr[]{Expr.Var.make(Name.Function.make(CAL_Prelude.Functions.eager)), accessApplication});
dcApplicationExpressions[i+1] = accessApplication;
}
// Now apply the data constructor to each of the arguments.
SourceModel.Expr dcApplication;
if (dcApplicationExpressions.length >= 2) {
dcApplication = Expr.Application.make(dcApplicationExpressions);
} else {
dcApplication = dcApplicationExpressions[0];
}
if (localDefns != null) {
dcApplication = SourceModel.Expr.Let.make(localDefns, dcApplication);
}
return dcApplication;
}
private SourceModel.FunctionDefn importJavaConstructor (JavaConstructor constructor, boolean innerClass) throws UnableToResolveForeignEntityException {
int nArgs = constructor.getNParams();
JavaTypeName argTypes[] = new JavaTypeName[nArgs + 1];
for (int i = 0; i < nArgs; ++i) {
argTypes[i] = constructor.getParamType(i);
}
argTypes[nArgs] = javaClass.getClassName();
TypeExprDefn argTypeDef = getTypeExprDefn(argTypes);
TypeSignature declaredType = TypeSignature.make(argTypeDef);
SourceModel.CALDoc.Comment.Function constructorFunctionComment;
{
SourceModel.CALDoc.TextSegment.Plain textSegment =
SourceModel.CALDoc.TextSegment.Plain.make(
"Constructor for " + constructor.getConstructorName() + ".");
SourceModel.CALDoc.TextBlock textBlock =
SourceModel.CALDoc.TextBlock.make(new SourceModel.CALDoc.TextSegment.TopLevel[]{textSegment});
constructorFunctionComment =
SourceModel.CALDoc.Comment.Function.make(
textBlock,
null);
}
SourceModel.FunctionDefn jConstructor =
FunctionDefn.Foreign.make(
constructorFunctionComment,
"j" + constructor.getConstructorName() + "_new",
Scope.PUBLIC,
true,
"constructor " + javaClass.getClassName().getFullJavaSourceName() + (innerClass ? "$" + constructor.getConstructorName() : ""),
declaredType);
return jConstructor;
}
private String makeCastFunctionToJobject (String foreignTypeName) {
String castFunctionName = Character.toLowerCase(foreignTypeName.charAt(0)) + foreignTypeName.substring(1) + "ToJObject";
if (castFunctionNameToTypeSignature.get(castFunctionName) == null) {
SourceModel.TypeSignature castFunctionTypeSignature =
TypeSignature.make(
TypeExprDefn.Function.make(
TypeExprDefn.TypeCons.make(
Name.TypeCons.makeUnqualified(foreignTypeName)),
JOBJECT_TYPE_EXPR_DEFN));
castFunctionNameToTypeSignature.put(castFunctionName, castFunctionTypeSignature);
}
return castFunctionName;
}
private String makeCastFunction (String fromForeignTypeName, String toForeignTypeName) {
String castFunctionName = "cast" + fromForeignTypeName + "To" + toForeignTypeName;
if (castFunctionNameToTypeSignature.get(castFunctionName) == null) {
SourceModel.TypeSignature castFunctionTypeSignature =
TypeSignature.make(
TypeExprDefn.Function.make(
TypeExprDefn.TypeCons.make(
Name.TypeCons.makeUnqualified(fromForeignTypeName)),
TypeExprDefn.TypeCons.make(
Name.TypeCons.makeUnqualified(toForeignTypeName))));
castFunctionNameToTypeSignature.put(castFunctionName, castFunctionTypeSignature);
}
return castFunctionName;
}
private String makeCastFunctionFromJObject (String foreignTypeName) {
String castFunctionName = "jObjectTo" + foreignTypeName;
if (castFunctionNameToTypeSignature.get(castFunctionName) == null) {
SourceModel.TypeSignature castFunctionTypeSignature =
TypeSignature.make(
TypeExprDefn.Function.make(
JOBJECT_TYPE_EXPR_DEFN,
TypeExprDefn.TypeCons.make(
Name.TypeCons.makeUnqualified(foreignTypeName))));
castFunctionNameToTypeSignature.put(castFunctionName, castFunctionTypeSignature);
}
return castFunctionName;
}
/**
* Create a LocalDefn for a let var cast from an existing var.
* @param outerTypeVarName
* @param outerForeignTypeName
* @param letVarName
* @param innerForeignTypeName
* @return the local definition
*/
private SourceModel.LocalDefn[]
makeOuterForeignTypeToInnerForeignTypeLetVar (
String outerTypeVarName,
String outerForeignTypeName,
String letVarName,
String innerForeignTypeName) {
SourceModel.LocalDefn[] localDefns = new SourceModel.LocalDefn[2];
TypeExprDefn foreignTypeExprDefn =
TypeExprDefn.TypeCons.make(Name.TypeCons.makeUnqualified(innerForeignTypeName));
SourceModel.CALDoc.TextSegment.Plain textSegment =
SourceModel.CALDoc.TextSegment.Plain.make(
"Cast the " + outerForeignTypeName + " value to a " + innerForeignTypeName);
SourceModel.CALDoc.TextBlock description =
SourceModel.CALDoc.TextBlock.make(new SourceModel.CALDoc.TextSegment.TopLevel[]{textSegment});
CALDoc.Comment.Function caldocComment =
CALDoc.Comment.Function.make(
description,
null);
SourceModel.LocalDefn.Function.TypeDeclaration localTypeDecl =
LocalDefn.Function.TypeDeclaration.make(
caldocComment,
letVarName,
TypeSignature.make(foreignTypeExprDefn));
String castFunctionName = makeCastFunction(outerForeignTypeName, innerForeignTypeName);
Expr letVarDef =
Expr.Application.make(
new Expr[]{
Expr.Var.make(Name.Function.makeUnqualified(castFunctionName)),
Expr.Var.makeUnqualified(outerTypeVarName)});
letVarDef =
Expr.Application.make(
new Expr[]{
Expr.Var.make(CAL_Prelude.Functions.eager),
letVarDef});
SourceModel.LocalDefn.Function.Definition localDefnFctn =
LocalDefn.Function.Definition.make(letVarName, null, letVarDef);
localDefns[0] = localTypeDecl;
localDefns[1] = localDefnFctn;
return localDefns;
}
/**
* Build up function type. a -> b -> c ...
* @param javaTypes
* @return a TypeExprDefn
*/
private SourceModel.TypeExprDefn getTypeExprDefn (JavaTypeName[] javaTypes) throws UnableToResolveForeignEntityException {
// First try to find a type constructor for each java type;
TypeConstructor typeConstructors[] = new TypeConstructor[javaTypes.length];
findTypeConstructorForJavaClass(javaTypes, typeConstructors);
Name.TypeCons typeConstructorNames[] = new Name.TypeCons[javaTypes.length];
for (int i = 0, n = javaTypes.length; i < n; ++i) {
if (typeConstructors[i] != null) {
if (typeConstructors[i].getScope().equals(Scope.PUBLIC)) {
typeConstructorNames[i] = Name.TypeCons.make(typeConstructors[i].getName());
} else {
// Need to reproduce this foreign type declaration
// in this module.
additionalForeignTypeDeclarations.put(typeConstructors[i].getName(), typeConstructors[i]);
typeConstructorNames[i] = Name.TypeCons.makeUnqualified(typeConstructors[i].getName().getUnqualifiedName());
}
} else
if (javaTypes[i].equals(JavaTypeName.BOOLEAN)){
typeConstructorNames[i] = Name.TypeCons.make(CAL_Prelude.TypeConstructors.Boolean);
} else {
// Couldn't find a type constructor for the Java class.
// Build up a Name.TypeCons using our naming pattern.
// try to find a module containing the base CAL type.
String baseCALTypeName = javaTypes[i].getUnqualifiedJavaSourceName();
if (baseCALTypeName.endsWith("_")) {
baseCALTypeName = baseCALTypeName.substring(0, baseCALTypeName.length()-1);
}
ModuleName moduleName = findModuleContainingType(baseCALTypeName);
if (moduleName == null) {
typeConstructorNames[i] = Name.TypeCons.makeUnqualified("J" + javaTypes[i].getUnqualifiedJavaSourceName());
} else {
moduleName = ModuleName.make(moduleName.toString() + "_JavaIO");
typeConstructorNames[i] = Name.TypeCons.make(moduleName, "J" + javaTypes[i].getUnqualifiedJavaSourceName());
}
}
}
TypeExprDefn codomain = TypeExprDefn.TypeCons.make(typeConstructorNames[typeConstructorNames.length - 1]);
for (int i = typeConstructorNames.length - 2; i >= 0; i--) {
TypeExprDefn domain = TypeExprDefn.TypeCons.make(typeConstructorNames[i]);
codomain = TypeExprDefn.Function.make(domain, codomain);
}
return codomain;
}
private final void updateInputableImports (TypeExpr typeExpr) {
TypeConsApp tca = typeExpr.rootTypeConsApp();
if (tca != null) {
for (int i = 0, n = tca.getNArgs(); i < n; ++i) {
updateInputableImports(tca.getArg(i));
}
}
if (typeExpr.isListType()) {
if (typeExpr.rootTypeConsApp() == null) {
((TypeApp)typeExpr).getOperandType();
}
} else if (typeExpr.isTupleType()) {
RecordType rt = (RecordType)typeExpr;
Map<FieldName, TypeExpr> hasFieldsMap = rt.getHasFieldsMap();
for (Iterator<TypeExpr> it = hasFieldsMap.values().iterator(); it.hasNext();) {
updateInputableImports(it.next());
}
} else if (!typeExpr.isFunctionType()) {
if (tca != null) {
TypeConstructor tc = tca.getRoot();
// Ignore functions
ModuleTypeInfo m = findModuleWithClassInstance(CAL_Prelude.TypeClasses.Inputable, tc.getName());
if (m != null) {
inputableImports.add(m.getModuleName());
} else if (!tc.getName().getModuleName().equals(module.getModuleName())){
// Create a module name based on our naming.
ModuleName mn =
ModuleName.make(
tc.getName().getModuleName().toString() + "_JavaIO");
inputableImports.add(mn);
}
}
}
}
private final ModuleTypeInfo findModuleWithClassInstance (QualifiedName className, QualifiedName typeConstructorName) {
return findModuleWithClassInstance (className, typeConstructorName, module, new LinkedHashSet<ModuleTypeInfo>());
}
private final ModuleTypeInfo findModuleWithClassInstance (QualifiedName className, QualifiedName typeConstructorName, ModuleTypeInfo module, Set<ModuleTypeInfo> visitedModules) {
if (visitedModules.contains(module)) {
return null;
}
visitedModules.add(module);
for (int i = 0, n = module.getNClassInstances(); i < n; ++i) {
ClassInstance ci = module.getNthClassInstance(i);
if (ci.getTypeClass().getName().equals(className)) {
// Check the instance type
TypeExpr instanceTypeExpr = ci.getType();
TypeConsApp tca = instanceTypeExpr.rootTypeConsApp();
if (tca != null) {
if (tca.getRoot().getName().equals(typeConstructorName)) {
return module;
}
}
}
}
for (int i = 0, n = module.getNImportedModules(); i < n; ++i) {
ModuleTypeInfo mti = module.getNthImportedModule(i);
ModuleTypeInfo ci = findModuleWithClassInstance(className, typeConstructorName, mti, visitedModules);
if (ci != null) {
return ci;
}
}
return null;
}
private final ModuleName findModuleContainingType (String typeName) {
return findModuleContainingType (new LinkedHashSet<ModuleName>(), typeName, module);
}
private final ModuleName findModuleContainingType(Set<ModuleName> visitedModules, String typeName, ModuleTypeInfo module) {
if (visitedModules.contains(module.getModuleName())) {
return null;
}
visitedModules.add(module.getModuleName());
for (int i = 0, n = module.getNTypeConstructors(); i < n; ++i) {
if (module.getNthTypeConstructor(i).getName().getUnqualifiedName().equals(typeName)) {
return module.getModuleName();
}
}
for (int i = 0, n = module.getNImportedModules(); i < n; ++i) {
ModuleTypeInfo importedModule = module.getNthImportedModule(i);
ModuleName mn = findModuleContainingType(visitedModules, typeName, importedModule);
if (mn != null) {
return mn;
}
}
return null;
}
/**
* Try to find a CAL type constructor corresponding to the named java type.
* @param javaTypes
* @param typeConstructors
*/
private void findTypeConstructorForJavaClass (JavaTypeName[] javaTypes, TypeConstructor[] typeConstructors) throws UnableToResolveForeignEntityException {
findTypeConstructorForJavaClass (javaTypes, typeConstructors, module, new LinkedHashSet<ModuleTypeInfo>());
}
/**
* Try to find a CAL type constructor corresponding to the named java type.
* @param javaTypes
* @param typeConstructors
* @param module
* @param visitedModules
*/
private void findTypeConstructorForJavaClass (JavaTypeName[] javaTypes, TypeConstructor[] typeConstructors, ModuleTypeInfo module, Set<ModuleTypeInfo> visitedModules) throws UnableToResolveForeignEntityException {
if (visitedModules.contains(module)) {
return;
}
visitedModules.add(module);
for (int i = 0, n = module.getNTypeConstructors(); i < n; ++i) {
TypeConstructor tc = module.getNthTypeConstructor(i);
ForeignTypeInfo fti = tc.getForeignTypeInfo();
if (fti == null) {
continue;
}
JavaTypeName foreignType = JavaTypeName.make(fti.getForeignType());
for (int j = 0, k = javaTypes.length; j < k; ++j) {
if (typeConstructors[j] != null) {
continue;
}
if (foreignType.equals(javaTypes[j])) {
typeConstructors[j] = tc;
}
}
}
for (int i = 0, n = module.getNImportedModules(); i < n; ++i) {
ModuleTypeInfo importedModule = module.getNthImportedModule(i);
findTypeConstructorForJavaClass(javaTypes, typeConstructors, importedModule, visitedModules);
}
}
private void makeForeignClassDeclaration (List<TopLevelSourceElement> topLevelDefnsList) {
// Declare the java class as a foreign type.
// ex. data foreign unsafe import jvm "org.olap.CubeType" public JCubeType;
SourceModel.CALDoc.Comment.TypeCons foreignTypeComment;
{
SourceModel.CALDoc.TextSegment.Plain textSegment =
SourceModel.CALDoc.TextSegment.Plain.make(
"\nForeign type for " + javaClass.getClassName().getFullJavaSourceName() + ".\n" +
typeConstructor.getName() + " outputs to and inputs from this type.\n");
SourceModel.CALDoc.TextBlock textBlock =
SourceModel.CALDoc.TextBlock.make(new SourceModel.CALDoc.TextSegment.TopLevel[]{textSegment});
foreignTypeComment =
SourceModel.CALDoc.Comment.TypeCons.make(
textBlock,
null);
}
SourceModel.TypeConstructorDefn.ForeignType foreignType =
TypeConstructorDefn.ForeignType.make(
foreignTypeComment,
typeConstructorInfo.calForeignTypeName,
Scope.PUBLIC,
true,
javaClass.getClassName().getFullJavaSourceName(),
Scope.PUBLIC,
true,
new Name.TypeClass[]{Name.TypeClass.make(CAL_Prelude.TypeClasses.Inputable), Name.TypeClass.make(CAL_Prelude.TypeClasses.Outputable)});
topLevelDefnsList.add (foreignType);
}
private void makeForeignClassDeclaration (DataConstructor dc, List<TopLevelSourceElement> topLevelDefnsList) {
DataConstructorInfo dcInfo = (DataConstructorInfo)typeConstructorInfo.dataConstructorInfo.get(dc);
SourceModel.CALDoc.Comment.TypeCons foreignTypeComment;
{
SourceModel.CALDoc.TextSegment.Plain textSegment =
SourceModel.CALDoc.TextSegment.Plain.make(
"\nForeign type for " + javaClass.getClassName().getFullJavaSourceName() + "." + dcInfo.innerClassName + ".\n" +
"This Java class corresponds to the " + dc.getName().getUnqualifiedName() + " data constructor.\n");
SourceModel.CALDoc.TextBlock textBlock =
SourceModel.CALDoc.TextBlock.make(new SourceModel.CALDoc.TextSegment.TopLevel[]{textSegment});
foreignTypeComment =
SourceModel.CALDoc.Comment.TypeCons.make(
textBlock,
null);
}
// Declare the java class as a foreign type.
// ex. data foreign unsafe import jvm "org.olap.CubeType" public JCubeType;
SourceModel.TypeConstructorDefn.ForeignType foreignType =
TypeConstructorDefn.ForeignType.make(
foreignTypeComment,
dcInfo.calForeignTypeName,
Scope.PUBLIC,
true,
javaClass.getClassName().getFullJavaSourceName() + "$" + dcInfo.innerClassName,
Scope.PUBLIC,
true,
new Name.TypeClass[]{Name.TypeClass.make(CAL_Prelude.TypeClasses.Inputable), Name.TypeClass.make(CAL_Prelude.TypeClasses.Outputable)});
topLevelDefnsList.add (foreignType);
}
private void importGetDCOrdinal (List<TopLevelSourceElement> topLevelDefnsList) {
// Bring in the 'getDCOrdinal()' method of the Java class.
SourceModel.TypeSignature getDCOrdinalTypeSignature =
TypeSignature.make(
TypeExprDefn.Function.make(
TypeExprDefn.TypeCons.make(Name.TypeCons.makeUnqualified(typeConstructorInfo.calForeignTypeName)),
INT_TYPE_EXPR_DEFN));
SourceModel.CALDoc.Comment.Function getDCOrdinalComment;
{
SourceModel.CALDoc.TextSegment.Plain textSegment =
SourceModel.CALDoc.TextSegment.Plain.make(
"\nRetrieve the ordinal value from an instance of " + typeConstructorInfo.calForeignTypeName + ".\n" +
"The ordinal can be used to determine which data constructor the " + typeConstructorInfo.calForeignTypeName + "\n" +
"instance corresponds to.");
SourceModel.CALDoc.TextBlock textBlock =
SourceModel.CALDoc.TextBlock.make(new SourceModel.CALDoc.TextSegment.TopLevel[]{textSegment});
getDCOrdinalComment =
SourceModel.CALDoc.Comment.Function.make(
textBlock,
null);
}
SourceModel.FunctionDefn getDCOrdinal =
FunctionDefn.Foreign.make(
getDCOrdinalComment,
"j" + typeConstructorInfo.calForeignTypeName.substring(1) + "_getDCOrdinal",
Scope.PRIVATE,
true,
"method getDCOrdinal",
getDCOrdinalTypeSignature);
topLevelDefnsList.add(getDCOrdinal);
}
private void generateEnumerationIO (List<TopLevelSourceElement> topLevelDefnsList) {
// Declare the java class as a foreign type.
// ex. data foreign unsafe import jvm "org.olap.CubeType" public JCubeType;
makeForeignClassDeclaration(topLevelDefnsList);
TypeExprDefn.TypeCons jClassTypeExpr =
TypeExprDefn.TypeCons.make(Name.TypeCons.makeUnqualified(typeConstructorInfo.calForeignTypeName));
// String -> String;
Map<String, String> enumFieldFunctionNames = new LinkedHashMap<String, String>();
// Now we need to bring in each instance of the enumeration class.
// These will be public static fields of the class.
for (int i = 0, n = typeConstructor.getNDataConstructors(); i < n; ++i) {
DataConstructor dc = typeConstructor.getNthDataConstructor(i);
String enumFieldName = createEnumFieldName(dc.getName().getUnqualifiedName());
// Example:
// foreign unsafe import jvm "static field org.olap.CubeType.TYPE"
// public jCubeType_TYPE :: JCubeType;
SourceModel.TypeSignature typeSignature =
TypeSignature.make(jClassTypeExpr);
String functionName = "j" + javaClass.getClassName().getUnqualifiedJavaSourceName() + "_" + enumFieldName;
SourceModel.CALDoc.Comment.Function foreignFieldComment;
{
SourceModel.CALDoc.TextSegment.Plain textSegment =
SourceModel.CALDoc.TextSegment.Plain.make(
"\nThe Java static field " + javaClass.getClassName().getUnqualifiedJavaSourceName() + "." + enumFieldName + ".\n" +
"This field is used to represent instances of the data constructor " + dc.getName().getUnqualifiedName() + ".\n");
SourceModel.CALDoc.TextBlock textBlock =
SourceModel.CALDoc.TextBlock.make(new SourceModel.CALDoc.TextSegment.TopLevel[]{textSegment});
foreignFieldComment =
SourceModel.CALDoc.Comment.Function.make(
textBlock,
null);
}
SourceModel.FunctionDefn enumInstance =
FunctionDefn.Foreign.make(
foreignFieldComment,
functionName,
Scope.PRIVATE,
true,
"static field " + javaClass.getClassName().getFullJavaSourceName() + "." + enumFieldName,
typeSignature);
topLevelDefnsList.add (enumInstance);
enumFieldFunctionNames.put(dc.getName().getUnqualifiedName(), functionName);
}
// Bring in the getDCOrdinal() method.
importGetDCOrdinal(topLevelDefnsList);
// Create an function that converts a JCubeType_ to a CubeType
// inputCubeType :: JCubeType_ -> CubeType;
// public inputCubeType jCubeType_ =
// case (jCubeType_getDCOrdinal jCubeType_) of
// 0 -> ...;
// 1 -> ...;
String inputArgumentName = "j" + javaClass.getClassName().getUnqualifiedJavaSourceName();
String inputFunctionName = "input" + typeConstructor.getName().getUnqualifiedName();
Expr.Var inputArgument = Expr.Var.make(Name.Function.makeUnqualified(inputArgumentName));
SourceModel.Expr condition =
Expr.Application.make(
new Expr[]{Expr.Var.make(Name.Function.makeUnqualified("j" + javaClass.getClassName().getUnqualifiedJavaSourceName() + "_getDCOrdinal")),
inputArgument});
SourceModel.Expr.Case.Alt caseAlts[] =
new SourceModel.Expr.Case.Alt[typeConstructor.getNDataConstructors()];
for (int i = 0, n = caseAlts.length; i < n; ++i) {
DataConstructor dc = typeConstructor.getNthDataConstructor(i);
caseAlts[i] =
Expr.Case.Alt.UnpackInt.make(
new BigInteger[]{BigInteger.valueOf(dc.getOrdinal())},
Expr.DataCons.make(Name.DataCons.make(dc.getName())));
}
SourceModel.Expr.Case body =
Expr.Case.make(condition, caseAlts);
SourceModel.FunctionDefn.Algebraic inputFunction =
FunctionDefn.Algebraic.make(
inputFunctionName,
Scope.PUBLIC,
new SourceModel.Parameter[]{Parameter.make(inputArgumentName, false)},
body);
TypeExprDefn inputFunctionType =
TypeExprDefn.Function.make(
TypeExprDefn.TypeCons.make(Name.TypeCons.makeUnqualified(typeConstructorInfo.calForeignTypeName)),
TypeExprDefn.TypeCons.make(Name.TypeCons.makeUnqualified(typeConstructor.getName().getUnqualifiedName())));
SourceModel.CALDoc.Comment.Function inputFunctionComment;
{
SourceModel.CALDoc.TextSegment.Plain textSegment =
SourceModel.CALDoc.TextSegment.Plain.make(
"\nInput an instance of " + typeConstructor.getName().getUnqualifiedName() + ".\n" +
"Translates an instance of " + typeConstructorInfo.calForeignTypeName + " to\n" +
"an instance of "+ typeConstructor.getName().getUnqualifiedName() + ".");
SourceModel.CALDoc.TextBlock textBlock =
SourceModel.CALDoc.TextBlock.make(new SourceModel.CALDoc.TextSegment.TopLevel[]{textSegment});
inputFunctionComment =
SourceModel.CALDoc.Comment.Function.make(
textBlock,
null);
}
SourceModel.FunctionTypeDeclaration inputFunctionTypeDecl =
FunctionTypeDeclaration.make(
inputFunctionComment,
inputFunctionName,
TypeSignature.make(inputFunctionType));
topLevelDefnsList.add(inputFunctionTypeDecl);
topLevelDefnsList.add(inputFunction);
// Create a function which creates a CubeType from a JObject.
String inputFromJObjectFunctionName = inputFunctionName + "FromJObject";
createInputFromJObjectFunction(inputFromJObjectFunctionName, inputFunctionName, topLevelDefnsList);
// Now we need to declare an instance of inputable.
// instance Inputable CubeType where
// input = jObjectToCubeType;
// ;
SourceModel.InstanceDefn instanceDefn =
InstanceDefn.make(
Name.TypeClass.make(CAL_Prelude.TypeClasses.Inputable),
InstanceDefn.InstanceTypeCons.TypeCons.make(Name.TypeCons.make(typeConstructor.getName()), null),
null,
new InstanceDefn.InstanceMethod[]{
InstanceDefn.InstanceMethod.make(CAL_Prelude.Functions.input.getUnqualifiedName(), Name.Function.makeUnqualified(inputFromJObjectFunctionName))
});
topLevelDefnsList.add(instanceDefn);
// Create a conversion function for CAL type to corresponding foreign type.
// cubeTypeToJCubeType :: CubeType -> JCubeType_;
// cubeTypeToJCubeType dcInstance =
// case dcInstance of
// DC1 -> unsafeCoerce jCubeType_DC1;
// ...
// ;
condition =
Expr.Var.makeUnqualified("dcInstance");
caseAlts =
new SourceModel.Expr.Case.Alt[typeConstructor.getNDataConstructors()];
for (int i = 0, n = caseAlts.length; i < n; ++i) {
DataConstructor dc = typeConstructor.getNthDataConstructor(i);
Expr enumFieldFunction =
Expr.Var.make(
Name.Function.makeUnqualified((String)enumFieldFunctionNames.get(dc.getName().getUnqualifiedName())));
caseAlts[i] =
Expr.Case.Alt.UnpackDataCons.make(
Name.DataCons.makeUnqualified(dc.getName().getUnqualifiedName()),
enumFieldFunction);
}
body =
Expr.Case.make(condition, caseAlts);
makeOutputableInstance(typeConstructor, body, topLevelDefnsList);
}
/**
* @return the inputableImports
*/
private Set<ModuleName> getInputableImports() {
return inputableImports;
}
/**
* Create a wrapper function around the input... function.
* The wrapper function will take a JObject as argument rather than
* the foreign type corresponding to the CAL type.
* This new function will be used for the Inputable class method input.
* @param inputFromJObjectFunctionName
* @param inputFunctionName
* @param topLevelDefnsList
*/
private void createInputFromJObjectFunction (
String inputFromJObjectFunctionName,
String inputFunctionName,
List<TopLevelSourceElement> topLevelDefnsList) {
// Now create a function that inputs from a JObject.
// inputCubeTypeFromJObject :: JObject -> CubeType;
// inputCubeTypeFromJObject jObject = inputCubeType (input jObject);
String inputArgumentName = "jObject";
Expr.Var inputArgument = Expr.Var.make(Name.Function.makeUnqualified(inputArgumentName));
SourceModel.Expr body = CAL_Prelude.Functions.input(inputArgument);
body =
Expr.Application.make(
new Expr[]{Expr.Var.make(Name.Function.makeUnqualified(inputFunctionName)),
body});
SourceModel.FunctionDefn.Algebraic inputFunction =
FunctionDefn.Algebraic.make(
inputFromJObjectFunctionName,
Scope.PRIVATE,
new SourceModel.Parameter[]{Parameter.make("jObject", false)},
body);
TypeExprDefn inputFunctionType =
TypeExprDefn.Function.make(
JOBJECT_TYPE_EXPR_DEFN,
TypeExprDefn.TypeCons.make(Name.TypeCons.makeUnqualified(typeConstructor.getName().getUnqualifiedName())));
SourceModel.CALDoc.Comment.Function inputFunctionComment;
{
SourceModel.CALDoc.TextSegment.Plain textSegment =
SourceModel.CALDoc.TextSegment.Plain.make(
"\nInput an instance of " + typeConstructor.getName().getUnqualifiedName() + ".\n" +
"Translates an instance of JObject to\n" +
"an instance of "+ typeConstructor.getName().getUnqualifiedName() + ".");
SourceModel.CALDoc.TextBlock textBlock =
SourceModel.CALDoc.TextBlock.make(new SourceModel.CALDoc.TextSegment.TopLevel[]{textSegment});
inputFunctionComment =
SourceModel.CALDoc.Comment.Function.make(
textBlock,
null);
}
SourceModel.FunctionTypeDeclaration inputFunctionTypeDecl =
FunctionTypeDeclaration.make(
inputFunctionComment,
inputFromJObjectFunctionName,
TypeSignature.make(inputFunctionType));
topLevelDefnsList.add(inputFunctionTypeDecl);
topLevelDefnsList.add(inputFunction);
}
}
private final class JavaDataClassGenerator {
private final String targetPackage;
private final TypeConstructorInfo typeConstructorInfo;
/** List of LogRecord */
private final List<LogRecord> logMessages = new ArrayList<LogRecord>();
/**
* JavaDataClassGenerator constructor.
* @param targetPackage
* @param typeConstructorInfo
*/
private JavaDataClassGenerator (
String targetPackage,
TypeConstructorInfo typeConstructorInfo) {
this.typeConstructorInfo = typeConstructorInfo;
this.targetPackage = targetPackage;
}
JavaClassRep generateClassForType () {
JavaTypeName typeConstructorClassName =
JavaTypeName.make(targetPackage + "." + typeConstructorInfo.javaClassName, false);
TypeConstructor typeConstructor = typeConstructorInfo.typeConstructor;
JavaClassRep outerTypeDefinition =
new OuterTypeDefinitionGenerator().generateOuterTypeDefinition(typeConstructorClassName);
// Now we need to generate inner classes for each data constructor.
// This is only necessary if the data type is not an enumeration.
if (!typeConstructorInfo.isEnumerationType) {
for (int i = 0, n = typeConstructor.getNDataConstructors(); i < n; ++i) {
DataConstructor dc = typeConstructor.getNthDataConstructor(i);
JavaClassRep dcClass = new InnerDCClassGenerator().generateInnerDCClass(dc, typeConstructorInfo, typeConstructorClassName);
outerTypeDefinition.addInnerClass(dcClass);
}
}
return outerTypeDefinition;
}
private final class InnerDCClassGenerator {
private final JavaClassRep generateInnerDCClass (
DataConstructor dc,
TypeConstructorInfo typeConstructorInfo,
JavaTypeName typeConstructorClassName) {
int classModifiers = Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL;
DataConstructorInfo dcInfo = (DataConstructorInfo)typeConstructorInfo.dataConstructorInfo.get(dc);
JavaTypeName dcClassTypeName =
JavaTypeName.make(
typeConstructorClassName.getFullJavaSourceName() + "$" + dcInfo.innerClassName, false);
JavaClassRep dcClass =
new JavaClassRep(
dcClassTypeName,
typeConstructorClassName,
classModifiers,
IO_Source_Generator.EMPTY_TYPE_NAME_ARRAY);
JavaDocComment jdc =
new JavaDocComment ("This class represents instances of the CAL data constructor " + dc.getName() + ".");
dcClass.setJavaDoc(jdc);
createFields (dcClass, dc, typeConstructorInfo);
dcClass.addConstructor(createConstructor(dc, typeConstructorInfo, dcClassTypeName));
dcClass.addMethod(createMethod_getDCName(dc.getName().getUnqualifiedName()));
for (FieldName fieldName : dcInfo.fieldTypeNames.keySet()) {
if (typeConstructorInfo.commonFieldNames.contains(fieldName)) {
continue;
}
JavaTypeName fieldType = (JavaTypeName)dcInfo.fieldTypeNames.get(fieldName);
String javaFieldName = (String)typeConstructorInfo.fieldJavaNames.get(fieldName);
String accessorName = (String)typeConstructorInfo.fieldJavaAccessorMethodNames.get(fieldName);
JavaMethod getter = createMethod_getField(accessorName, javaFieldName, fieldType, true);
dcClass.addMethod(getter);
}
dcClass.addMethod(createMethod_getDCOrdinal(dc.getOrdinal()));
dcClass.addMethod (JavaDataClassGenerator.this.createMethod_toString(dcInfo.allFieldNames, dcInfo.fieldTypeNames));
dcClass.addMethod (
JavaDataClassGenerator.this.createMethod_equals(
dcClassTypeName,
dcInfo.allFieldNames,
dcInfo.fieldTypeNames));
dcClass.addMethod (
JavaDataClassGenerator.this.createMethod_hashCode(
dcInfo.allFieldNames,
dcInfo.fieldTypeNames));
return dcClass;
}
private final JavaConstructor createConstructor(
DataConstructor dc,
TypeConstructorInfo typeConstructorInfo,
JavaTypeName dcClassTypeName) {
DataConstructorInfo dci = (DataConstructorInfo)typeConstructorInfo.dataConstructorInfo.get(dc);
String[] argNames = new String [dc.getArity()];
JavaTypeName[] argTypes = new JavaTypeName [argNames.length];
Block constructorBody = new Block();
for (int i = 0, n = dc.getArity(); i < n; ++i) {
FieldName fn = (FieldName)dc.getNthFieldName(i);
JavaTypeName type = (JavaTypeName)dci.fieldTypeNames.get(fn);
String fieldName = (String)typeConstructorInfo.fieldJavaNames.get(fn);
String argName = fieldName+"$";
argNames[i] = argName;
argTypes[i] = type;
if (!typeConstructorInfo.commonFieldNames.contains(fn)) {
JavaExpression.JavaField.Instance field =
new JavaExpression.JavaField.Instance(null, fieldName, type);
JavaExpression assign = new Assignment (field, new MethodVariable(argName));
constructorBody.addStatement(new ExpressionStatement(assign));
}
}
String constructorName = dcClassTypeName.getUnqualifiedJavaSourceName();
int index = constructorName.lastIndexOf('.');
if (index > -1) {
constructorName = constructorName.substring(index + 1);
}
JavaConstructor constructor;
if (typeConstructorInfo.commonFieldNames.size() > 0) {
JavaExpression superArgValues[] = new JavaExpression[typeConstructorInfo.commonFieldNames.size()];
JavaTypeName superArgTypes[] = new JavaTypeName[superArgValues.length];
int i = 0;
for (FieldName superFieldName : typeConstructorInfo.commonFieldNames) {
JavaTypeName fieldType = (JavaTypeName)dci.fieldTypeNames.get(superFieldName);
String fieldName = (String)typeConstructorInfo.fieldJavaNames.get(superFieldName);
String argName = fieldName+"$";
JavaExpression superArgValue = new MethodVariable(argName);
superArgValues[i] = superArgValue;
superArgTypes[i] = fieldType;
++i;
}
constructor = new JavaConstructor (Modifier.PUBLIC, argNames, argTypes, constructorName, superArgValues, superArgTypes);
} else {
constructor = new JavaConstructor (Modifier.PUBLIC, argNames, argTypes, constructorName);
}
constructor.addStatement(constructorBody);
return constructor;
}
private final void createFields (JavaClassRep dcClass, DataConstructor dc, TypeConstructorInfo typeConstructorInfo) {
DataConstructorInfo dci = (DataConstructorInfo)typeConstructorInfo.dataConstructorInfo.get(dc);
// Add a volatile int field for the hashcode.
JavaFieldDeclaration hashCodeFieldDecl =
new JavaFieldDeclaration(
Modifier.PRIVATE | Modifier.VOLATILE,
JavaTypeName.INT,
HASH_CODE_FIELD_NAME,
LiteralWrapper.make(new Integer(0)));
hashCodeFieldDecl.setJavaDoc(new JavaDocComment("Lazily initialized, cached hashCode."));
dcClass.addFieldDeclaration(hashCodeFieldDecl);
for (int i = 0, n = dc.getArity(); i < n; ++i) {
FieldName fieldName = dc.getNthFieldName(i);
if (typeConstructorInfo.commonFieldNames.contains(fieldName)) {
continue;
}
JavaTypeName type = (JavaTypeName)dci.fieldTypeNames.get(fieldName);
String javaFieldName = (String)typeConstructorInfo.fieldJavaNames.get(fieldName);
int modifiers = Modifier.FINAL | Modifier.PRIVATE;
JavaFieldDeclaration fieldDec = new JavaFieldDeclaration (modifiers, type, javaFieldName, null);
dcClass.addFieldDeclaration(fieldDec);
}
}
private final JavaMethod createMethod_getDCOrdinal(int ordinal) {
JavaMethod javaMethod = new JavaMethod(Modifier.PUBLIC, JavaTypeName.INT, GET_DC_ORDINAL_METHOD_NAME);
// return dc ordinal.
javaMethod.addStatement (new ReturnStatement(LiteralWrapper.make (new Integer (ordinal))));
JavaDocComment jdc =
new JavaDocComment ("@return the ordinal of this instance of " + typeConstructorInfo.javaClassName);
javaMethod.setJavaDocComment(jdc);
return javaMethod;
}
private final JavaMethod createMethod_getDCName(String dcName) {
int modifiers = Modifier.PUBLIC | Modifier.FINAL;
JavaMethod javaMethod = new JavaMethod(modifiers, JavaTypeName.STRING, GET_DC_NAME_METHOD_NAME);
javaMethod.addStatement(new ReturnStatement(LiteralWrapper.make(dcName)));
JavaDocComment jdc =
new JavaDocComment ("@return the name of the data constructor corresponding to this instance of " + typeConstructorInfo.javaClassName);
javaMethod.setJavaDocComment(jdc);
return javaMethod;
}
}
private final class OuterTypeDefinitionGenerator {
/**
* Generate the java class representation of the data type for which this builder is responsible.
* The generated representation will be the outermost class definition only -- no inner classes will have been generated.
* @param typeConstructorClassName
* @return class rep
*/
private JavaClassRep generateOuterTypeDefinition(JavaTypeName typeConstructorClassName) {
int classModifiers = Modifier.PUBLIC;
if (typeConstructorInfo.isEnumerationType) {
classModifiers |= Modifier.FINAL;
} else {
classModifiers |= Modifier.ABSTRACT;
}
JavaTypeName superClassTypeName = JavaTypeName.OBJECT;
// No interfaces are implemented
JavaTypeName[] interfaces = IO_Source_Generator.EMPTY_TYPE_NAME_ARRAY;
JavaClassRep javaClassRep =
new JavaClassRep(
typeConstructorClassName,
superClassTypeName,
classModifiers,
interfaces);
// 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 (" + javaClassRep.getClassName().getUnqualifiedJavaSourceName() + ".java)");
mlc.addLine("was generated from CAL type constructor: " + typeConstructorInfo.typeConstructor.getName() + ".");
mlc.addLine(" ");
mlc.addLine("Creation date: " + new Date());
mlc.addLine("--!>");
mlc.addLine(" ");
javaClassRep.setComment(mlc);
// We want to insert the CALDoc comment for the type as a JavaDoc comment for the class.
JavaDocComment jdc =
new JavaDocComment ("This class (" + javaClassRep.getClassName().getUnqualifiedJavaSourceName() + ") provides a Java data class corresponding to");
jdc.addLine("the CAL type constructor " + typeConstructorInfo.typeConstructor.getName() + ".");
jdc.addLine("");
if (typeConstructorInfo.isEnumerationType) {
jdc.addLine("This type constructor is an enumeration. (i.e. all data constructors have no fields)");
jdc.addLine("The individual data constructors are represented by instances of " + javaClassRep.getClassName().getUnqualifiedJavaSourceName() + " held ");
jdc.addLine("in static final fields.");
} else {
jdc.addLine("Because the type constructor has only one data constructor, with the same name");
jdc.addLine("as the type constructor this class also represents instances of the data constructor.");
}
javaClassRep.setJavaDoc(jdc);
// Generate fields in this class for any fields which are common to all data constructors.
createFields(javaClassRep, typeConstructorInfo);
if (typeConstructorInfo.isEnumerationType) {
// Add instance fields for name and ordinal
// Add static final fields for each data constructor
createFields_Enumeration(typeConstructorInfo.typeConstructor, javaClassRep, typeConstructorClassName);
// Add a function which takes an int value for the
// ordinal and returns the corresponding enum instance.
javaClassRep.addMethod(createMethod_fromOrdinal (typeConstructorClassName));
}
if (typeConstructorInfo.commonFieldNames.size() > 0) {
javaClassRep.addConstructor(createConstructor(typeConstructorInfo, typeConstructorClassName));
} else {
javaClassRep.addConstructor(createConstructor_default(typeConstructorClassName, typeConstructorInfo.isEnumerationType));
}
// Create a get_FieldName method for each unique field name in the set of
// data constructors.
// If the field is common to all DCs it will be implemented at this
// level. Otherwise the implementation at this level throws an error
// and the DC classes are expected to override it.
for (FieldName fieldName : typeConstructorInfo.commonFieldNames) {
String javaFieldName = (String)typeConstructorInfo.fieldJavaNames.get(fieldName);
Set<JavaTypeName> fieldTypes = typeConstructorInfo.allFieldTypeNames.get(fieldName);
for (JavaTypeName type : fieldTypes) {
String accessorMethodName = (String)typeConstructorInfo.fieldJavaAccessorMethodNames.get(fieldName);
JavaMethod getter = createMethod_getField (
accessorMethodName,
javaFieldName,
type,
true);
javaClassRep.addMethod(getter);
getter.setJavaDocComment(new JavaDocComment ("@return " + fieldName));
}
}
javaClassRep.addMethod(createMethod_getDCName(typeConstructorInfo));
javaClassRep.addMethod(createMethod_getDCOrdinal(typeConstructorInfo));
JavaMethod toString = createMethod_toString();
if (toString != null) {
javaClassRep.addMethod (toString);
}
if (typeConstructorInfo.isEnumerationType) {
JavaMethod equals = JavaDataClassGenerator.this.createMethod_equals(
typeConstructorClassName,
typeConstructorInfo.commonFieldNames,
typeConstructorInfo.commonFieldTypes);
javaClassRep.addMethod (equals);
}
createMethod_hashCode (javaClassRep);
return javaClassRep;
}
private void createMethod_hashCode (JavaClassRep javaClassRep) {
if (typeConstructorInfo.isEnumerationType) {
// We can just return the ordinal.
JavaMethod hashCode =
new JavaMethod(
Modifier.PUBLIC | Modifier.FINAL,
JavaTypeName.INT,
"hashCode");
JavaExpression call_getOrdinal =
new MethodInvocation.Instance(
null,
GET_DC_ORDINAL_METHOD_NAME,
JavaTypeName.INT,
MethodInvocation.InvocationType.VIRTUAL);
hashCode.addStatement(new ReturnStatement (call_getOrdinal));
javaClassRep.addMethod(hashCode);
}
}
private JavaMethod createMethod_toString() {
JavaMethod toString;
if (typeConstructorInfo.isEnumerationType) {
toString =
new JavaMethod(
Modifier.PUBLIC | Modifier.FINAL,
JavaTypeName.STRING,
"toString");
JavaExpression getDCName =
new JavaExpression.MethodInvocation.Instance(
null,
GET_DC_NAME_METHOD_NAME,
JavaTypeName.STRING,
MethodInvocation.InvocationType.VIRTUAL);
toString.addStatement(new ReturnStatement (getDCName));
return toString;
} else {
toString = null;
}
if (toString != null) {
JavaDocComment jdc =
new JavaDocComment ("@return a String representing this instance of " + typeConstructorInfo.javaClassName);
toString.setJavaDocComment(jdc);
}
return toString;
}
private JavaMethod createMethod_fromOrdinal (JavaTypeName dataType_TypeName) {
JavaMethod fromOrdinal =
new JavaMethod(
Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL,
dataType_TypeName,
"ordinal",
JavaTypeName.INT,
true,
"fromOrdinal");
// build up a switch.
JavaStatement.SwitchStatement switchStatement =
new JavaStatement.SwitchStatement(new MethodVariable("ordinal"));
for (int i = 0, n = typeConstructorInfo.typeConstructor.getNDataConstructors(); i < n; ++i) {
DataConstructor dc = typeConstructorInfo.typeConstructor.getNthDataConstructor(i);
String enumFieldName = createEnumFieldName(dc.getName().getUnqualifiedName());
JavaField field = new JavaField.Static(dataType_TypeName, enumFieldName, dataType_TypeName);
JavaStatement.SwitchStatement.IntCaseGroup intCase =
new SwitchStatement.IntCaseGroup(
dc.getOrdinal(),
new ReturnStatement(field));
switchStatement.addCase(intCase);
}
// Throw an error if ordinal is outside accepted range.
JavaExpression message =
new JavaExpression.OperatorExpression.Binary(
JavaOperator.STRING_CONCATENATION,
LiteralWrapper.make ("Invalid ordinal " ),
new MethodVariable("ordinal"));
message =
new JavaExpression.OperatorExpression.Binary(
JavaOperator.STRING_CONCATENATION,
message,
LiteralWrapper.make(" for data type " + typeConstructorInfo.typeConstructor.getName().getUnqualifiedName()));
JavaExpression createException =
new JavaExpression.ClassInstanceCreationExpression (
UNSUPPORTED_OPERATION_TYPE_NAME,
message,
JavaTypeName.STRING);
JavaStatement throwStatement = new JavaStatement.ThrowStatement(createException);
SwitchStatement.DefaultCase defaultCase =
new SwitchStatement.DefaultCase(throwStatement);
switchStatement.addCase(defaultCase);
fromOrdinal.addStatement(switchStatement);
JavaDocComment jdc =
new JavaDocComment ("@param ordinal");
jdc.addLine("@return the instance of " + typeConstructorInfo.javaClassName + " corresponding to the given ordinal.");
fromOrdinal.setJavaDocComment(jdc);
return fromOrdinal;
}
/**
* Create fields for this class.
* @param javaClassRep
* @param typeConstructorInfo
*/
private void createFields (
JavaClassRep javaClassRep,
TypeConstructorInfo typeConstructorInfo) {
for (FieldName fieldName : typeConstructorInfo.commonFieldNames) {
Set<JavaTypeName> javaTypeNames = typeConstructorInfo.allFieldTypeNames.get(fieldName);
assert (javaTypeNames.size() == 1);
Iterator<JavaTypeName> types = javaTypeNames.iterator();
JavaTypeName type = types.next();
String javaFieldName = typeConstructorInfo.fieldJavaNames.get(fieldName);
int modifiers = Modifier.FINAL;
JavaFieldDeclaration fieldDec = new JavaFieldDeclaration (modifiers, type, javaFieldName, null);
javaClassRep.addFieldDeclaration(fieldDec);
}
}
private final JavaMethod createMethod_getDCOrdinal(TypeConstructorInfo typeConstructorInfo) {
int modifiers = Modifier.PUBLIC;
JavaMethod javaMethod = new JavaMethod(modifiers, JavaTypeName.INT, GET_DC_ORDINAL_METHOD_NAME);
if (typeConstructorInfo.isEnumerationType) {
// return instance field.
javaMethod.addStatement (new ReturnStatement (new JavaField.Instance (null, ORDINAL_FIELD_NAME, JavaTypeName.INT)));
} else {
javaMethod.addStatement (new ReturnStatement(LiteralWrapper.make (new Integer(-1))));
}
JavaDocComment jdc =
new JavaDocComment ("@return the ordinal of this instance of " + typeConstructorInfo.javaClassName);
javaMethod.setJavaDocComment(jdc);
return javaMethod;
}
private final JavaMethod createMethod_getDCName(TypeConstructorInfo typeConstructorInfo) {
int modifiers = Modifier.PUBLIC;
JavaMethod javaMethod = new JavaMethod(modifiers, JavaTypeName.STRING, GET_DC_NAME_METHOD_NAME);
if (typeConstructorInfo.isEnumerationType) {
// return the name$ field
javaMethod.addStatement (new ReturnStatement (new JavaField.Instance (null, DC_NAME_FIELD_NAME, JavaTypeName.STRING)));
} else {
javaMethod.addStatement (new ReturnStatement(LiteralWrapper.NULL));
}
JavaDocComment jdc =
new JavaDocComment ("@return the name of the data constructor corresponding to this instance of " + typeConstructorInfo.javaClassName);
javaMethod.setJavaDocComment(jdc);
return javaMethod;
}
/**
* Create a constructor for the data type class.
* @param typeConstructorInfo
* @param className
* @return the constructor for the data type class.
*/
private JavaConstructor createConstructor (
TypeConstructorInfo typeConstructorInfo,
JavaTypeName className) {
String[] argNames = new String [typeConstructorInfo.commonFieldNames.size()];
JavaTypeName[] argTypes = new JavaTypeName [argNames.length];
Block constructorBody = new Block();
int i = 0;
for (FieldName fn : typeConstructorInfo.commonFieldNames) {
Set<JavaTypeName> javaTypeNames = typeConstructorInfo.allFieldTypeNames.get(fn);
assert(javaTypeNames.size() == 1);
Iterator<JavaTypeName> types = javaTypeNames.iterator();
JavaTypeName type = types.next();
String fieldName = (String)typeConstructorInfo.fieldJavaNames.get(fn);
String argName = fieldName+"$";
argNames[i] = argName;
argTypes[i] = type;
JavaExpression.JavaField.Instance field =
new JavaExpression.JavaField.Instance(null, fieldName, type);
JavaExpression assign = new Assignment (field, new MethodVariable(argName));
constructorBody.addStatement(new ExpressionStatement(assign));
i++;
}
String constructorName = className.getUnqualifiedJavaSourceName();
int index = constructorName.lastIndexOf('.');
if (index > -1) {
constructorName = constructorName.substring(index + 1);
}
JavaConstructor constructor;
if (!typeConstructorInfo.isEnumerationType) {
constructor = new JavaConstructor (Modifier.PRIVATE, argNames, argTypes, constructorName);
} else {
constructor = new JavaConstructor (Modifier.PUBLIC, argNames, argTypes, constructorName);
}
constructor.addStatement(constructorBody);
return constructor;
}
/**
* Create the default constructor for the data type class.
* @param className
* @param isEnumeration
* @return the constructor.
*/
private final JavaConstructor createConstructor_default (
JavaTypeName className,
boolean isEnumeration) {
JavaConstructor constructor;
if (isEnumeration) {
// We want a private constructor that initializes the name$ and ordinal$ fields.
constructor =
new JavaConstructor (
Modifier.PRIVATE,
new String[]{"ordinal", "name"},
new JavaTypeName[]{JavaTypeName.INT, JavaTypeName.STRING},
className.getUnqualifiedJavaSourceName());
JavaExpression assignOrdinal =
new JavaExpression.Assignment (
new JavaField.Instance(null, ORDINAL_FIELD_NAME, JavaTypeName.INT),
new MethodVariable("ordinal"));
constructor.addStatement(new ExpressionStatement(assignOrdinal));
JavaExpression assignName =
new JavaExpression.Assignment (
new JavaField.Instance(null, DC_NAME_FIELD_NAME, JavaTypeName.STRING),
new MethodVariable("name"));
constructor.addStatement(new ExpressionStatement(assignName));
} else {
constructor = new JavaConstructor (Modifier.PUBLIC, className.getUnqualifiedJavaSourceName());
}
return constructor;
}
private void createFields_Enumeration (
TypeConstructor typeConstructor,
JavaClassRep dataTypeClass,
JavaTypeName dataType_TypeName) {
JavaTypeName[] constructorArgTypes = new JavaTypeName[]{JavaTypeName.INT, JavaTypeName.STRING};
// For each data constructor create a constant int field for the ordinal.
for (int i = 0, n = typeConstructor.getNDataConstructors(); i < n; ++i) {
DataConstructor dc = typeConstructor.getNthDataConstructor(i);
String ordinalFieldName = createEnumFieldName(dc.getName().getUnqualifiedName()) + "_ORDINAL";
JavaExpression initializer = LiteralWrapper.make(new Integer(dc.getOrdinal()));
JavaFieldDeclaration fieldDec =
new JavaFieldDeclaration (
Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL,
JavaTypeName.INT, ordinalFieldName, initializer);
dataTypeClass.addFieldDeclaration(fieldDec);
JavaDocComment jdc =
new JavaDocComment ("Ordinal value corresponding to the " + dc.getName().getUnqualifiedName() + " data constructor.");
fieldDec.setJavaDoc(jdc);
}
// For each data constructor create a static field
for (int i = 0, n = typeConstructor.getNDataConstructors(); i < n; ++i) {
DataConstructor dc = typeConstructor.getNthDataConstructor(i);
String staticFieldName = createEnumFieldName(dc.getName().getUnqualifiedName());
JavaExpression initializer =
new JavaExpression.ClassInstanceCreationExpression(
dataType_TypeName,
new JavaExpression[]{
new JavaField.Static(
dataType_TypeName,
staticFieldName + "_ORDINAL",
JavaTypeName.INT),
LiteralWrapper.make(dc.getName().getUnqualifiedName())},
constructorArgTypes);
JavaFieldDeclaration fieldDec =
new JavaFieldDeclaration (
Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL,
dataType_TypeName, staticFieldName, initializer);
JavaDocComment jdc =
new JavaDocComment ("This instance of " + typeConstructorInfo.javaClassName + " representing the " + dc.getName().getUnqualifiedName() + " data constructor.");
fieldDec.setJavaDoc(jdc);
dataTypeClass.addFieldDeclaration(fieldDec);
}
// Add two instance fields.
// int ordinal$;
// String name$;
JavaFieldDeclaration fieldDec = new JavaFieldDeclaration (Modifier.PRIVATE, JavaTypeName.INT, ORDINAL_FIELD_NAME, null);
dataTypeClass.addFieldDeclaration(fieldDec);
fieldDec = new JavaFieldDeclaration (Modifier.PRIVATE, JavaTypeName.STRING, DC_NAME_FIELD_NAME, null);
dataTypeClass.addFieldDeclaration(fieldDec);
}
}
/**
* Generate an accessor function of the form RTValue get_FieldName() {}
* @param methodName
* @param fieldName
* @param fieldType
* @param implementAtThisLevel
* @return JavaMethod
*/
private final JavaMethod createMethod_getField (
String methodName,
String fieldName,
JavaTypeName fieldType,
boolean implementAtThisLevel) {
int modifiers = Modifier.PUBLIC;
if (implementAtThisLevel) {
modifiers = modifiers | Modifier.FINAL;
}
// Create the method.
JavaMethod javaMethod = new JavaMethod(modifiers, fieldType, methodName);
if (implementAtThisLevel) {
JavaField field = new JavaField.Instance (null, fieldName, fieldType);
javaMethod.addStatement(new ReturnStatement(field));
} else {
// This class should throw an error for any access. The methods will
// be overridden by derived classes for each data constructor.
JavaExpression getDCName =
new MethodInvocation.Instance(null, GET_DC_NAME_METHOD_NAME, JavaTypeName.STRING, MethodInvocation.InvocationType.VIRTUAL);
JavaExpression message =
new JavaExpression.OperatorExpression.Binary(
JavaOperator.STRING_CONCATENATION,
LiteralWrapper.make ("Cannot access field " + fieldName + " on an instance of "),
getDCName);
JavaExpression createException =
new JavaExpression.ClassInstanceCreationExpression (
UNSUPPORTED_OPERATION_TYPE_NAME,
message,
JavaTypeName.STRING);
javaMethod.addStatement(new JavaStatement.ThrowStatement(createException));
}
return javaMethod;
}
private final JavaMethod createMethod_hashCode (
Set<FieldName> fieldNames,
Map<FieldName, JavaTypeName> fieldNameToType) {
JavaMethod hashCode =
new JavaMethod (
Modifier.PUBLIC | Modifier.FINAL,
JavaTypeName.INT,
"hashCode");
JavaField hashCodeField =
new JavaField.Instance (
null,
HASH_CODE_FIELD_NAME,
JavaTypeName.INT);
JavaExpression condition =
new OperatorExpression.Binary (
JavaOperator.EQUALS_INT,
hashCodeField,
LiteralWrapper.make(new Integer(0)));
JavaStatement.Block thenBlock = new JavaStatement.Block();
IfThenElseStatement ifThen =
new IfThenElseStatement (condition, thenBlock);
hashCode.addStatement (ifThen);
// build up the hashcode value.
JavaExpression.LocalVariable result =
new LocalVariable ("result", JavaTypeName.INT);
LocalVariableDeclaration localVarDecl =
new LocalVariableDeclaration(result, LiteralWrapper.make(new Integer(17)));
thenBlock.addStatement(localVarDecl);
LiteralWrapper thirtySeven = LiteralWrapper.make (new Integer(37));
JavaExpression thirtySevenTimesResult =
new OperatorExpression.Binary(JavaOperator.MULTIPLY_INT, thirtySeven, result);
LiteralWrapper zero = LiteralWrapper.make (new Integer (0));
LiteralWrapper one = LiteralWrapper.make (new Integer (1));
// Start by including dc name in the hashcode.
// get objects hashcode
JavaExpression nameExpression =
new MethodInvocation.Instance (
new MethodInvocation.Instance(
null,
GET_DC_NAME_METHOD_NAME,
JavaTypeName.STRING,
MethodInvocation.InvocationType.VIRTUAL),
"hashCode",
JavaTypeName.INT,
MethodInvocation.InvocationType.VIRTUAL);
JavaExpression newResult =
new OperatorExpression.Binary (JavaOperator.ADD_INT, thirtySevenTimesResult, nameExpression);
JavaExpression assignResult = new Assignment (result, newResult);
thenBlock.addStatement(new ExpressionStatement (assignResult));
// Now get the contribution from each dc field.
for (FieldName fn : fieldNames) {
String javaFieldName = typeConstructorInfo.fieldJavaNames.get (fn);
JavaTypeName fieldType = fieldNameToType.get (fn);
JavaField field = new JavaField.Instance(null, javaFieldName, fieldType);
JavaExpression fieldExpression;
if (fieldType instanceof JavaTypeName.Primitive) {
if (fieldType instanceof JavaTypeName.Primitive.Boolean) {
fieldExpression =
new OperatorExpression.Ternary (field, zero, one);
} else if (fieldType instanceof JavaTypeName.Primitive.Byte ||
fieldType instanceof JavaTypeName.Primitive.Char ||
fieldType instanceof JavaTypeName.Primitive.Short) {
fieldExpression =
new CastExpression(JavaTypeName.INT, field);
}else if (fieldType instanceof JavaTypeName.Primitive.Double) {
// long f = Double.doubleToLongBits(f);
// result = (int) (f ^ (f >>> 32));
JavaExpression.LocalVariable f =
new LocalVariable ("f", JavaTypeName.LONG);
JavaExpression initializeF =
new MethodInvocation.Static (
JavaTypeName.DOUBLE_OBJECT,
"doubleToLongBits",
field,
JavaTypeName.DOUBLE,
JavaTypeName.LONG);
LocalVariableDeclaration fVarDecl =
new LocalVariableDeclaration(f, initializeF);
thenBlock.addStatement(fVarDecl);
fieldExpression =
new OperatorExpression.Binary (
JavaOperator.SHIFTR_UNSIGNED_LONG,
f,
LiteralWrapper.make(new Integer (32)));
fieldExpression =
new OperatorExpression.Binary (
JavaOperator.BITWISE_XOR_LONG,
f,
fieldExpression);
fieldExpression =
new CastExpression (JavaTypeName.INT, fieldExpression);
} else if (fieldType instanceof JavaTypeName.Primitive.Float) {
fieldExpression =
new MethodInvocation.Static (
JavaTypeName.FLOAT_OBJECT,
"floatToIntBits",
field,
JavaTypeName.FLOAT,
JavaTypeName.INT);
} else if (fieldType instanceof JavaTypeName.Primitive.Int) {
fieldExpression = field;
} else if (fieldType instanceof JavaTypeName.Primitive.Long) {
fieldExpression =
new OperatorExpression.Binary (
JavaOperator.SHIFTR_UNSIGNED_LONG,
field,
LiteralWrapper.make(new Integer (32)));
fieldExpression =
new OperatorExpression.Binary (
JavaOperator.BITWISE_XOR_LONG,
field,
fieldExpression);
fieldExpression =
new CastExpression (JavaTypeName.INT, fieldExpression);
} else {
fieldExpression =
new MethodInvocation.Instance (field, "hashCode", JavaTypeName.INT, MethodInvocation.InvocationType.VIRTUAL);
}
} else {
// get objects hashcode
fieldExpression =
new MethodInvocation.Instance (field, "hashCode", JavaTypeName.INT, MethodInvocation.InvocationType.VIRTUAL);
}
newResult =
new OperatorExpression.Binary (JavaOperator.ADD_INT, thirtySevenTimesResult, fieldExpression);
assignResult = new Assignment (result, newResult);
thenBlock.addStatement(new ExpressionStatement (assignResult));
}
// Assign the hash code value to the hashCode field.
JavaExpression assign = new JavaExpression.Assignment(hashCodeField, result);
thenBlock.addStatement(new ExpressionStatement (assign));
// Return the initialized hashCode field.
hashCode.addStatement (new ReturnStatement (hashCodeField));
return hashCode;
}
private final JavaMethod createMethod_equals (
JavaTypeName typeName,
Set<FieldName> fieldNames,
Map<FieldName, JavaTypeName> fieldNameToType) {
String argName = "object";
JavaMethod equals =
new JavaMethod(
Modifier.PUBLIC | Modifier.FINAL,
JavaTypeName.BOOLEAN,
argName,
JavaTypeName.OBJECT,
false,
"equals");
MethodVariable objectArg = new MethodVariable (argName);
JavaField thisField = new JavaField.This(typeName);
// if (object == this) {
// return true;
// }
JavaExpression condition =
new JavaExpression.OperatorExpression.Binary(
JavaOperator.EQUALS_OBJECT,
thisField,
objectArg);
JavaStatement.IfThenElseStatement ifThen =
new IfThenElseStatement(
condition,
new ReturnStatement(LiteralWrapper.TRUE));
equals.addStatement(ifThen);
// if (object == null) {
// return false;
// }
condition =
new JavaExpression.OperatorExpression.Binary(JavaOperator.EQUALS_OBJECT, objectArg, LiteralWrapper.NULL);
ifThen =
new IfThenElseStatement (condition, new ReturnStatement(LiteralWrapper.FALSE));
equals.addStatement(ifThen);
// if (!(object instanceOf ThisType)) {
// return false;
// }
condition =
new JavaExpression.InstanceOf(objectArg, typeName);
condition =
new OperatorExpression.Unary(JavaOperator.LOGICAL_NEGATE, condition);
ifThen =
new IfThenElseStatement (
condition,
new ReturnStatement(LiteralWrapper.FALSE));
equals.addStatement(ifThen);
// ThisType castObject = (ThisType)object;
LocalVariable localVar =
new LocalVariable ("cast" + argName, typeName);
JavaStatement localVarDecl =
new JavaStatement.LocalVariableDeclaration (
localVar,
new JavaExpression.CastExpression(typeName, objectArg));
equals.addStatement (localVarDecl);
// Check DC name
// if (!getDCName().equals(castObject.getDCName())) {
// return false;
// }
JavaExpression thisGetDCName =
new MethodInvocation.Instance(null, GET_DC_NAME_METHOD_NAME, JavaTypeName.STRING, MethodInvocation.InvocationType.VIRTUAL);
JavaExpression otherGetDCName =
new MethodInvocation.Instance(localVar, GET_DC_NAME_METHOD_NAME, JavaTypeName.STRING, MethodInvocation.InvocationType.VIRTUAL);
JavaExpression compareDCNames =
new MethodInvocation.Instance(thisGetDCName, "equals", otherGetDCName, JavaTypeName.OBJECT, JavaTypeName.BOOLEAN, MethodInvocation.InvocationType.VIRTUAL);
compareDCNames = new OperatorExpression.Unary(JavaOperator.LOGICAL_NEGATE, compareDCNames);
ifThen = new IfThenElseStatement(compareDCNames, new ReturnStatement(LiteralWrapper.FALSE));
equals.addStatement(ifThen);
if (fieldNames != null && fieldNames.size() > 0) {
// Now we need to compare equality for the various fields.
Iterator<FieldName> fields = fieldNames.iterator();
FieldName fn = fields.next ();
JavaTypeName fieldType = (JavaTypeName)fieldNameToType.get(fn);
JavaExpression compare = makeFieldComparison (fn, fieldType, localVar);
while (fields.hasNext()) {
fn = fields.next ();
fieldType = (JavaTypeName)fieldNameToType.get(fn);
JavaExpression nextCompare = makeFieldComparison (fn, fieldType, localVar);
compare =
new OperatorExpression.Binary (JavaOperator.CONDITIONAL_AND, compare, nextCompare);
}
equals.addStatement(new ReturnStatement(compare));
} else {
equals.addStatement(new ReturnStatement(LiteralWrapper.TRUE));
}
return equals;
}
private final JavaExpression makeFieldComparison (
FieldName fieldName,
JavaTypeName fieldType,
JavaExpression other) {
String javaFieldName = (String)typeConstructorInfo.fieldJavaNames.get(fieldName);
JavaExpression thisField = new JavaField.Instance (null, javaFieldName, fieldType);
JavaExpression otherField = new JavaField.Instance (other, javaFieldName, fieldType);
if (fieldType instanceof JavaTypeName.Primitive) {
// We need to use the correct version of the
// == operator.
// this.field == other.field
JavaOperator operator;
if (fieldType instanceof JavaTypeName.Primitive.Boolean) {
operator = JavaOperator.CONDITIONAL_AND;
} else if (fieldType instanceof JavaTypeName.Primitive.Byte) {
operator = JavaOperator.EQUALS_BYTE;
} else if (fieldType instanceof JavaTypeName.Primitive.Char) {
operator = JavaOperator.EQUALS_CHAR;
} else if (fieldType instanceof JavaTypeName.Primitive.Double) {
operator = JavaOperator.EQUALS_BYTE;
} else if (fieldType instanceof JavaTypeName.Primitive.Float) {
operator = JavaOperator.EQUALS_FLOAT;
} else if (fieldType instanceof JavaTypeName.Primitive.Int) {
operator = JavaOperator.EQUALS_INT;
} else if (fieldType instanceof JavaTypeName.Primitive.Long) {
operator = JavaOperator.EQUALS_LONG;
} else if (fieldType instanceof JavaTypeName.Primitive.Short) {
operator = JavaOperator.EQUALS_SHORT;
}else {
operator = JavaOperator.EQUALS_OBJECT;
}
JavaExpression compare =
new OperatorExpression.Binary (operator, thisField, otherField);
return compare;
} else {
// Invoke the equals method.
// (this.field == null) ? (other.field == null) | this.field.equals(other.field)
JavaExpression condition =
new OperatorExpression.Binary (
JavaOperator.EQUALS_OBJECT,
thisField,
LiteralWrapper.NULL);
JavaExpression then =
new OperatorExpression.Binary (
JavaOperator.EQUALS_OBJECT,
otherField,
LiteralWrapper.NULL);
JavaExpression elsePart =
new MethodInvocation.Instance (
thisField,
"equals",
otherField,
JavaTypeName.OBJECT,
JavaTypeName.BOOLEAN,
MethodInvocation.InvocationType.VIRTUAL);
JavaExpression ternary =
new OperatorExpression.Ternary (
condition,
then,
elsePart);
return ternary;
}
}
private final JavaMethod createMethod_toString (Set<FieldName> fieldNames, Map<FieldName, JavaTypeName> fieldTypes) {
JavaMethod toString =
new JavaMethod(
Modifier.PUBLIC | Modifier.FINAL,
JavaTypeName.STRING,
"toString");
JavaExpression getDCName =
new JavaExpression.MethodInvocation.Instance(
null,
GET_DC_NAME_METHOD_NAME,
JavaTypeName.STRING,
MethodInvocation.InvocationType.VIRTUAL);
JavaExpression message =
new JavaExpression.OperatorExpression.Binary(
JavaOperator.STRING_CONCATENATION,
getDCName,
LiteralWrapper.make("\n"));
for (FieldName fn : fieldNames) {
// There will only be one field type.
JavaTypeName fieldType = (JavaTypeName)fieldTypes.get(fn);
String javaFieldName = (String)typeConstructorInfo.fieldJavaNames.get(fn);
JavaExpression field =
new JavaField.Instance(null, javaFieldName, fieldType);
JavaExpression fieldString;
if (fieldType instanceof JavaTypeName.Primitive ||
fieldType.equals(JavaTypeName.STRING)) {
fieldString = field;
} else {
fieldString =
new MethodInvocation.Instance(field, "toString", JavaTypeName.STRING, MethodInvocation.InvocationType.VIRTUAL);
}
message =
new JavaExpression.OperatorExpression.Binary(
JavaOperator.STRING_CONCATENATION,
message,
LiteralWrapper.make(" " + fn.toString() + " = "));
message =
new JavaExpression.OperatorExpression.Binary(
JavaOperator.STRING_CONCATENATION,
message,
fieldString);
message =
new JavaExpression.OperatorExpression.Binary(
JavaOperator.STRING_CONCATENATION,
message,
LiteralWrapper.make("\n"));
}
toString.addStatement (new ReturnStatement (message));
return toString;
}
// /**
// * 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;
// }
}
/**
* Use this function to get the field types for a data constructor.
* @param dc
* @return an array of TypeExpr
*/
private final 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;
}
private final String createEnumFieldName(String dcName) {
//Substitute _ for #
//Substitute _ for .
//Substitute _ for $
StringBuffer sb = new StringBuffer();
char c = dcName.charAt(0);
//for the first character, don't worry about case or the underscore since the CAL language guarantees that functions start
//with a lower case letter.
switch (c) {
case '#':
case '$':
case '.':
sb.append('_');
break;
default:
if (Character.isUpperCase(c)) {
sb.append(c);
} else {
sb.append(Character.toUpperCase(c));
}
break;
}
for (int i = 1, n = dcName.length(); i < n; ++i) {
c = dcName.charAt(i);
switch (c) {
case '#':
case '$':
case '.':
case '_':
sb.append("__"); //escape special characters past the first with 2 underscores.
break;
default:
{
if (Character.isUpperCase(c)) {
sb.append('_');
sb.append(c);
} else {
sb.append(Character.toUpperCase(c));
}
break;
}
}
}
dcName = sb.toString();
if (isReservedWord(dcName)) {
//there are no reserved words that start with an underscore.
dcName = dcName + "_";
}
return dcName;
}
/**
* @param name a simple name
* @return whether the given name is a reserved word.
* ie. either a Java language keyword, or a name of a file which cannot be created in the Windows file system.
*/
private final boolean isReservedWord (String name) {
if (JavaReservedWords.javaLanguageKeywords.contains(name)) {
return true;
}
// Check for names that will conflict with reserved words in the Windows file system.
// In this case we're looking for COM1, COM2, ... or LPT1, LPT2, ...
// Also: CLOCK$, CON, AUX, PRN, NUL, CONFIG$
// Note: we want to look at these in a case insensitive fashion, since in the windows file
// system con and CON are the same.
name = name.toLowerCase();
// check the windows reserved words with all lower case.
if (windowsReservedWords.contains(name)) {
return true;
}
if (name.startsWith("com") || name.startsWith("lpt")) {
String rest = name.substring(3);
boolean isInt = true;
for (int i = 0; i < rest.length(); ++i) {
if (!Character.isDigit(rest.charAt(i))) {
isInt = false;
break;
}
}
if (isInt) {
return true;
}
}
return false;
}
/**
* Go from a TypeExpr to the corresponding JavaTypeName.
* @param typeExpr
* @param typeToClassMappings
* @param moduleToPackageMappings
* @param targetPackage
* @return a JavaTypeName
*/
private final JavaTypeName typeExprToTypeName (
TypeExpr typeExpr,
Map<QualifiedName, JavaTypeName> typeToClassMappings,
Map<ModuleName, String> moduleToPackageMappings,
String targetPackage) throws UnableToResolveForeignEntityException {
if (typeExpr != null) {
if (typeExpr instanceof RecordType) {
return JavaTypeName.make(java.util.List.class);
}
TypeConsApp typeConsApp = typeExpr.rootTypeConsApp();
if (typeConsApp != null) {
if (typeConsApp.isNonParametricType(CAL_Prelude.TypeConstructors.Boolean)) {
return JavaTypeName.BOOLEAN;
}
if(typeConsApp.getForeignTypeInfo() != null) {
ForeignTypeInfo fti = typeConsApp.getForeignTypeInfo();
return JavaTypeName.make (fti.getForeignType());
}
TypeConstructor typeConstructor = typeConsApp.getRoot();
JavaTypeName typeName = (JavaTypeName)typeToClassMappings.get(typeConstructor.getName());
if (typeName != null) {
return typeName;
}
String unqualifiedTypeConstructorName = typeConstructor.getName().getUnqualifiedName();
if (unqualifiedTypeConstructorName.equals("Function")) {
return CAL_VALUE;
}
// We are going to build up a JavaTypeName based on our naming convention for generated
// I/O classes.
// Check to see if the CAL module is mapped to a Java package.
String referencedPackage = moduleToPackageMappings.get(typeConstructor.getName().getModuleName());
if (referencedPackage == null) {
// If not use the target package.
referencedPackage = targetPackage;
}
if (referencedPackage.endsWith(".")) {
referencedPackage = referencedPackage.substring(0, referencedPackage.lastIndexOf('.'));
}
if (referencedPackage.lastIndexOf('.') > 0) {
referencedPackage = referencedPackage.substring(0, referencedPackage.lastIndexOf('.')+1);
} else {
referencedPackage = referencedPackage + ".";
}
// Create a Java class name.
JavaTypeName typeConstructorClassName =
JavaTypeName.make(referencedPackage + typeConstructor.getName().getModuleName().getLastComponent() + "." + getClassName(typeConstructor), false);
return typeConstructorClassName;
}
}
return JavaTypeName.OBJECT;
}
private final class CAL_Module_ImportGenerator<T> extends SourceModelCopier <T> {
private final SourceModel.ModuleDefn moduleDefn;
private final Map<String, Name.DataCons> shortNameToFullNameDataCons = new LinkedHashMap<String, Name.DataCons>();
private final Map<String, Name.Function> shortNameToFullNameFunction = new LinkedHashMap<String, Name.Function>();
private final Map<String, Name.TypeClass> shortNameToFullNameTypeClass = new LinkedHashMap<String, Name.TypeClass>();
private final Map<String, Name.TypeCons> shortNameToFullNameTypeCons = new LinkedHashMap<String, Name.TypeCons>();
private final Set<ModuleName> mustImportModules;
/**
*
* @param moduleDefn
* @param mustImportModules - Set of ModuleName
*/
CAL_Module_ImportGenerator (ModuleDefn moduleDefn, Set<ModuleName> mustImportModules) {
this.moduleDefn = moduleDefn;
this.mustImportModules = mustImportModules;
}
/**
* @param cons the source model element to be traversed
* @param arg unused argument
* @return null
*/
public Name.DataCons visit_Name_DataCons(
Name.DataCons cons, T arg) {
if (cons.isQualified()) {
if (!cons.getModuleName().toString().equals(moduleDefn.getModuleName().toString())) {
Name.DataCons existingMapping = (Name.DataCons)shortNameToFullNameDataCons.get(cons.getUnqualifiedName());
if (existingMapping != null) {
// Mapping exists for short name. Is it for this data cons.
if (existingMapping.getModuleName().toString().equals(cons.getModuleName().toString())) {
// Mapping is valid, return unqualified name.
return Name.DataCons.makeUnqualified(cons.getUnqualifiedName());
}
} else {
// Mapping doesn't exist. Create one.
shortNameToFullNameDataCons.put(cons.getUnqualifiedName(), cons);
return Name.DataCons.makeUnqualified(cons.getUnqualifiedName());
}
} else {
return Name.DataCons.makeUnqualified(cons.getUnqualifiedName());
}
}
return super.visit_Name_DataCons(cons, arg);
}
/**
* @param function the source model element to be traversed
* @param arg unused argument
* @return null
*/
public Name.Function visit_Name_Function(
Name.Function function, T arg) {
if (function.isQualified()) {
if (!function.getModuleName().toString().equals(moduleDefn.getModuleName().toString())) {
Name.Function existingMapping = (Name.Function)shortNameToFullNameFunction.get(function.getUnqualifiedName());
if (existingMapping != null) {
// Mapping exists for short name. Is it for this data cons.
if (existingMapping.getModuleName().toString().equals(function.getModuleName().toString())) {
// Mapping is valid, return unqualified name.
return Name.Function.makeUnqualified(function.getUnqualifiedName());
}
} else {
// Mapping doesn't exist. Create one.
shortNameToFullNameFunction.put(function.getUnqualifiedName(), function);
return Name.Function.makeUnqualified(function.getUnqualifiedName());
}
} else {
return Name.Function.makeUnqualified(function.getUnqualifiedName());
}
}
return super.visit_Name_Function(function, arg);
}
/**
* @param typeClass the source model element to be traversed
* @param arg unused argument
* @return null
*/
public Name.TypeClass visit_Name_TypeClass(
Name.TypeClass typeClass, T arg) {
if (typeClass.isQualified()) {
if (!typeClass.getModuleName().toString().equals(moduleDefn.getModuleName().toString())) {
Name.TypeClass existingMapping = (Name.TypeClass)shortNameToFullNameTypeClass.get(typeClass.getUnqualifiedName());
if (existingMapping != null) {
// Mapping exists for short name. Is it for this data cons.
if (existingMapping.getModuleName().toString().equals(typeClass.getModuleName().toString())) {
// Mapping is valid, return unqualified name.
return Name.TypeClass.makeUnqualified(typeClass.getUnqualifiedName());
}
} else {
// Mapping doesn't exist. Create one.
shortNameToFullNameTypeClass.put(typeClass.getUnqualifiedName(), typeClass);
return Name.TypeClass.makeUnqualified(typeClass.getUnqualifiedName());
}
} else {
return Name.TypeClass.makeUnqualified(typeClass.getUnqualifiedName());
}
}
return super.visit_Name_TypeClass(typeClass, arg);
}
/**
* @param cons the source model element to be traversed
* @param arg unused argument
* @return null
*/
public Name.TypeCons visit_Name_TypeCons(
Name.TypeCons cons, T arg) {
if (cons.isQualified()) {
if (!cons.getModuleName().toString().equals(moduleDefn.getModuleName().toString())) {
Name.TypeCons existingMapping = (Name.TypeCons)shortNameToFullNameTypeCons.get(cons.getUnqualifiedName());
if (existingMapping != null) {
// Mapping exists for short name. Is it for this data cons.
if (existingMapping.getModuleName().toString().equals(cons.getModuleName().toString())) {
// Mapping is valid, return unqualified name.
return Name.TypeCons.makeUnqualified(cons.getUnqualifiedName());
}
} else {
// Mapping doesn't exist. Create one.
shortNameToFullNameTypeCons.put(cons.getUnqualifiedName(), cons);
return Name.TypeCons.makeUnqualified(cons.getUnqualifiedName());
}
} else {
return Name.TypeCons.makeUnqualified(cons.getUnqualifiedName());
}
}
return super.visit_Name_TypeCons(cons, arg);
}
/**
* @param defn the source model element to be copied
* @param arg unused argument
* @return a deep copy of the source model element
*/
public ModuleDefn visit_ModuleDefn(
ModuleDefn defn, T arg) {
CALDoc.Comment.Module newCALDocComment = null;
if (defn.getCALDocComment() != null) {
newCALDocComment = (CALDoc.Comment.Module)defn.getCALDocComment().accept(this, arg);
}
final int nFriendModules = defn.getNFriendModules();
Friend[] newFriendModules = new Friend[nFriendModules];
for (int i = 0; i < nFriendModules; i++) {
newFriendModules[i] = (Friend)defn.getNthFriendModule(i).accept(this, arg);
}
final int nTopLevelDefns = defn.getNTopLevelDefns();
TopLevelSourceElement[] newTopLevelDefns = new TopLevelSourceElement[nTopLevelDefns];
for (int i = 0; i < nTopLevelDefns; i++) {
newTopLevelDefns[i] = (TopLevelSourceElement)defn.getNthTopLevelDefn(i).accept(this, arg);
}
SourceModel.Import newImportedModules[] = buildImports();
return ModuleDefn.make(
newCALDocComment,
(Name.Module)defn.getModuleName().accept(this, arg),
newImportedModules,
newFriendModules,
newTopLevelDefns);
}
private SourceModel.Import[] buildImports () {
Set<String> moduleNames = new LinkedHashSet<String>();
// Note: SourceModel.Name, and derived classes, do not implement 'equals' or 'hashCode'
// so we can't use them in hash sets, hash maps, etc.
// Build up the set of modules to import.
for (Name.Qualifiable name : shortNameToFullNameDataCons.values()) {
String moduleName = name.getModuleName().toString();
moduleNames.add(moduleName);
}
for (Name.Qualifiable name : shortNameToFullNameFunction.values()) {
String moduleName = name.getModuleName().toString();
moduleNames.add(moduleName);
}
for (Name.Qualifiable name : shortNameToFullNameTypeClass.values()) {
String moduleName = name.getModuleName().toString();
moduleNames.add(moduleName);
}
for (Name.Qualifiable name : shortNameToFullNameTypeCons.values()) {
String moduleName = name.getModuleName().toString();
moduleNames.add(moduleName);
}
List<SourceModel.Import> allImports = new ArrayList<SourceModel.Import>();
SourceModel.Import preludeImport = null;
String preludeModuleName = CAL_Prelude.MODULE_NAME.toString();
// Now build up imports for each module.
for (String moduleNameString : moduleNames) {
List<String> dataConsNameStringList = new ArrayList<String>();
List<String> functionNameStringList = new ArrayList<String>();
List<String> typeConsNameStringList = new ArrayList<String>();
List<String> typeClassNameStringList = new ArrayList<String>();
for (String shortName :shortNameToFullNameDataCons.keySet()) {
Name.Qualifiable qn = shortNameToFullNameDataCons.get(shortName);
if (qn.getModuleName().toString().equals(moduleNameString)) {
dataConsNameStringList.add(shortName);
}
}
for (String shortName : shortNameToFullNameFunction.keySet()) {
Name.Qualifiable qn = shortNameToFullNameFunction.get(shortName);
if (qn.getModuleName().toString().equals(moduleNameString)) {
functionNameStringList.add(shortName);
}
}
for (String shortName : shortNameToFullNameTypeClass.keySet()) {
Name.Qualifiable qn = shortNameToFullNameTypeClass.get(shortName);
if (qn.getModuleName().toString().equals(moduleNameString)) {
typeClassNameStringList.add(shortName);
}
}
for (String shortName : shortNameToFullNameTypeCons.keySet()) {
Name.Qualifiable qn = shortNameToFullNameTypeCons.get(shortName);
if (qn.getModuleName().toString().equals(moduleNameString)) {
typeConsNameStringList.add(shortName);
}
}
SourceModel.Import importUsing = Import.make (
ModuleName.make(moduleNameString),
(String[])functionNameStringList.toArray(new String[]{}),
(String[])dataConsNameStringList.toArray(new String[]{}),
(String[])typeConsNameStringList.toArray(new String[]{}),
(String[])typeClassNameStringList.toArray(new String[]{})
);
if (moduleNameString.equals(preludeModuleName)) {
preludeImport = importUsing;
} else {
allImports.add(importUsing);
}
}
if (preludeImport == null) {
preludeImport = SourceModel.Import.make(CAL_Prelude.MODULE_NAME);
}
allImports.add(0, preludeImport);
for (ModuleName moduleName : mustImportModules) {
String moduleNameString = moduleName.toString();
boolean found = false;
for (Iterator<String> moduleNamesIterator = moduleNames.iterator(); moduleNamesIterator.hasNext(); ) {
if (moduleNameString.equals((String)moduleNamesIterator.next())) {
found = true;
}
}
if (!found) {
Import imp = Import.make(moduleName);
allImports.add(imp);
}
}
return (Import[])allImports.toArray(new Import[]{});
}
ModuleDefn generateImports (SourceModel.ModuleDefn moduleDefn) {
return (ModuleDefn)moduleDefn.accept(this, null);
}
}
private static final class DataConstructorInfo {
/** DataConstructor associated with this information. */
final DataConstructor dataConstructor;
/** All the FieldName for this data constructor. */
final Set<FieldName> allFieldNames;
/**
* FieldName -> String
* Map FieldName to the name of the Java accessor method for
* that field in the generated Java class.
*/
final Map<FieldName, String> fieldJavaAccessorMethodNames;
/**
* FieldName -> JavaTypeName
* Map FieldName to the corresponding Java type.
*/
final Map<FieldName, JavaTypeName> fieldTypeNames;
/**
* Name of the inner class for the data constructor.
*/
final String innerClassName;
/** Foreign type name for the inner class. */
final String calForeignTypeName;
/**
* True if the data constructor has fields which are not
* common with the other data constructors for the type.
*/
final boolean containsFields;
DataConstructorInfo (
DataConstructor dataConstructor,
Map<FieldName, String> fieldAccessorMethodNames,
Map<FieldName, JavaTypeName> fieldTypeNames,
boolean containsFields) {
this.dataConstructor = dataConstructor;
this.fieldJavaAccessorMethodNames = fieldAccessorMethodNames;
this.fieldTypeNames = fieldTypeNames;
this.innerClassName = fixupClassName(dataConstructor.getName().getUnqualifiedName());
this.calForeignTypeName = "J" + innerClassName;
this.containsFields = containsFields;
allFieldNames = new LinkedHashSet<FieldName>();
for (int i = 0, n = dataConstructor.getNArgumentNames(); i < n; ++i) {
allFieldNames.add (dataConstructor.getNthFieldName (i));
}
}
}
/**
* Class used to hold information about a TypeConstructor.
* This information is used in generating the Java and CAL code
* necessary to use input/output to move values between Java and
* CAL.
* @author rcypher
*
*/
private static final class TypeConstructorInfo {
/** The TypeConstructor this information applies to. */
final TypeConstructor typeConstructor;
/** The name of the generated Java class corresponding to the type constructor. */
final String javaClassName;
/** The CAL name used for the foreign type declartion of the generated Java class. */
String calForeignTypeName;
/** Is the type constructor an enumeration. (i.e. all data constructors have zero fields) */
final boolean isEnumerationType;
/** Set of FieldName. All field names from all data constructors in the type. */
final Set<FieldName> allFieldNames;
/**
* FieldName -> (Set of JavaTypeName).
* Maps a FieldName to the corresponding Java types.
* Fields with the same names in different data constructors
* can have different types.
*/
final Map<FieldName, Set<JavaTypeName>> allFieldTypeNames;
/**
* Set of FieldName.
* Fields which appear with same name/type in all data constructors.
*/
final Set<FieldName> commonFieldNames;
/** FieldName -> JavaTypeName */
final Map<FieldName, JavaTypeName> commonFieldTypes;
/**
* FieldName -> String
* The names of the Java fields corresponding to the CAL fields.
*/
final Map<FieldName, String> fieldJavaNames;
/**
* FieldName -> String
* Map FieldName to the name of the Java accessor method
* in the generated Java class.
*/
final Map<FieldName, String> fieldJavaAccessorMethodNames;
/**
* FieldName -> (Map DataConstructor -> String).
* The Java field accessor methods are declared as
* foreign functions in the generated CAL.
* From a FieldName you can access a Map of the
* data constructors containing the field to the name of
* the foreign function in the generated CAL.
*/
final Map<FieldName, Map<DataConstructor, String>> calFieldAccessorFunctionNames;
/**
* FieldName -> (Map JavaTypeName -> String)
* For a FieldName a Map of the corresponding Java types
* to the CAL foreign name for that type.
*/
final Map<FieldName, Map<JavaTypeName, String>> calFieldForeignTypes;
/**
* DataConstructor -> DataConstructorInfo
* Information for each data constructor.
*/
final Map<DataConstructor, DataConstructorInfo> dataConstructorInfo = new LinkedHashMap<DataConstructor, DataConstructorInfo>();
TypeConstructorInfo (
TypeConstructor typeConstructor,
String javaClassName,
boolean isEnumerationType,
Set<FieldName> allFieldNames,
Map<FieldName, Set<JavaTypeName>> allFieldTypeNames,
Set<FieldName> commonFieldNames,
Map<FieldName, String> fieldJavaNames,
Map<FieldName, String> fieldJavaAccessorMethodNames,
Map<FieldName, Map<JavaTypeName, String>> calFieldForeignTypes,
Map<QualifiedName, JavaTypeName> typeToClassMappings
) {
this.typeConstructor = typeConstructor;
this.javaClassName = javaClassName;
this.isEnumerationType = isEnumerationType;
this.allFieldNames = allFieldNames;
this.allFieldTypeNames = allFieldTypeNames;
this.commonFieldNames = commonFieldNames;
this.fieldJavaNames = fieldJavaNames;
this.fieldJavaAccessorMethodNames = fieldJavaAccessorMethodNames;
this.calFieldForeignTypes = calFieldForeignTypes;
this.calForeignTypeName = "J" + javaClassName;
this.calFieldAccessorFunctionNames = new LinkedHashMap<FieldName, Map<DataConstructor, String>>();
this.commonFieldTypes = new LinkedHashMap<FieldName, JavaTypeName>();
for (FieldName fn : commonFieldNames) {
Set<JavaTypeName> fieldTypes = this.allFieldTypeNames.get(fn);
assert (fieldTypes.size() == 1);
commonFieldTypes.put(fn, fieldTypes.iterator().next());
}
for (int i = 0, n = typeConstructor.getNDataConstructors(); i < n; ++i) {
DataConstructor dc = typeConstructor.getNthDataConstructor(i);
for (int j = 0, k = dc.getArity(); j < k; ++j) {
FieldName fn = dc.getNthFieldName(j);
Map<DataConstructor, String> calAccessorNames = calFieldAccessorFunctionNames.get(fn);
if (calAccessorNames == null) {
calAccessorNames = new LinkedHashMap<DataConstructor, String>();
calFieldAccessorFunctionNames.put(fn, calAccessorNames);
}
String javaAccessorName = (String)fieldJavaAccessorMethodNames.get(fn);
String className;
if (commonFieldNames.contains(fn)) {
className = javaClassName;
} else {
className = fixupClassName(dc.getName().getUnqualifiedName());
}
String calAccessorName = "j" + className + "_" + javaAccessorName;
calAccessorNames.put(dc, calAccessorName);
}
}
}
}
/** List of LogRecord */
private final List<LogRecord> ioLogMessages = new ArrayList<LogRecord>();
IO_Source_Generator () {
}
private void logMessage (Level level, String message) {
ioLogMessages.add(new LogRecord(level, message));
}
private final String getClassName (TypeConstructor typeConstructor) {
String unqualifiedName = typeConstructor.getName().getUnqualifiedName() + "_";
return fixupClassName(unqualifiedName);
}
/**
* 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
*/
private static final 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;
}
private final Set<FieldName> getCommonFieldNames (TypeConstructor typeConstructor) {
Set<FieldName> commonFieldNames = new LinkedHashSet<FieldName>();
int nDCs = typeConstructor.getNDataConstructors();
DataConstructor firstDC = typeConstructor.getNthDataConstructor(0);
for (int i = 0; i < firstDC.getArity(); ++i) {
FieldName fn = firstDC.getNthFieldName(i);
commonFieldNames.add(fn);
}
for (int i = 1; i < nDCs; ++i) {
DataConstructor dc = typeConstructor.getNthDataConstructor(i);
Set<FieldName> commonFieldNamesClone = new LinkedHashSet<FieldName>(commonFieldNames);
for (int j = 0, dcArity = dc.getArity(); j < dcArity; ++j) {
commonFieldNamesClone.remove(dc.getNthFieldName(j));
}
commonFieldNames.removeAll(commonFieldNamesClone);
}
return commonFieldNames;
}
private final String getJavaFieldNameFromFieldName (FieldName fn) {
String fieldName = fixupFieldName(fn.toString());
if (!fieldName.startsWith("_")) {
fieldName = "_" + fieldName;
}
return fieldName;
}
/**
* 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 final String fixupFieldName(String varName) {
if (JavaReservedWords.javaLanguageKeywords.contains(varName)) {
return varName + "_";
}
// The optimizer will generate symbols sometimes where the first character
// of the name between the last two '$' is a digit
if (Character.isDigit(varName.charAt(0))){
return "_" + varName;
}
if (varName.equals("QualifiedName")) {
varName = varName + "_";
}
varName = varName.replace ('.', '_');
varName = varName.replace ('#', '_');
return varName;
}
}