Package org.openquark.cal.internal.machine.lecc

Source Code of org.openquark.cal.internal.machine.lecc.CALToJavaNames

/*
* 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);
    }
}
TOP

Related Classes of org.openquark.cal.internal.machine.lecc.CALToJavaNames

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