/* * 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. */ /* * CALToJavaNames.java * Created: Oct 28, 2003 2:52:51 PM * By: RCypher */ package org.openquark.cal.internal.machine.lecc; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import org.openquark.cal.compiler.DataConstructor; import org.openquark.cal.compiler.ModuleName; import org.openquark.cal.compiler.QualifiedName; import org.openquark.cal.compiler.TypeConstructor; import org.openquark.cal.internal.javamodel.JavaReservedWords; import org.openquark.cal.internal.javamodel.JavaTypeName; import org.openquark.cal.internal.machine.lecc.LECCModule.FunctionGroupInfo; import org.openquark.cal.internal.module.Cal.Core.CAL_Debug_internal; import org.openquark.cal.internal.module.Cal.Core.CAL_Dynamic_internal; import org.openquark.cal.internal.module.Cal.Core.CAL_Exception_internal; import org.openquark.cal.internal.module.Cal.Core.CAL_Prelude_internal; import org.openquark.cal.internal.module.Cal.Core.CAL_Record_internal; import org.openquark.cal.internal.module.Cal.Utilities.CAL_QuickCheck_internal; import org.openquark.cal.internal.runtime.lecc.LECCMachineConfiguration; import org.openquark.cal.machine.Module; import org.openquark.cal.machine.ProgramResourceLocator; import org.openquark.cal.module.Cal.Core.CAL_Prelude; /** * This class is used to manage the conversion of CAL names * to java names. * All conversion should be done through the members of this class. * Creation: Oct 28, 2003 * @author RCypher */ final class CALToJavaNames { private static final String TYPE_PREFIX = "TYPE_"; private static final String DATACONSTRUCTOR_PREFIX = "CAL_"; private static final boolean TRUNCATE_LONG_CLASS_NAMES = LECCMachineConfiguration.isLeccRuntimeStatic(); private static final int OUTER_CLASS_NAME_LENGTH_LIMIT = 80; private static final int INNER_CLASS_NAME_LENGTH_LIMIT = OUTER_CLASS_NAME_LENGTH_LIMIT + 15; /** Track number of supercombinator names that have been truncated because they are too long. */ private static final AtomicInteger nTruncatedNames = new AtomicInteger(0); /** * (String -> JavaTypeName) * Map of names of primitive functions that are implemented in lecc in the lecc.functions package * to their defining JavaTypeName constant. * Basically these are the primitive functions that are not simply equivalent to Java primitives * (as are things like Prelude.addInt). * If this map is modified by code outside of the static initialization block it will need * to be synchronized. */ private static final Map<QualifiedName, JavaTypeName> primitiveFunctionsMap = new HashMap<QualifiedName, JavaTypeName>(); static { primitiveFunctionsMap.put(CAL_Prelude_internal.Functions.equalsRecord, JavaTypeNames.RTEQUALS_RECORD); primitiveFunctionsMap.put(CAL_Prelude_internal.Functions.notEqualsRecord, JavaTypeNames.RTNOT_EQUALS_RECORD); primitiveFunctionsMap.put(CAL_Prelude_internal.Functions.compareRecord, JavaTypeNames.RTCOMPARE_RECORD); primitiveFunctionsMap.put(CAL_Prelude_internal.Functions.recordToJListPrimitive, JavaTypeNames.RTRECORD_TO_JLIST_PRIMITIVE); primitiveFunctionsMap.put(CAL_Record_internal.Functions.recordToJRecordValuePrimitive, JavaTypeNames.RTRECORD_TO_JRECORDVALUE_PRIMITIVE); primitiveFunctionsMap.put(CAL_Record_internal.Functions.strictRecordPrimitive, JavaTypeNames.RTSTRICT_RECORD_PRIMITIVE); primitiveFunctionsMap.put(CAL_Prelude_internal.Functions.recordFromJMapPrimitive, JavaTypeNames.RTRECORD_FROM_JMAP_PRIMITIVE); primitiveFunctionsMap.put(CAL_Prelude_internal.Functions.recordFromJListPrimitive, JavaTypeNames.RTRECORD_FROM_JLIST_PRIMITIVE); primitiveFunctionsMap.put(CAL_Dynamic_internal.Functions.recordFieldTypePrimitive, JavaTypeNames.RTRECORD_FIELD_TYPE_PRIMITIVE); primitiveFunctionsMap.put(CAL_Dynamic_internal.Functions.recordFieldValuePrimitive, JavaTypeNames.RTRECORD_FIELD_VALUE_PRIMITIVE); primitiveFunctionsMap.put(CAL_Dynamic_internal.Functions.insertOrdinalRecordFieldPrimitive, JavaTypeNames.RTINSERT_ORDINAL_RECORD_FIELD_PRIMITIVE); primitiveFunctionsMap.put(CAL_Dynamic_internal.Functions.insertTextualRecordFieldPrimitive, JavaTypeNames.RTINSERT_TEXTUAL_RECORD_FIELD_PRIMITIVE); primitiveFunctionsMap.put(CAL_Dynamic_internal.Functions.appendRecordPrimitive, JavaTypeNames.RTAPPEND_RECORD_PRIMITIVE); primitiveFunctionsMap.put(CAL_Prelude_internal.Functions.recordTypeDictionary, JavaTypeNames.RTRECORD_TYPE_DICTIONARY); primitiveFunctionsMap.put(CAL_Prelude.Functions.seq, JavaTypeNames.RTSEQ); primitiveFunctionsMap.put(CAL_Prelude.Functions.deepSeq, JavaTypeNames.RTDEEP_SEQ); primitiveFunctionsMap.put(CAL_Prelude_internal.Functions.ordinalValue, JavaTypeNames.RTORDINAL_VALUE); primitiveFunctionsMap.put(CAL_Prelude.Functions.error, JavaTypeNames.RTERROR); primitiveFunctionsMap.put(CAL_Exception_internal.Functions.primThrow, JavaTypeNames.RTTHROW); primitiveFunctionsMap.put(CAL_Exception_internal.Functions.primCatch, JavaTypeNames.RTCATCH); primitiveFunctionsMap.put(CAL_QuickCheck_internal.Functions.arbitraryRecordPrimitive, JavaTypeNames.RTARBITRARY_RECORD_PRIMITIVE); primitiveFunctionsMap.put(CAL_QuickCheck_internal.Functions.coarbitraryRecordPrimitive, JavaTypeNames.RTCOARBITRARY_RECORD_PRIMITIVE); primitiveFunctionsMap.put(CAL_Debug_internal.Functions.showRecord, JavaTypeNames.RTSHOW_RECORD); primitiveFunctionsMap.put(CAL_Record_internal.Functions.buildListPrimitive, JavaTypeNames.RTRECORD_TO_LIST_PRIMITIVE); primitiveFunctionsMap.put(CAL_Record_internal.Functions.buildRecordPrimitive, JavaTypeNames.RTLIST_TO_RECORD_PRIMITIVE); } /** * 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 HashSet<String>(); static { windowsReservedWords.add ("clock$"); windowsReservedWords.add ("con"); windowsReservedWords.add ("prn"); windowsReservedWords.add ("nul"); windowsReservedWords.add ("config$"); windowsReservedWords.add ("aux"); } /** * Take a supercombinator name and do the appropriate * transformations to create a valid java class name. * Note: this is an unqualified class name. * @param scModule The module that the symbol is being defined in. * @param scName * @param module the LECCModule instance corresponding to either the module defining the entity, or one of its dependent modules. * This is used for obtaining the appropriate {@link LECCModule.ClassNameMapper} for use in mapping names. * @return String */ private static String createClassNameFromSC (ModuleName scModule, String scName, LECCModule module) { scName = module.getFunctionGroupInfo(QualifiedName.make(scModule, scName)).getFunctionGroupName(); scName = fixupUnqualifiedName(scModule, scName, module); char[] ln = scName.toCharArray(); ln[0] = Character.toUpperCase(ln[0]); return new String(ln); } /** * @param moduleName the name of the module in which the cal entity exists. * @param unqualifiedClassName the name previously returned by a call to createClassNameFromSC(). * @param module the LECCModule instance corresponding to either the module defining the entity, or one of its dependent modules. * This is used for obtaining the appropriate {@link LECCModule.ClassNameMapper} for use in mapping names. * @return the unqualified name of the function, or null if the class name does not represent a function * whose name was generated by this class. */ static String getUnqualifiedFunctionNameFromClassName(ModuleName moduleName, String unqualifiedClassName, LECCModule module) { if (moduleName.equals(CAL_Prelude.MODULE_NAME) && unqualifiedClassName.equals("If")) { return "if"; } char[] ln = unqualifiedClassName.toCharArray(); ln[0] = Character.toLowerCase(ln[0]); String lowerCasedFixedUpName = new String(ln); return module.getClassNameMapper().getOriginalName(lowerCasedFixedUpName); } /** * @param scName * @param module * @return The name of the Java field used for an instance of the named supercombinator. */ static String getInstanceFieldName (QualifiedName scName, LECCModule module) { // Check to see if this is the only sc in the group. LECCModule.FunctionGroupInfo fgi = module.getFunctionGroupInfo(scName); if (fgi.getNFunctions() <= 1) { return "$instance"; } return "$instance_" + cleanSCName(scName.getUnqualifiedName()); } /** * Take a qualified supercombinator name and do the appropriate * transformations to create a valid java class name. * Note: this is an unqualified class name. * @param scName * @param module the LECCModule instance corresponding to either the module defining the entity, or one of its dependent modules. * This is used for obtaining the appropriate {@link LECCModule.ClassNameMapper} for use in mapping names. * @return String */ static String createClassNameFromSC (QualifiedName scName, LECCModule module) { JavaTypeName primitiveFunctionType = primitiveFunctionsMap.get(scName); if (primitiveFunctionType != null) { return primitiveFunctionType.getUnqualifiedJavaSourceName(); } return createClassNameFromSC(scName.getModuleName(), scName.getUnqualifiedName(), module); } /** * Take a CAL module name and do any necessary transformations * to create a valid java package name. * @param moduleName * @return String */ static String createPackageNameFromModule (ModuleName moduleName) { return ProgramResourceLocator.MODULE_PREFIX + moduleName.toSourceText().replaceAll("_", "__").replace('.', '_'); } /** * Take a CAL qualified name and do any necessary transformations * to create a valid java package name. * @param qn * @return String */ private static String createPackageNameFromModule (QualifiedName qn) { return createPackageNameFromModule(qn.getModuleName()); } /** * Take a CAL qualified name and do any necessary transformations * to create a valid java package name. * @param module * @return String */ static String createPackageNameFromModule (Module module) { return createPackageNameFromModule(module.getName()); } /** * Reverse the transformation applied by createPackageNameFromModule(). * @param packageName a valid java package name, created from createPackageNameFromModule(). * @return the name of the module for that package. */ static ModuleName createModuleNameFromPackageName (String packageName) { return ProgramResourceLocator.createModuleNameFromPackageNameSegment(packageName); } /** * Create a fully qualified package name from a module name. * @param moduleName * @return String */ private static String createFullPackageNameFromModule (ModuleName moduleName) { return createFullPackageName (createPackageNameFromModule(moduleName)); } /** * Return the fully qualified package name corresponding to a type. * @param typeCons * @return String */ private static String createFullPackageName (TypeConstructor typeCons) { return createFullPackageNameFromModule(typeCons.getName().getModuleName()); } /** * Create a fully qualified package name from a qualified name. * @param qn * @return String */ protected static String createFullPackageNameFromModule (QualifiedName qn) { return createFullPackageName (createPackageNameFromModule(qn)); } /** * Create a fully qualified package name from the given package name. * @param packageName * @return String */ static String createFullPackageName (String packageName) { return LECCMachineConfiguration.ROOT_PACKAGE + "." + packageName; } /** * Create a fully qualified class name from a module and supercombinator * name. * @param moduleName * @param scName * @param module the LECCModule instance corresponding to either the module defining the entity, or one of its dependent modules. * This is used for obtaining the appropriate {@link LECCModule.ClassNameMapper} for use in mapping names. * @return String; */ private static String createFullClassNameFromSC(ModuleName moduleName, String scName, LECCModule module) { String className = createClassNameFromSC(moduleName, scName, module); String packageName = createPackageNameFromModule(moduleName); return createFullClassName (packageName, className); } /** * Create a fully qualified class name from the qualified name of a supercombinator. * @param scName * @param module the LECCModule instance corresponding to either the module defining the entity, or one of its dependent modules. * This is used for obtaining the appropriate {@link LECCModule.ClassNameMapper} for use in mapping names. * @return String; */ static String createFullClassNameFromSC (QualifiedName scName, LECCModule module) { JavaTypeName primitiveFunctionType = primitiveFunctionsMap.get(scName); if (primitiveFunctionType != null) { return primitiveFunctionType.getFullJavaSourceName(); } return createFullClassNameFromSC(scName.getModuleName(), scName.getUnqualifiedName(), module); } /** * @param moduleName the name of the module in which the cal entity exists. * @param classNameForType the name previously returned by a call to createClassNameFromType(). * @param module the LECCModule instance corresponding to either the module defining the entity, or one of its dependent modules. * This is used for obtaining the appropriate {@link LECCModule.ClassNameMapper} for use in mapping names. * @return the original name before it was fixed up, or null if the class name does not represent a type * whose name was generated by this class. */ static String getUnqualifiedTypeNameFromClassName(ModuleName moduleName, String classNameForType, LECCModule module) { if (!classNameForType.startsWith(TYPE_PREFIX)) { return null; } String noPrefixTypeName = classNameForType.substring(TYPE_PREFIX.length()); module = (LECCModule)module.findModule(moduleName); return module.getClassNameMapper().getOriginalName(noPrefixTypeName); } /** * Given a TypeConstructor generate a corresponding unqualified class name. * @param typeCons * @param module the LECCModule instance corresponding to either the module defining the entity, or one of its dependent modules. * This is used for obtaining the appropriate {@link LECCModule.ClassNameMapper} for use in mapping names. * @return String */ static String createClassNameFromType (TypeConstructor typeCons, LECCModule module) { String typeName = typeCons.getName().getUnqualifiedName(); return TYPE_PREFIX + fixupUnqualifiedName(typeCons.getName().getModuleName(), typeName, module); } /** * Takes a data constructor and returns the name of the corresponding class. * i.e. TypeClass$DataConstructorClass * @param dc * @param module the LECCModule instance corresponding to either the module defining the entity, or one of its dependent modules. * This is used for obtaining the appropriate {@link LECCModule.ClassNameMapper} for use in mapping names. * @return String */ static String createClassName (DataConstructor dc, LECCModule module) { String typeClassName = createClassNameFromType (dc.getTypeConstructor(), module); String consClassName = createInnerClassNameFromDC_internal(dc, module); //inner classes must use a $ as a separator. String innerClassName = typeClassName + "$" + consClassName; return doClassNameTruncation(innerClassName, module.getName(), module, true); } /** * Internal helper for creating the name of the corresponding inner class from a data constructor. * @param dc * @param module the LECCModule instance corresponding to either the module defining the entity, or one of its dependent modules. * This is used for obtaining the appropriate {@link LECCModule.ClassNameMapper} for use in mapping names. * @return String */ private static String createInnerClassNameFromDC_internal (DataConstructor dc, LECCModule module) { return DATACONSTRUCTOR_PREFIX + createClassNameFromSC (dc.getName().getModuleName(), dc.getName().getUnqualifiedName(), module); } /** * Given a DataConstructor creates the name of the corresponding inner class. * @param dc * @param module the LECCModule instance corresponding to either the module defining the entity, or one of its dependent modules. * This is used for obtaining the appropriate {@link LECCModule.ClassNameMapper} for use in mapping names. * @return String */ static String createInnerClassNameFromDC (DataConstructor dc, LECCModule module) { // This method *cannot* directly call createInnerClassNameFromDC_internal, as the returned name needs to be // in sync with the name returned by createClassName (which performs name truncation on the entire class name), // and thus we delegate to createClassName and extract the inner class name portion from the entire class name. final String unqualifiedClassName = createClassName(dc, module); final int posOfLastDollarChar = unqualifiedClassName.lastIndexOf('$'); if (posOfLastDollarChar == -1) { throw new IllegalStateException("The unqualified name of the inner class for a data constructor does not contain a '$'."); } return unqualifiedClassName.substring(posOfLastDollarChar + 1); } /** * Create a fully qualified class name corresponding to the given TypeConstructor. * @param typeCons * @param module the LECCModule instance corresponding to either the module defining the entity, or one of its dependent modules. * This is used for obtaining the appropriate {@link LECCModule.ClassNameMapper} for use in mapping names. * @return String */ static String createFullClassNameFromType (TypeConstructor typeCons, LECCModule module) { String className = createClassNameFromType (typeCons, module); String packageName = createFullPackageNameFromModule(typeCons.getName().getModuleName()); return compound(packageName, className); } /** * Create a JavaTypeName corresponding to a TypeConsApp. * @param typeCons the name of the module that the type is defined in. * @param module the LECCModule instance corresponding to either the module defining the entity, or one of its dependent modules. * This is used for obtaining the appropriate {@link LECCModule.ClassNameMapper} for use in mapping names. * @return JavaTypeName */ static JavaTypeName createTypeNameFromType (TypeConstructor typeCons, LECCModule module) { String packageName = createFullPackageName (typeCons); String className = createClassNameFromType (typeCons, module); return JavaTypeName.make(compound(packageName, className), false); } /** * Create a JavaTypeName corresponding to the class used to * represent multiple zero arity DCs for a data type. * @param typeCons * @param module the LECCModule instance corresponding to either the module defining the entity, or one of its dependent modules. * This is used for obtaining the appropriate {@link LECCModule.ClassNameMapper} for use in mapping names. * @return JavaTypeName */ static JavaTypeName createTypeNameForTagDCFromType (TypeConstructor typeCons, LECCModule module) { String packageName = createFullPackageName(typeCons); String unqualifiedClassName = createUnqualifiedClassNameForTagDCFromType(typeCons, module); return JavaTypeName.make(compound(packageName, unqualifiedClassName), false); } /** * @param typeCons a type constructor entity. * @param module the LECCModule instance corresponding to either the module defining the entity, or one of its dependent modules. * This is used for obtaining the appropriate {@link LECCModule.ClassNameMapper} for use in mapping names. * @return the unqualified class name to use for the tag DC class for that type. */ static String createUnqualifiedClassNameForTagDCFromType (TypeConstructor typeCons, LECCModule module) { String typeClassName = createClassNameFromType (typeCons, module); return typeClassName + "$TagDC"; } /** * Create a JavaTypeName corresponding to a data constructor. * @param dc * @param module the LECCModule instance corresponding to either the module defining the entity, or one of its dependent modules. * This is used for obtaining the appropriate {@link LECCModule.ClassNameMapper} for use in mapping names. * @return JavaTypeName */ static JavaTypeName createTypeNameFromDC (DataConstructor dc, LECCModule module) { return JavaTypeName.make(createFullClassNameFromDC(dc, module), false); } /** * Create a JavaTypeName corresponding to a data constructor. * @param dcName * @param module the LECCModule instance corresponding to either the module defining the entity, or one of its dependent modules. * @return full class name */ static String createFullClassNameFromDC (QualifiedName dcName, LECCModule module) { // Determine whether this DC is going to be in a shared class. module = (LECCModule)module.findModule(dcName.getModuleName()); DataConstructor dc = module.getModuleTypeInfo().getDataConstructor(dcName.getUnqualifiedName()); return createFullClassNameFromDC(dc, module); } /** * Create a class name, without package, for the generated class for * a data constructor. * @param dc * @param module the LECCModule instance corresponding to either the module defining the entity, or one of its dependent modules. * This is used for obtaining the appropriate {@link LECCModule.ClassNameMapper} for use in mapping names. * @return the class name */ private static String createClassNameFromDC (DataConstructor dc, LECCModule module) { // Determine whether this DC is going to be in a shared class. TypeConstructor typeCons = dc.getTypeConstructor(); boolean inSharedClass = false; if (dc.getArity() == 0) { int nTagDCs = 0; for (int i = 0, nDCs = typeCons.getNDataConstructors(); i < nDCs; ++i) { if (typeCons.getNthDataConstructor(i).getArity() == 0) { nTagDCs++; if (nTagDCs > 1) { inSharedClass = true; break; } } } } String className; if (inSharedClass) { className = createUnqualifiedClassNameForTagDCFromType(typeCons, module); } else { className = createClassName(dc, module); } return className; } /** * Create a full class name corresponding to a data constructor. * @param dc * @param module the LECCModule instance corresponding to either the module defining the entity, or one of its dependent modules. * This is used for obtaining the appropriate {@link LECCModule.ClassNameMapper} for use in mapping names. * @return full class name */ private static String createFullClassNameFromDC (DataConstructor dc, LECCModule module) { String packageName = createFullPackageNameFromModule(dc.getName().getModuleName()); String className = createClassNameFromDC (dc, module); return compound(packageName, className); } /** * Create an unqualified (no package) class name for the inner FieldSelection * class associated with the given DC. * @param dc * @param module the LECCModule instance corresponding to either the module defining the entity, or one of its dependent modules. * This is used for obtaining the appropriate {@link LECCModule.ClassNameMapper} for use in mapping names. * @return the FieldSelection class name. */ static String createFieldSelectionClassNameFromDC (DataConstructor dc, LECCModule module) { String className = createClassNameFromDC (dc, module); className = className + "$FieldSelection"; return className; } /** * @param dc * @param module the LECCModule instance corresponding to either the module defining the entity, or one of its dependent modules. * This is used for obtaining the appropriate {@link LECCModule.ClassNameMapper} for use in mapping names. * @return a JavaTypeName for the inner FieldSelection class. */ static JavaTypeName createFieldSelectionClassTypeNameFromDC (DataConstructor dc, LECCModule module) { String className = createFullClassNameFromDC (dc, module); className = className + "$FieldSelection"; return JavaTypeName.make(className, false); } /** * Create a JavaTypeName corresponding to the named supercombinator. * @param scName * @param module the LECCModule instance corresponding to either the module defining the entity, or one of its dependent modules. * This is used for obtaining the appropriate {@link LECCModule.ClassNameMapper} for use in mapping names. * @return JavaTypeName */ static JavaTypeName createTypeNameFromSC (QualifiedName scName, LECCModule module) { JavaTypeName primitiveFunctionType = primitiveFunctionsMap.get(scName); if (primitiveFunctionType != null) { return primitiveFunctionType; } String className = createClassNameFromSC(scName, module); String packageName = createFullPackageNameFromModule(scName); return JavaTypeName.make(compound(packageName, className), false); } /** * @param scName the name of a function. * @param module the LECCModule instance corresponding to either the module defining the entity, or one of its dependent modules. * This is used for obtaining the appropriate {@link LECCModule.ClassNameMapper} for use in mapping names. * @return the unqualified inner class name to use to represent a lazy app node for that function. */ static String createLazyInnerClassNameFromSC (QualifiedName scName, LECCModule module) { return createInnerClassNameFromSC(scName, module, false); } /** * @param scName the name of a function. * @param module the LECCModule instance corresponding to either the module defining the entity, or one of its dependent modules. * This is used for obtaining the appropriate {@link LECCModule.ClassNameMapper} for use in mapping names. * @return the unqualified inner class name to use to represent a strict app node for that function. */ static String createStrictInnerClassNameFromSC (QualifiedName scName, LECCModule module) { return createInnerClassNameFromSC(scName, module, true); } /** * @param scName the name of a function. * @param module the LECCModule instance corresponding to either the module defining the entity, or one of its dependent modules. * This is used for obtaining the appropriate {@link LECCModule.ClassNameMapper} for use in mapping names. * @param strict true if the class name is for a strict application node * @return the unqualified inner class name to use to represent a strict app node for that function. */ static String createInnerClassNameFromSC (QualifiedName scName, LECCModule module, boolean strict) { // First get the name of the outer class. createClassNameFromSC handles mapping scName to the // appropriate function group. String outerClassName = createClassNameFromSC(scName, module); String innerClassName = outerClassName + (strict ? "$RTAppS" : "$RTAppL"); // If this is a primitive function we can stop there. Otherwise we append the name // of the function which the application node is specific to. This is to handle // function groups which can contain multiple inner application node classes. // Since primitive functions are never grouped there is no need to disambiguate. boolean needToDisambiguate = primitiveFunctionsMap.get(scName) == null; if (needToDisambiguate) { // If this is not a primitive we can check to see if the function is alone in // its function group. If there is only one function in the function group we // don't need to disambiguate. ModuleName containingModuleName = scName.getModuleName(); LECCModule containingModule = (LECCModule)module.findModule(containingModuleName); if (containingModule != null) { FunctionGroupInfo fgi = containingModule.getFunctionGroupInfo(scName); if(fgi.getNFunctions() == 1) { needToDisambiguate = false; } } } if (needToDisambiguate) { String suffix = fixupUnqualifiedName(scName.getModuleName(), scName.getUnqualifiedName(), module); char[] ln = suffix.toCharArray(); ln[0] = Character.toUpperCase(ln[0]); innerClassName = innerClassName + "_" + new String(ln); } // Now we need to check for excessive length. return doClassNameTruncation(innerClassName, scName.getModuleName(), module, true); } /** * Java versions 1.4 and previous for Windows do not handle long file paths (i.e. greater than 256 characters). * This is a problem when accessing files using the standard Java APIs. Also it is a problem when using * the Java compiler (jcc) to compiler automatically generated Java source. * In an attemp to ameliorate this problem we truncate excessively long class names. * @param className - class name to be truncated. * @param moduleName - name of the containing module. * @param module - the containing module or a module dependent on the containing module. * @param innerClass - true if the class name is for an inner class. * @return the truncated class name. */ private static String doClassNameTruncation (final String className, ModuleName moduleName, LECCModule module, boolean innerClass) { if (moduleName == null){ // if the symbol is not in the map then the caller must have passed in // the module name. Only callers that are making the object corresponding // to the symbol should pass in the module name. throw new IllegalArgumentException(); } // Find the module that is named by moduleName. // // Note that by the precondition on the module parameter (namely that the specified module // must either be the one named by moduleName, or one of its dependent modules), // this call to findModule should always succeed. module = (LECCModule)module.findModule(moduleName); LECCModule.ClassNameMapper classNameMapper = module.getClassNameMapper(); // We synchronize on the classNameMapper because we want to query it and potentially add a mapping to it // all atomically. synchronized (classNameMapper) { String fixedName = classNameMapper.getFixedName(className); if (fixedName != null) { return fixedName; } fixedName = className; if (CALToJavaNames.TRUNCATE_LONG_CLASS_NAMES) { // If the fixed up name is too long we want to truncate it and make it unique. if (fixedName.length() > (innerClass ? INNER_CLASS_NAME_LENGTH_LIMIT : OUTER_CLASS_NAME_LENGTH_LIMIT)) { fixedName = fixedName.substring(0, INNER_CLASS_NAME_LENGTH_LIMIT) + getNextTruncationDisambiguator() + "_"; } } classNameMapper.addMapping(className, fixedName); return fixedName; } } /** * @param scName the name of a function. * @param module the LECCModule instance corresponding to either the module defining the entity, or one of its dependent modules. * This is used for obtaining the appropriate {@link LECCModule.ClassNameMapper} for use in mapping names. * @return the type name to use to represent a lazy app node for that function. */ static JavaTypeName createLazyInnerTypeNameFromSC (QualifiedName scName, LECCModule module) { String fullOuter = createFullClassNameFromSC(scName, module); String innerClassName = createLazyInnerClassNameFromSC(scName, module); String fullInnerClassName = fullOuter.substring(0, fullOuter.lastIndexOf('.') + 1) + innerClassName; return JavaTypeName.make(fullInnerClassName, false); } /** * @param scName the name of a function. * @param module the LECCModule instance corresponding to either the module defining the entity, or one of its dependent modules. * This is used for obtaining the appropriate {@link LECCModule.ClassNameMapper} for use in mapping names. * @return the type name to use to represent a strict app node for that function. */ static JavaTypeName createStrictInnerTypeNameFromSC (QualifiedName scName, LECCModule module) { String fullOuter = createFullClassNameFromSC(scName, module); String innerClassName = createStrictInnerClassNameFromSC(scName, module); String fullInnerClassName = fullOuter.substring(0, fullOuter.lastIndexOf('.') + 1) + innerClassName; return JavaTypeName.make(fullInnerClassName, false); } /** * Create a fully qualified class name from the unqualified class name and containing package. * @param packageName * @param className * @return String */ private static String createFullClassName (String packageName, String className) { return compound(createFullPackageName(packageName), className); } /** * If className represents a class for a lecc module, return the module name. If not, return null. * * @param qualifiedClassName the name of the class. * @return the last component in the package name, if the class represents a class in a lecc module. * If not, return null. */ static ModuleName getModuleNameFromPackageName(String qualifiedClassName) { // Get the package of the request class. int lastPeriodIndex = qualifiedClassName.lastIndexOf('.'); int secondLastPeriodIndex = qualifiedClassName.lastIndexOf('.', lastPeriodIndex - 1); // lastIndexOf() handles second arg < 0 // Check that there are at least two periods. if (secondLastPeriodIndex < 0) { return null; } // Check the package name, minus the last package segment. String subPackageName = qualifiedClassName.substring(0, secondLastPeriodIndex); if (!subPackageName.equals(LECCMachineConfiguration.ROOT_PACKAGE)) { return null; } // Analyze the last package segment. String lastPackageNameSegment = qualifiedClassName.substring(secondLastPeriodIndex + 1, lastPeriodIndex); return ProgramResourceLocator.createModuleNameFromPackageNameSegment(lastPackageNameSegment); } /** * Return the name of the class, in a form that can be used in source code. * eg. [[B ==> byte[][]. * CALExecutor$ForeignFunctionException ==> CALExecutor.ForiegnFunctionException. * @param name * @return String */ static String fixupClassName (String name) { // Count the number of array dimensions (if any). int i = 0; while (name.startsWith("[")) { i++; name = name.substring(1); } if (name.startsWith ("L") && name.endsWith(";")) { // This is a fully qualified class name. name = name.substring (1, name.length() - 1); } else if (name.equals ("Z")) { // boolean name = "boolean"; } else if (name.equals ("B")) { name = "byte"; } else if (name.equals ("C")) { name = "char"; } else if (name.equals("S")) { name = "short"; } else if (name.equals ("I")) { name = "integer"; } else if (name.equals ("J")) { name = "long"; } else if (name.equals ("F")) { name = "float"; } else if (name.equals("D")) { name = "double"; } for (int j = 0; j < i; ++j) { name = name + "[]"; } // Substitute . for $ name = name.replace ('$', '.'); return name; } /** * Some CAL identifier names are not valid java identifiers and need to be adjusted. * * Note this produces a name that is safe to save as a Windows filename. In other words, case mangling occurs so * that upper case letters are replaced by an underscore followed by an upper case letter. * * @param moduleName The name of the module that the name symbol is defined in. * @param name the unqualified sc or type name. * @param module the LECCModule instance corresponding to either the module defining the entity, or one of its dependent modules. * This is used for obtaining the appropriate {@link LECCModule.ClassNameMapper} for use in mapping names. * @return String */ /* @implementation if you change this method, there's a good chance you need to change fixupInnerClassName as well. */ private static String fixupUnqualifiedName (ModuleName moduleName, String name, LECCModule module) { if (name.equals ("$if") || name.equals ("if")) { return "if"; } if (moduleName == null){ // if the symbol is not in the map then the caller must have passed in // the module name. Only callers that are making the object corresponding // to the symbol should pass in the module name. throw new IllegalArgumentException(); } // Find the module that is named by moduleName. // // Note that by the precondition on the module parameter (namely that the specified module // must either be the one named by moduleName, or one of its dependent modules), // this call to findModule should always succeed. module = (LECCModule)module.findModule(moduleName); LECCModule.ClassNameMapper classNameMapper = module.getClassNameMapper(); final String originalName = name; // We synchronize on the classNameMapper because we want to query it and potentially add a mapping to it // all atomically. synchronized (classNameMapper) { String fixedName = classNameMapper.getFixedName(originalName); if (fixedName != null) { return fixedName; } name = smartNameShorten(name); // Remove invalid java characters (ex. '.' or '$') etc. fixedName = cleanSCName(name); if (CALToJavaNames.TRUNCATE_LONG_CLASS_NAMES) { // If the fixed up name is too long we want to truncate it and make it unique. if (fixedName.length() > OUTER_CLASS_NAME_LENGTH_LIMIT) { fixedName = fixedName.substring(0, OUTER_CLASS_NAME_LENGTH_LIMIT) + getNextTruncationDisambiguator() + "_"; } } classNameMapper.addMapping(originalName, fixedName); return fixedName; } } /** * This function attempts to do intelligent shortening of CAL * names before they are converted to Java names. * * @param name * @return the shortened name. */ private static String smartNameShorten(String name) { // Currently we only handle the case of shortening dictionary function // names. The intention is to add handling for other recognizable // situations where a name can be shortened in an intelligent fashion. if (name.startsWith("$dict")) { name = shortenDictName(name); } return name; } /** * Shorten the name of a dictionary function. * @param name - the original name of the dictionary function. * @return - the shortened name. */ private static String shortenDictName (String name) { // dictionary functions have names with the pattern: // $dictClassModuleName.ClassName#DataTypeModuleName.DataTypeName // For example for the class Enum and the data type Byte, both in // the module CAL.Core.Prelude the name would be: // $dictCal.Core.Prelude.Enum#Cal.Core.Prelude.Byte // // This generated class will be in the package corresponding to // the data type module. Since there can only be one data type // with a given name in a module it is safe to shorten the // name be removing the module names. // i.e. the above name would become: $dict.Enum#Byte int hashIndex = name.indexOf('#'); if (hashIndex > 0) { String className = name.substring(5, hashIndex); className = className.substring(className.lastIndexOf('.')); String instanceName = name.substring(name.lastIndexOf('.')); name = "$dict" + className + instanceName; } return name; } /** * A synchronized static method for obtaining the next disambiguator to use for a truncated class name. * @return the next disambiguator. */ private static int getNextTruncationDisambiguator() { return nTruncatedNames.incrementAndGet(); } /** * Clean up a supercombinator name so that it is * a valid java name. * @param name * @return the cleaned name. */ static String cleanSCName(String name) { if (isReservedWord(name)) { //there are no reserved words that start with an underscore. name = name + "_"; } //Substitute _ for # //Substitute _ for . //Substitute _ for $ StringBuilder sb = new StringBuilder(); char c = name.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: sb.append(c); break; } for (int i = 1, n = name.length(); i < n; ++i) { c = name.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); break; } } } return sb.toString(); } /** * @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. */ static 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; } /** * 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 */ static String fixupVarName(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; } /** * @param packageName * @param className * @return packageName + "." + className. */ private static String compound(String packageName, String className) { return new StringBuilder(packageName).append('.').append(className).toString(); } /** * Build the java name for a let variable definition function. * @param qualifiedDefFunctionName * @param module * @return the Java name for the let variable definition function. */ static String makeLetVarDefFunctionJavaName (QualifiedName qualifiedDefFunctionName, LECCModule module) { String defFunctionName = qualifiedDefFunctionName.getUnqualifiedName(); FunctionGroupInfo fgi = module.getFunctionGroupInfo(qualifiedDefFunctionName); if (fgi != null && fgi.getNFunctions() == 1) { int firstDollarIndex = defFunctionName.indexOf('$'); if (firstDollarIndex > 0) { int secondDollarIndex = defFunctionName.indexOf('$', firstDollarIndex + 1); if (secondDollarIndex > firstDollarIndex) { defFunctionName = defFunctionName.substring(firstDollarIndex + 1); } } } return CALToJavaNames.fixupVarName(defFunctionName); } }