/*
* 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.
*/
/*
* ModuleTypeInfo.java
* Creation date: (June 6, 2001)
* By: Bo Ilic
*/
package org.openquark.cal.compiler;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.openquark.cal.internal.serialization.ModuleSerializationTags;
import org.openquark.cal.internal.serialization.RecordInputStream;
import org.openquark.cal.internal.serialization.RecordOutputStream;
import org.openquark.cal.internal.serialization.RecordInputStream.RecordHeaderInfo;
import org.openquark.cal.machine.Module;
import org.openquark.cal.util.ArrayMap;
import org.openquark.cal.util.ArraySet;
/**
* Provides information about the top-level entities defined by the module,
* for use by external clients or dependent modules.
*
* Note that ModuleTypeInfo is immutable with respect to external clients.
*
* Creation date: (June 6, 2001)
* @author Bo Ilic
*/
public final class ModuleTypeInfo {
private static final int serializationSchema = 0;
/** the name of this module. */
private final ModuleName moduleName;
/**
* (ModuleName) the modules that are friends of this Module. Friend modules are able to access protected elements of
* this module.
*/
private final ArraySet<ModuleName> friendModulesSet;
/**
* (ModuleName->ModuleTypeInfo) a map from the names of the modules imported by this module to their ModuleTypeInfo objects.
* Ordered by import order within the CAL source of the module.
*/
private final ArrayMap<ModuleName, ModuleTypeInfo> importedModuleMap;
/**
* (String -> ModuleName) map from an external function or class method's name to the name of its defining module.
* The function or class method must be visible within this module (i.e. it must be public, and the
* module in which it is defined must be imported in the current module).
* These are the external function and class methods that can be used in an unqualified way within this module.
*/
private final Map<String, ModuleName> usingFunctionOrClassMethodMap;
/**
* (String -> ModuleName) map from an external data constructor name to the name of its defining module.
* The data constructor must be visible within this module (i.e. it must be public, and the
* module in which it is defined must be imported in the current module).
* These are the external data constructors that can be used in an unqualified way within this module.
*/
private final Map<String, ModuleName> usingDataConstructorMap;
/**
* (String -> ModuleName) map from an external type constructor name to the name of its defining module.
* The type constructor must be visible within this module (i.e. it must be public, and the
* module in which it is defined must be imported in the current module).
* These are the external type constructors that can be used in an unqualified way within this module.
*/
private final Map<String, ModuleName> usingTypeConstructorMap;
/**
* (String -> ModuleName) map from an external type class's name to the name of its defining module.
* The type class must be visible within this module (i.e. it must be public, and the
* module in which it is defined must be imported in the current module).
* These are the external type classes that can be used in an unqualified way within this module.
*/
private final Map<String, ModuleName> usingTypeClassMap;
/**
* (String->TypeConstructor) a map from the names of the type constructors defined in the module to their entity objects.
* Ordered by definition order within the CAL source of the module.
*/
private final ArrayMap<String, ScopedEntity> typeConstructorMap;
/**
* (String->TypeClass) a map from the names of the type classes defined in the module to their TypeClass objects.
* Ordered by definition order within the CAL source of the module.
*/
private final ArrayMap<String, ScopedEntity> typeClassMap;
/**
* (ClassInstanceIdentifier->ClassInstance) a map from the names of the class instances defined in this module to their ClassInstance objects.
* Ordered by definition order within the CAL source of the module.
*/
private final ArrayMap<ClassInstanceIdentifier, ClassInstance> classInstanceMap;
/**
* (String->Function) a map from the names of the functions defined in this module to their entity objects.
* Ordered by definition order within the CAL source of the module.
*/
private final ArrayMap<String, ScopedEntity> functionMap;
/**
* (String->ClassMethod) A cache used to quickly look up class methods.
* Type classes hold onto their class methods.
*/
private final transient Map<String, ClassMethod> cachedClassMethodsMap;
/**
* (String->DataConstructor) A cache used to quickly look up data constructors.
* Type constructors hold onto their data constructors.
*/
private final transient Map<String, DataConstructor> cachedDataConstructorsMap;
/** (ModuleName->ModuleTypeInfo) Map from module name to module type info for direct and indirect dependees of this module,
* not including this module. */
private final transient Map<ModuleName, ModuleTypeInfo> cachedDependeeModuleTypeInfoMap;
/** A base instance of ModuleTypeInfo to extend. This is used for extension modules. */
private final ModuleTypeInfo baseTypeInfo;
/** A reference to the containing Module object. */
private final Module module;
/** Source metrics associated with this module */
private ModuleSourceMetrics moduleSourceMetrics;
/** Set of QualifiedNames from imported modules that occur in this module */
private Set<QualifiedName> importedNameOccurrences;
/** The CALDoc comment for this module, or null if there is none. */
private CALDocComment calDocComment;
/** The module name resolver for resolving module names in the context of this module. */
private ModuleNameResolver moduleNameResolver;
/**
* This constructor is for internal CAL compiler use only.
*
* Constructor to use for non-extension modules.
* @param moduleName the name of the module. Cannot be null.
* @param module back pointer to the module. Cannot be null.
*/
public ModuleTypeInfo(ModuleName moduleName, Module module) {
if (moduleName == null || module == null) {
throw new NullPointerException();
}
this.moduleName = moduleName;
this.module = module;
friendModulesSet = new ArraySet<ModuleName>();
importedModuleMap = new ArrayMap<ModuleName, ModuleTypeInfo>();
usingFunctionOrClassMethodMap = new HashMap<String, ModuleName>();
usingDataConstructorMap = new HashMap<String, ModuleName>();
usingTypeConstructorMap = new HashMap<String, ModuleName>();
usingTypeClassMap = new HashMap<String, ModuleName>();
moduleNameResolver = ModuleNameResolver.make(moduleName, importedModuleMap.keySet());
typeConstructorMap = new ArrayMap<String, ScopedEntity>();
typeClassMap = new ArrayMap<String, ScopedEntity>();
classInstanceMap = new ArrayMap<ClassInstanceIdentifier, ClassInstance>();
functionMap = new ArrayMap<String, ScopedEntity>();
cachedClassMethodsMap = new HashMap<String, ClassMethod>();
cachedDataConstructorsMap = new HashMap<String, DataConstructor>();
cachedDependeeModuleTypeInfoMap = new HashMap<ModuleName, ModuleTypeInfo>();
baseTypeInfo = null;
moduleSourceMetrics = null;
importedNameOccurrences = null;
calDocComment = null;
}
/**
* Creates a ModuleTypeInfo instance which extends the specified base type info.
* This is used for extension modules.
* @param baseTypeInfo
*/
ModuleTypeInfo (ModuleTypeInfo baseTypeInfo) {
this.moduleName = null;
this.module = baseTypeInfo.module;
friendModulesSet = null;
importedModuleMap = null;
usingFunctionOrClassMethodMap = null;
usingDataConstructorMap = null;
usingTypeConstructorMap = null;
usingTypeClassMap = null;
moduleNameResolver = null;
typeConstructorMap = new ArrayMap<String, ScopedEntity>();
typeClassMap = new ArrayMap<String, ScopedEntity>();
classInstanceMap = new ArrayMap<ClassInstanceIdentifier, ClassInstance>();
functionMap = new ArrayMap<String, ScopedEntity>();
cachedClassMethodsMap = new HashMap<String, ClassMethod>();
cachedDataConstructorsMap = new HashMap<String, DataConstructor>();
cachedDependeeModuleTypeInfoMap = new HashMap<ModuleName, ModuleTypeInfo>();
if (baseTypeInfo.baseTypeInfo != null) {
throw new IllegalArgumentException("can't extend an extension module.");
}
this.baseTypeInfo = baseTypeInfo;
moduleSourceMetrics = null;
calDocComment = null;
}
/**
* @return boolean True if this ModuleTypeInfo is an extension module (used for adjuncts, and
* other temporary modifications of regular CAL modules).
*/
boolean isExtension() {
return baseTypeInfo != null;
}
/**
* Creation date: (6/6/01 10:05:52 AM)
* @return the name of the Module that this ModuleTypeInfo has type info for
*/
public ModuleName getModuleName() {
if (baseTypeInfo != null) {
return baseTypeInfo.getModuleName();
}
return moduleName;
}
/**
* @param moduleName
* @return true if the module with name 'moduleName' is a friend of this module.
*/
public boolean hasFriendModule(ModuleName moduleName) {
if (baseTypeInfo != null) {
return baseTypeInfo.hasFriendModule(moduleName);
}
return friendModulesSet.contains(moduleName);
}
void addFriendModule(ModuleName moduleName) {
if (baseTypeInfo != null) {
throw new UnsupportedOperationException("Adding friend modules to an adjunct is unsupported");
}
friendModulesSet.add(moduleName);
}
/**
* @return number of friend modules that this module declares.
*/
public int getNFriendModules() {
return friendModulesSet.size();
}
/**
* @param n zero-based index.
* @return the name of a friend module declared by this module.
*/
public ModuleName getNthFriendModule(int n) {
return friendModulesSet.get(n);
}
/**
* True if the entity can be used lexically in this CAL module.
* In particular, the entity must be
* a) defined in this module
* or
* b) defined in a module imported by this module, be public in that module, or this module is a friend
* of that module.
*
* @param scopedEntity
* @return true if the given scoped entity can be used lexically in this module.
*/
public boolean isEntityVisible(ScopedEntity scopedEntity) {
if (baseTypeInfo != null) {
return baseTypeInfo.isEntityVisible(scopedEntity);
}
ModuleName entityModuleName = scopedEntity.getName().getModuleName();
if (entityModuleName.equals(moduleName)) {
//an entity can always be used within its own module
return true;
}
ModuleTypeInfo importedModuleTypeInfo = getImportedModule(entityModuleName);
if (importedModuleTypeInfo == null) {
//this module does not import the module in which the entity is defined, and so the entity is not visible.
return false;
}
Scope scope = scopedEntity.getScope();
return scope.isPublic() || (scope.isProtected() && importedModuleTypeInfo.hasFriendModule(moduleName));
}
/**
* A functional agent is either a function, a data constructor or a class method.
* Clients that don't care what specific kind of functional agent they are using, only that
* it can be used to transform, can call this method.
* Creation date: (6/7/01 9:20:30 AM)
* @param functionalAgentName
* @return FunctionalAgent the entity or null if it doesn't exist
*/
public FunctionalAgent getFunctionalAgent(String functionalAgentName) {
int nameLength = functionalAgentName.length();
if (nameLength == 0) {
return null;
}
char firstChar = functionalAgentName.charAt(0);
if (Character.isUpperCase(firstChar)) {
return getDataConstructor(functionalAgentName);
}
return getFunctionOrClassMethod(functionalAgentName);
}
public FunctionalAgent getFunctionOrClassMethod(String functionOrClassMethodName) {
if (baseTypeInfo != null) {
FunctionalAgent entity = baseTypeInfo.getFunctionOrClassMethod(functionOrClassMethodName);
if (entity != null) {
return entity;
}
}
FunctionalAgent entity = getFunction(functionOrClassMethodName);
if (entity != null) {
return entity;
}
return getClassMethod(functionOrClassMethodName);
}
/**
* An internal helper to get a function or class method that is in a module
* imported into this module, either directly or indirectly and regardless
* of the scope of the given entity.
* This is mostly for internal compiler usage where class method functions
* can resolve to be functions that are not directly visible within a module.
*
* @param functionOrClassMethodName
* @return FunctionalAgent
*/
FunctionalAgent getReachableFunctionOrClassMethod(QualifiedName functionOrClassMethodName) {
if (baseTypeInfo != null) {
FunctionalAgent functionOrClassMethod = baseTypeInfo.getReachableFunctionOrClassMethod(functionOrClassMethodName);
if (functionOrClassMethod != null) {
return functionOrClassMethod;
}
}
ModuleName functionOrClassMethodModuleName = functionOrClassMethodName.getModuleName();
if(functionOrClassMethodModuleName.equals(moduleName)) {
return getFunctionOrClassMethod(functionOrClassMethodName.getUnqualifiedName());
}
ModuleTypeInfo moduleTypeInfo = getDependeeModuleTypeInfo(functionOrClassMethodModuleName);
if (moduleTypeInfo == null) {
return null;
}
return moduleTypeInfo.getFunctionOrClassMethod(functionOrClassMethodName.getUnqualifiedName());
}
/**
* Method getFunctionalAgents.
* A functional agent is either a function, a data constructor or a class method.
* i.e. anything that to an external client "looks like" a top-level named function or "Gem".
* @return FunctionalAgent[] the array of functional agents in this module. Includes entities
* like Cons which can be used in CAL text in operator form, but not hidden functions
* introduced in the implementation (e.g. lifted lambda expressions).
*/
public FunctionalAgent[] getFunctionalAgents() {
List<FunctionalAgent> functionalAgentList = new ArrayList<FunctionalAgent>();
if (baseTypeInfo != null) {
FunctionalAgent baseEntities[] = baseTypeInfo.getFunctionalAgents();
for (int i = 0; i < baseEntities.length; ++i) {
functionalAgentList.add(baseEntities[i]);
}
}
//add the functions
for (int i = 0, nFunctions = getNFunctions(); i < nFunctions; ++i) {
functionalAgentList.add(getNthFunction(i));
}
//add the data constructors
for (int i = 0, nTypeCons = getNTypeConstructors(); i < nTypeCons; ++i) {
TypeConstructor typeCons = getNthTypeConstructor(i);
for (int j = 0, nDataCons = typeCons.getNDataConstructors(); j < nDataCons; ++j) {
functionalAgentList.add(typeCons.getNthDataConstructor(j));
}
}
//add the class methods
for (int i = 0, nTypeClasses = getNTypeClasses(); i < nTypeClasses; ++i) {
TypeClass typeClass = getNthTypeClass(i);
for (int j = 0, nClassMethods = typeClass.getNClassMethods(); j < nClassMethods; ++j) {
functionalAgentList.add(typeClass.getNthClassMethod(j));
}
}
return functionalAgentList.toArray(new FunctionalAgent[0]);
}
/**
* Method getImportedModule.
* @param importedModuleName
* @return ModuleTypeInfo the imported module or null if the module does not import a module with the given name.
*/
public ModuleTypeInfo getImportedModule(ModuleName importedModuleName) {
if (baseTypeInfo != null) {
return baseTypeInfo.getImportedModule(importedModuleName);
}
return importedModuleMap.get(importedModuleName);
}
/**
* Get the module type info for the named dependee.
* The dependency can be direct or indirect.
* @param dependeeModuleName
* @return the ModuleTypeInfo for the named module if it is a dependee of this module, otherwise null
*/
public ModuleTypeInfo getDependeeModuleTypeInfo (ModuleName dependeeModuleName) {
// Synchronize cache access to ensure thread-safety.
synchronized (cachedDependeeModuleTypeInfoMap) {
if (cachedDependeeModuleTypeInfoMap.isEmpty() && getNImportedModules() != 0) {
populateDependeeModuleTypeInfoMapHelper(this);
// An entry will have been entered for the current module, so we need to remove it.
cachedDependeeModuleTypeInfoMap.remove(getModuleName());
}
return cachedDependeeModuleTypeInfoMap.get(dependeeModuleName);
}
}
/**
* Helper method to populate dependeeModuleTypeInfoMap.
* @param currentModuleTypeInfo the ModuleTypeInfo being traversed.
*/
private void populateDependeeModuleTypeInfoMapHelper(ModuleTypeInfo currentModuleTypeInfo) {
ModuleName currentModuleName = currentModuleTypeInfo.getModuleName();
if (cachedDependeeModuleTypeInfoMap.containsKey(currentModuleName)) {
return;
}
cachedDependeeModuleTypeInfoMap.put(currentModuleName, currentModuleTypeInfo);
for (int i = 0; i < currentModuleTypeInfo.getNImportedModules(); ++i) {
ModuleTypeInfo mti = currentModuleTypeInfo.getNthImportedModule(i);
populateDependeeModuleTypeInfoMapHelper(mti);
}
}
/**
* @return The ModuleSourceMetrics object associated with this module
*/
ModuleSourceMetrics getModuleSourceMetrics() {
return moduleSourceMetrics;
}
/**
* @param qualifiedName
* @return true if qualifiedName occurs in this module and is from an imported module.
*/
boolean doesImportedNameOccur(QualifiedName qualifiedName) {
// I did not initialize this with an empty data structure
// since the presence or absence of this value is used to
// detect an error when the variable is initialized.
if (importedNameOccurrences == null){
return false;
}
return importedNameOccurrences.contains(qualifiedName);
}
/**
* Set the ModuleSourceMetrics object associated with this module
* @param moduleSourceMetrics The ModuleSourceMetrics to associate with this module
*/
void setModuleSourceMetrics(ModuleSourceMetrics moduleSourceMetrics) {
if (this.moduleSourceMetrics != null) {
throw new IllegalArgumentException("The module " + moduleName + " already has an associated ModuleSourceMetrics object");
}
this.moduleSourceMetrics = moduleSourceMetrics;
}
/**
* Set the importedNameOccurrences associated with this module.
* @param importedNameOccurrences the importedNameOccurrences to associate with this module
*/
void setImportedNameOccurrences(Set<QualifiedName> importedNameOccurrences) {
if(this.importedNameOccurrences != null) {
throw new IllegalArgumentException("The module " + moduleName + " already has an associated importedNameOccurrences object.");
}
this.importedNameOccurrences = importedNameOccurrences;
}
/**
* @return the CALDoc comment for this module, or null if there is none.
*/
public CALDocComment getCALDocComment() {
return calDocComment;
}
/**
* Set the CALDoc comment associated with this module.
* @param comment the CALDoc comment for this module, or null if there is none.
*/
void setCALDocComment(CALDocComment comment) {
calDocComment = comment;
}
/**
* @return whether this module is deprecated (via a CALDoc deprecated block).
*/
public final boolean isDeprecated() {
if (calDocComment == null) {
return false;
} else {
return calDocComment.getDeprecatedBlock() != null;
}
}
/**
* Method getNImportedModules.
* @return int the number of modules directly imported into this module.
*/
public int getNImportedModules() {
if (baseTypeInfo != null) {
return baseTypeInfo.getNImportedModules();
}
return importedModuleMap.size();
}
/**
* Method getNthImportedModule.
* @param n zero based index
* @return ModuleTypeInfo the nth module imported by this module
*/
public ModuleTypeInfo getNthImportedModule (int n) {
if (baseTypeInfo != null) {
return baseTypeInfo.getNthImportedModule(n);
}
return importedModuleMap.getNthValue(n);
}
/**
* Method addImportedModule.
* @param importedModuleTypeInfo
* @throws IllegalArgumentException if the imported module is already imported by this module
*/
void addImportedModule(ModuleTypeInfo importedModuleTypeInfo) {
if (baseTypeInfo != null) {
throw new IllegalStateException("can't add an imported module to an extension module.");
}
ModuleName importedModuleName = importedModuleTypeInfo.getModuleName();
if (importedModuleMap.containsKey(importedModuleName)) {
throw new IllegalArgumentException("The module " + moduleName + " already imports the module " + importedModuleName + ".");
}
importedModuleMap.put(importedModuleName, importedModuleTypeInfo);
}
/**
* This method needs to be called when all the imports are processed by {@link #addImportedModule}. This
* builds up the module name resolver needed to resolve module names.
*/
void finishAddingImportedModules() {
if (baseTypeInfo != null) {
throw new IllegalStateException("imported modules are not allowed in an extension module.");
}
moduleNameResolver = ModuleNameResolver.make(moduleName, importedModuleMap.keySet());
}
/**
* @return the module name resolver for resolving module names in the context of this module.
*/
public ModuleNameResolver getModuleNameResolver() {
if (baseTypeInfo != null) {
return baseTypeInfo.getModuleNameResolver();
}
return moduleNameResolver;
}
/**
* Method getTypeConstructor.
* @param typeConstructorName the unqualified type constructor name e.g. "Maybe".
* @return TypeConstructor the type constructor or null if the module does not define a type constructor with the given name.
*/
public TypeConstructor getTypeConstructor(String typeConstructorName) {
if (baseTypeInfo != null) {
TypeConstructor typeCons = baseTypeInfo.getTypeConstructor(typeConstructorName);
if (typeCons != null) {
return typeCons;
}
}
return (TypeConstructor) typeConstructorMap.get(typeConstructorName);
}
/**
* @param typeConstructorName name of a type constructor visible from this module
* @return TypeConstructor the type constructor or null if it does not exist or is not visible
*/
public TypeConstructor getVisibleTypeConstructor(QualifiedName typeConstructorName) {
if (baseTypeInfo != null) {
TypeConstructor typeCons = baseTypeInfo.getVisibleTypeConstructor(typeConstructorName);
if (typeCons != null) {
return typeCons;
}
}
ModuleName typeConstructorModuleName = typeConstructorName.getModuleName();
if(typeConstructorModuleName.equals(moduleName)) {
return getTypeConstructor(typeConstructorName.getUnqualifiedName());
}
ModuleTypeInfo importedModuleTypeInfo = getImportedModule(typeConstructorModuleName);
if (importedModuleTypeInfo == null) {
return null;
}
TypeConstructor typeConstructor = importedModuleTypeInfo.getTypeConstructor(typeConstructorName.getUnqualifiedName());
if (typeConstructor != null) {
Scope scope = typeConstructor.getScope();
if (scope.isPublic() || (scope.isProtected() && importedModuleTypeInfo.hasFriendModule(this.moduleName))) {
return typeConstructor;
}
}
return null;
}
/**
* An internal helper to get a type constructor that is in a module
* imported into this module, either directly or indirectly and regardless
* of the scope of the given entity.
* This is for internal use by the compile when loading serialized module files.
*
* @param typeConstructorName
* @return TypeConstructor
*/
public TypeConstructor getReachableTypeConstructor(QualifiedName typeConstructorName) {
if (baseTypeInfo != null) {
TypeConstructor typeCons = baseTypeInfo.getReachableTypeConstructor(typeConstructorName);
if (typeCons != null) {
return typeCons;
}
}
ModuleName typeConstructorModuleName = typeConstructorName.getModuleName();
if(typeConstructorModuleName.equals(moduleName)) {
return getTypeConstructor(typeConstructorName.getUnqualifiedName());
}
ModuleTypeInfo moduleTypeInfo = getDependeeModuleTypeInfo(typeConstructorModuleName);
if (moduleTypeInfo == null) {
return null;
}
return moduleTypeInfo.getTypeConstructor(typeConstructorName.getUnqualifiedName());
}
/**
* Method getNTypeConstructors.
* @return int the number of type constructors defined within the module
*/
public int getNTypeConstructors() {
int nTC = 0;
if (baseTypeInfo != null) {
nTC += baseTypeInfo.getNTypeConstructors();
}
return nTC + typeConstructorMap.size();
}
/**
* Method getNthTypeConstructor.
* @param n zero based index
* @return TypeConstructor the nth type constructor defined within the module (defined via a data or foreign data declaration).
*/
public TypeConstructor getNthTypeConstructor(int n) {
if (baseTypeInfo != null) {
if (n < baseTypeInfo.getNTypeConstructors()) {
return baseTypeInfo.getNthTypeConstructor(n);
} else {
n -= baseTypeInfo.getNTypeConstructors();
}
}
return (TypeConstructor)typeConstructorMap.getNthValue(n);
}
/**
* Method addTypeConstructor.
* @param typeCons the new type constructor to be added to the module
*/
void addTypeConstructor(TypeConstructor typeCons) {
QualifiedName typeConstructorName = typeCons.getName();
if (!moduleName.equals(typeConstructorName.getModuleName())) {
throw new IllegalArgumentException("the type constructor added must belong to the module " + moduleName + ".");
}
if (containsTypeConstructor(typeConstructorName.getUnqualifiedName())) {
throw new IllegalArgumentException("The module " + moduleName + " already defines the type " + typeConstructorName + ".");
}
typeConstructorMap.put(typeConstructorName.getUnqualifiedName(), typeCons);
}
/**
* Determine if this type info or the base type info contain the named type.
* @param name
* @return - true if if this type info or the base type info contain the named type.
*/
private boolean containsTypeConstructor (String name) {
return ((baseTypeInfo != null && baseTypeInfo.containsTypeConstructor(name)) || typeConstructorMap.containsKey(name));
}
/**
* Method getTypeClass.
* @param typeClassName the unqualified type class name e.g. "Ord".
* @return TypeClass the type class or null if the module does not define a type class with the given name.
*/
public TypeClass getTypeClass(String typeClassName) {
if (baseTypeInfo != null) {
TypeClass typeClass = baseTypeInfo.getTypeClass(typeClassName);
if (typeClass != null) {
return typeClass;
}
}
return (TypeClass) typeClassMap.get(typeClassName);
}
/**
* @param typeClassName name of a type class visible from this module
* @return TypeClass the type class or null if it does not exist or is not visible
*/
public TypeClass getVisibleTypeClass(QualifiedName typeClassName) {
if (baseTypeInfo != null) {
TypeClass typeClass = baseTypeInfo.getVisibleTypeClass(typeClassName);
if (typeClass != null) {
return typeClass;
}
}
ModuleName typeClassModuleName = typeClassName.getModuleName();
if(typeClassModuleName.equals(moduleName)) {
return getTypeClass(typeClassName.getUnqualifiedName());
}
ModuleTypeInfo importedModuleTypeInfo = getImportedModule(typeClassModuleName);
if (importedModuleTypeInfo == null) {
return null;
}
TypeClass typeClass = importedModuleTypeInfo.getTypeClass(typeClassName.getUnqualifiedName());
if (typeClass != null) {
Scope scope = typeClass.getScope();
if (scope.isPublic() || (scope.isProtected() && importedModuleTypeInfo.hasFriendModule(this.moduleName))) {
return typeClass;
}
}
return null;
}
/**
* An internal helper to get a type class that is in a module
* imported into this module, either directly or indirectly and regardless
* of the scope of the given entity.
* This is for internal use by the compile when loading serialized module files.
*
* @param typeClassName
* @return TypeClassEntity
*/
TypeClass getReachableTypeClass(QualifiedName typeClassName) {
if (baseTypeInfo != null) {
TypeClass typeClass = baseTypeInfo.getReachableTypeClass(typeClassName);
if (typeClass != null) {
return typeClass;
}
}
ModuleName typeClassModuleName = typeClassName.getModuleName();
if(typeClassModuleName.equals(moduleName)) {
return getTypeClass(typeClassName.getUnqualifiedName());
}
ModuleTypeInfo moduleTypeInfo = getDependeeModuleTypeInfo(typeClassModuleName);
if (moduleTypeInfo == null) {
return null;
}
return moduleTypeInfo.getTypeClass(typeClassName.getUnqualifiedName());
}
/**
* Method getNTypeClasses.
* @return int the number of type classes defined within the module
*/
public int getNTypeClasses() {
int nTypeClasses = 0;
if (baseTypeInfo != null) {
nTypeClasses += baseTypeInfo.getNTypeClasses();
}
return nTypeClasses + typeClassMap.size();
}
/**
* Method getNTopLevelTypeClasses
* @return int the number of type classes defined at the top level of the type info.
*/
int getNTopLevelTypeClasses () {
return typeClassMap.size();
}
/**
* Method getNthTypeClass.
* @param n zero based index
* @return TypeClassEntity the nth type class defined within the module.
*/
public TypeClass getNthTypeClass(int n) {
if (baseTypeInfo != null) {
if (n < baseTypeInfo.getNTypeClasses()) {
return baseTypeInfo.getNthTypeClass(n);
} else {
n -= baseTypeInfo.getNTypeClasses();
}
}
return (TypeClass)typeClassMap.getNthValue(n);
}
/**
* Method getNthTopLevelTypeClass.
* @param n zero based index
* @return TypeClassEntity the nth type class defined within the top level module.
*/
TypeClass getNthTopLevelTypeClass (int n) {
return (TypeClass)typeClassMap.getNthValue(n);
}
/**
* Method addTypeClass.
* @param typeClass the new type class to be added to the module
*/
void addTypeClass(TypeClass typeClass) {
QualifiedName typeClassName = typeClass.getName();
if (!moduleName.equals(typeClassName.getModuleName())) {
throw new IllegalArgumentException("the type class added must belong to the module " + moduleName + ".");
}
if (containsTypeClass (typeClassName.getUnqualifiedName())) {
throw new IllegalArgumentException("The module " + moduleName + " already defines the class " + typeClassName.getUnqualifiedName() + ".");
}
typeClassMap.put(typeClassName.getUnqualifiedName(), typeClass);
}
private boolean containsTypeClass (String name) {
return ((baseTypeInfo != null && baseTypeInfo.containsTypeClass(name)) || typeClassMap.containsKey(name));
}
/**
* Method getFunction.
* @param functionName unqualified name of the function
* @return Function the function or null if the module does not define a function with the given name.
*/
public Function getFunction(String functionName) {
if (baseTypeInfo != null) {
Function e = baseTypeInfo.getFunction(functionName);
if (e != null) {
return e;
}
}
return (Function) functionMap.get(functionName);
}
/**
* Method getNFunctionss.
* @return int number of functions defined within the module
*/
public int getNFunctions() {
int nFunctions = 0;
if (baseTypeInfo != null) {
nFunctions += baseTypeInfo.getNFunctions();
}
return nFunctions + functionMap.size();
}
/**
* Method getNthFunction.
* @param n zero based index
* @return FunctionalAgent the nth function defined within the module
*/
public Function getNthFunction(int n) {
if (baseTypeInfo != null) {
if (n < baseTypeInfo.getNFunctions()) {
return baseTypeInfo.getNthFunction(n);
} else {
n -= baseTypeInfo.getNFunctions();
}
}
return (Function)functionMap.getNthValue(n);
}
/**
* @param functionEntity the function to add to the module.
*/
void addFunction(Function functionEntity) {
if (!moduleName.equals(functionEntity.getName().getModuleName())) {
throw new IllegalArgumentException("function added must belong to the module " + moduleName + ".");
}
String functionName = functionEntity.getName().getUnqualifiedName();
if (containsFunction (functionName)) {
throw new IllegalArgumentException("The module " + moduleName + " already defines the function " + functionName + ".");
}
functionMap.put(functionName, functionEntity);
}
private boolean containsFunction (String functionName) {
return ((baseTypeInfo != null && baseTypeInfo.containsFunction(functionName)) || functionMap.containsKey(functionName));
}
/**
* @param functionName name of a function visible from this module
* @return Function the function or null if it does not exist or is not visible
*/
public Function getVisibleFunction(QualifiedName functionName) {
if (baseTypeInfo != null) {
Function e = baseTypeInfo.getVisibleFunction(functionName);
if (e != null) {
return e;
}
}
ModuleName functionModuleName = functionName.getModuleName();
if(functionModuleName.equals(moduleName)) {
return getFunction(functionName.getUnqualifiedName());
}
ModuleTypeInfo importedModuleTypeInfo = getImportedModule(functionModuleName);
if (importedModuleTypeInfo == null) {
return null;
}
Function function = importedModuleTypeInfo.getFunction(functionName.getUnqualifiedName());
if (function != null) {
Scope scope = function.getScope();
if (scope.isPublic() || (scope.isProtected() && importedModuleTypeInfo.hasFriendModule(this.moduleName))) {
return function;
}
}
return null;
}
/**
* An internal helper to get a function that is in a module
* imported into this module, either directly or indirectly and regardless
* of the scope of the given entity.
* This is for internal use by the compile when loading serialized module files.
*
* @param functionName
* @return TypeClassEntity
*/
Function getReachableFunction(QualifiedName functionName) {
if (baseTypeInfo != null) {
Function f = baseTypeInfo.getReachableFunction(functionName);
if (f != null) {
return f;
}
}
ModuleName functionModuleName = functionName.getModuleName();
if(functionModuleName.equals(moduleName)) {
return getFunction(functionName.getUnqualifiedName());
}
ModuleTypeInfo moduleTypeInfo = getDependeeModuleTypeInfo(functionModuleName);
if (moduleTypeInfo == null) {
return null;
}
return moduleTypeInfo.getFunction(functionName.getUnqualifiedName());
}
/**
* @param dataConsName name of the data constructor, assumed to belong to this module
* @return DataConstructor the data constructor or null if it is not in this module
*/
public DataConstructor getDataConstructor(String dataConsName) {
if (baseTypeInfo != null) {
DataConstructor dataCons = baseTypeInfo.getDataConstructor(dataConsName);
if (dataCons != null) {
return dataCons;
}
}
// Synchronize cache access to ensure thread-safety.
synchronized (cachedDataConstructorsMap) {
//first check for the data constructor in the cache
DataConstructor dataCons = cachedDataConstructorsMap.get(dataConsName);
if (dataCons != null) {
return dataCons;
}
//next, check within each type constructor, and add to the cache.
for (int i = 0, nTypeCons = getNTypeConstructors(); i < nTypeCons; ++i) {
TypeConstructor typeCons = getNthTypeConstructor(i);
dataCons = typeCons.getDataConstructor(dataConsName);
if (dataCons != null) {
cachedDataConstructorsMap.put(dataConsName, dataCons);
return dataCons;
}
}
}
return null;
}
/**
* @param dataConsName name of a data constructor visible from this module
* @return DataConstructor the data constructor or null if it does not exist or is not visible
*/
public DataConstructor getVisibleDataConstructor(QualifiedName dataConsName) {
if (baseTypeInfo != null) {
DataConstructor dataCons = baseTypeInfo.getVisibleDataConstructor(dataConsName);
if (dataCons != null) {
return dataCons;
}
}
ModuleName dataConsModuleName = dataConsName.getModuleName();
if(dataConsModuleName.equals(moduleName)) {
return getDataConstructor(dataConsName.getUnqualifiedName());
}
ModuleTypeInfo importedModuleTypeInfo = getImportedModule(dataConsModuleName);
if (importedModuleTypeInfo == null) {
return null;
}
DataConstructor dataCons = importedModuleTypeInfo.getDataConstructor(dataConsName.getUnqualifiedName());
if (dataCons != null) {
Scope scope = dataCons.getScope();
if (scope.isPublic() || (scope.isProtected() && importedModuleTypeInfo.hasFriendModule(this.moduleName))) {
return dataCons;
}
}
return null;
}
/**
* An internal helper to get a data constructor that is in a module
* imported into this module, either directly or indirectly and regardless
* of the scope of the given entity.
* This is for internal use by the compile when loading serialized module files.
*
* @param dataConstructorName
* @return DataConstructor
*/
DataConstructor getReachableDataConstructor(QualifiedName dataConstructorName) {
if (baseTypeInfo != null) {
DataConstructor dataCons = baseTypeInfo.getReachableDataConstructor(dataConstructorName);
if (dataCons != null) {
return dataCons;
}
}
ModuleName dataConstructorModuleName = dataConstructorName.getModuleName();
if(dataConstructorModuleName.equals(moduleName)) {
return getDataConstructor(dataConstructorName.getUnqualifiedName());
}
ModuleTypeInfo moduleTypeInfo = getDependeeModuleTypeInfo(dataConstructorModuleName);
if (moduleTypeInfo == null) {
return null;
}
return moduleTypeInfo.getDataConstructor(dataConstructorName.getUnqualifiedName());
}
/**
* Creation date: (6/7/01 9:20:30 AM)
* @param classMethodName class method name belonging to the current module
* @return ClassMethod the class method, or null if it does not exist.
*/
public ClassMethod getClassMethod(String classMethodName) {
if (baseTypeInfo != null) {
ClassMethod classMethod = baseTypeInfo.getClassMethod(classMethodName);
if (classMethod != null) {
return classMethod;
}
}
// Synchronize cache access to ensure thread-safety.
synchronized (cachedClassMethodsMap) {
//first check for the class method in the cache.
ClassMethod classMethod = cachedClassMethodsMap.get(classMethodName);
if (classMethod != null) {
return classMethod;
}
//next, check within each type class, and add to the cache.
for (int i = 0, nTypeClasses = getNTypeClasses(); i < nTypeClasses; ++i) {
TypeClass typeClass = getNthTypeClass(i);
classMethod = typeClass.getClassMethod(classMethodName);
if (classMethod != null) {
cachedClassMethodsMap.put(classMethodName, classMethod);
return classMethod;
}
}
}
return null;
}
/**
* Method getVisibleClassMethod.
* @param classMethodName name of a class method visible from this module
* @return ClassMethod the class method or null if it does not exist or is not visible
*/
public ClassMethod getVisibleClassMethod(QualifiedName classMethodName) {
if (baseTypeInfo != null) {
ClassMethod classMethod = baseTypeInfo.getVisibleClassMethod(classMethodName);
if (classMethod != null) {
return classMethod;
}
}
ModuleName classMethodModuleName = classMethodName.getModuleName();
if(classMethodModuleName.equals(moduleName)) {
return getClassMethod(classMethodName.getUnqualifiedName());
}
ModuleTypeInfo importedModuleTypeInfo = getImportedModule(classMethodModuleName);
if (importedModuleTypeInfo == null) {
return null;
}
ClassMethod classMethod = importedModuleTypeInfo.getClassMethod(classMethodName.getUnqualifiedName());
if (classMethod != null) {
Scope scope = classMethod.getScope();
if (scope.isPublic() || (scope.isProtected() && importedModuleTypeInfo.hasFriendModule(this.moduleName))) {
return classMethod;
}
}
return null;
}
/**
* An internal helper to get a class method that is in a module
* imported into this module, either directly or indirectly and regardless
* of the scope of the given entity.
* This is for internal use by the compile when loading serialized module files.
*
* @param classMethodName
* @return ClassMethod
*/
ClassMethod getReachableClassMethod(QualifiedName classMethodName) {
if (baseTypeInfo != null) {
ClassMethod classMethod = baseTypeInfo.getReachableClassMethod(classMethodName);
if (classMethod != null) {
return classMethod;
}
}
ModuleName classMethodModuleName = classMethodName.getModuleName();
if(classMethodModuleName.equals(moduleName)) {
return getClassMethod(classMethodName.getUnqualifiedName());
}
ModuleTypeInfo moduleTypeInfo = getDependeeModuleTypeInfo(classMethodModuleName);
if (moduleTypeInfo == null) {
return null;
}
return moduleTypeInfo.getClassMethod(classMethodName.getUnqualifiedName());
}
/**
* Method addClassInstance.
* @param classInstance
*/
void addClassInstance (ClassInstance classInstance) {
if (containsClassInstance(classInstance)) {
throw new IllegalArgumentException("The module " + moduleName + " already defines an instance that overlaps with " + classInstance.getNameWithContext() + ".");
}
classInstanceMap.put(classInstance.getIdentifier(), classInstance);
}
private boolean containsClassInstance (ClassInstance ci) {
return ((baseTypeInfo != null && baseTypeInfo.containsClassInstance(ci)) || classInstanceMap.containsKey(ci.getIdentifier()));
}
/**
* Method getClassInstance.
* @param classInstanceName
* @return ClassInstance
*/
public ClassInstance getClassInstance (ClassInstanceIdentifier classInstanceName) {
if (baseTypeInfo != null) {
ClassInstance ci = baseTypeInfo.getClassInstance(classInstanceName);
if (ci != null) {
return ci;
}
}
return classInstanceMap.get(classInstanceName);
}
private ClassInstance getVisibleClassInstance (Set<ModuleName> visitedModules, ClassInstanceIdentifier classInstanceIdentifier) {
if (!visitedModules.add(moduleName)) {
//If a module has been visited already, return immediately.
//This is needed to avoid inefficiencies when there is a deep module hierarchy with many diamond shaped
//import hierarchies. For example, Intellicut on the Everything project on the Ord a => a input of lessThan
//went from taking 16 seconds to 0.7 seconds!
return null;
}
ClassInstance classInstance = getClassInstance(classInstanceIdentifier);
if (classInstance != null) {
return classInstance;
}
for (int i = 0, nImports = getNImportedModules(); i < nImports; ++i) {
ModuleTypeInfo importedModule = getNthImportedModule(i);
classInstance = importedModule.getVisibleClassInstance(visitedModules, classInstanceIdentifier);
if (classInstance != null) {
return classInstance;
}
}
return null;
}
/**
* Method getVisibleClassInstance.
* @param classInstanceIdentifier
* @return ClassInstance
*/
public ClassInstance getVisibleClassInstance (ClassInstanceIdentifier classInstanceIdentifier) {
if (baseTypeInfo != null) {
ClassInstance ci = baseTypeInfo.getVisibleClassInstance(classInstanceIdentifier);
if (ci != null) {
return ci;
}
}
return getVisibleClassInstance(new HashSet<ModuleName>(), classInstanceIdentifier);
}
/**
* Returns the unique visible typeClass-typeCons instance for the given type class and type constructor if there is
* one, or null if none such exists.
* @param typeClass
* @param typeCons
* @return ClassInstance
*/
public ClassInstance getVisibleClassInstance (TypeClass typeClass, TypeConstructor typeCons) {
return getVisibleClassInstance(new ClassInstanceIdentifier.TypeConstructorInstance(typeClass.getName(), typeCons.getName()));
}
/**
* Method getNClassInstances.
* @return int the number of class instances defined within the module
*/
public int getNClassInstances() {
int nCI = 0;
if (baseTypeInfo != null) {
nCI += baseTypeInfo.getNClassInstances();
}
return nCI + classInstanceMap.size();
}
/**
* Method getNTopLevelClassInstances.
* @return int the number of class instances defined within the top level module
*/
int getNTopLevelClassInstances() {
return classInstanceMap.size();
}
/**
* Method getNthClassInstance.
* @param n zero based index
* @return ClassInstance the nth class instance defined within the module (defined via an instance declaration).
*/
public ClassInstance getNthClassInstance(int n) {
if (baseTypeInfo != null) {
if (n < baseTypeInfo.getNClassInstances()) {
return baseTypeInfo.getNthClassInstance(n);
} else {
n -= baseTypeInfo.getNClassInstances();
}
}
return classInstanceMap.getNthValue(n);
}
/**
* Method getNthTopLevelClassInstance.
* @param n zero based index
* @return ClassInstance the nth class instance defined within the top level module (defined via an instance declaration).
*/
ClassInstance getNthTopLevelClassInstance(int n) {
return classInstanceMap.getNthValue(n);
}
/**
* Insert the method's description here.
* Creation date: (6/18/01 10:33:31 AM)
* @return String
*/
@Override
public String toString() {
StringBuilder result = new StringBuilder();
if (isExtension()) {
result.append("extension module of ");
}
result.append("module ").append(getModuleName()).append('\n');
result.append("import ");
for (int i = 0, n = getNImportedModules(); i < n; ++i) {
result.append(getNthImportedModule(i).getModuleName()).append(' ');
}
result.append('\n');
result.append("friend ");
for (int i = 0, n = getNFriendModules(); i < n; ++i) {
result.append(getNthFriendModule(i)).append(' ');
}
result.append('\n');
result.append("--------------type constructors\n");
for (int i = 0, n = getNTypeConstructors(); i < n; ++i) {
result.append(getNthTypeConstructor(i).getName().getUnqualifiedName()).append('\n');
}
result.append("--------------type classes\n");
for (int i = 0, n = getNTypeClasses(); i < n; ++i) {
result.append(getNthTypeClass(i).getName().getUnqualifiedName()).append('\n');
}
result.append("--------------class instances\n");
for (int i = 0, n = getNClassInstances(); i < n; ++i) {
result.append(getNthClassInstance(i).getNameWithContext()).append('\n');
}
result.append("--------------functions\n");
for (int i = 0, n = getNFunctions(); i < n; ++i) {
result.append(getNthFunction(i)).append('\n');
}
result.append("--------------cached class methods\n");
synchronized (cachedClassMethodsMap) {
for (final String cachedClassMethod : cachedClassMethodsMap.keySet()) {
result.append(cachedClassMethodsMap.get(cachedClassMethod)).append('\n');
}
}
result.append("--------------cached data constructors\n");
synchronized (cachedDataConstructorsMap) {
for (final String cachedDataConstructor : cachedDataConstructorsMap.keySet()) {
result.append(cachedDataConstructorsMap.get(cachedDataConstructor)).append('\n');
}
}
result.append("--------------cached dependee module type info\n");
synchronized (cachedDependeeModuleTypeInfoMap) {
for (final ModuleName cachedDependeeModuleTypeInfo : cachedDependeeModuleTypeInfoMap.keySet()) {
result.append(cachedDependeeModuleTypeInfo).append('\n');
}
}
return result.toString();
}
/**
* Add the information in all the "import M using function = ..." clauses to this ModuleTypeInfo.
* Note: this should not be done for extension modules since extension modules cannot import
* other modules.
*
* @param usingFunctionOrClassMethodMap function or class method name to the imported module name in which it is defined.
*/
void addUsingFunctionOrClassMethodMap(Map<String, ModuleName> usingFunctionOrClassMethodMap) {
this.usingFunctionOrClassMethodMap.putAll(usingFunctionOrClassMethodMap);
}
/**
* @param usingFunctionOrClassMethodName name of a function or class method as declared in one of
* the "import module using function = functionOrClassMethodName" clauses.
* @return the corresponding import module name, or null if this is not a "using function"
*/
ModuleName getModuleOfUsingFunctionOrClassMethod(String usingFunctionOrClassMethodName) {
if (isExtension()) {
return baseTypeInfo.getModuleOfUsingFunctionOrClassMethod(usingFunctionOrClassMethodName);
}
return usingFunctionOrClassMethodMap.get(usingFunctionOrClassMethodName);
}
void addUsingDataConstructorMap(Map<String, ModuleName> usingDataConstructorMap) {
this.usingDataConstructorMap.putAll(usingDataConstructorMap);
}
ModuleName getModuleOfUsingDataConstructor(String usingDataConstructorName) {
if (isExtension()) {
return baseTypeInfo.getModuleOfUsingDataConstructor(usingDataConstructorName);
}
return usingDataConstructorMap.get(usingDataConstructorName);
}
void addUsingTypeConstructorMap(Map<String, ModuleName> usingTypeConstructorMap) {
this.usingTypeConstructorMap.putAll(usingTypeConstructorMap);
}
ModuleName getModuleOfUsingTypeConstructor(String usingTypeConstructorName) {
if (isExtension()) {
return baseTypeInfo.getModuleOfUsingTypeConstructor(usingTypeConstructorName);
}
return usingTypeConstructorMap.get(usingTypeConstructorName);
}
void addUsingTypeClassMap(Map<String, ModuleName> usingTypeClassMap) {
this.usingTypeClassMap.putAll(usingTypeClassMap);
}
ModuleName getModuleOfUsingTypeClass(String usingTypeClassName) {
if (isExtension()) {
return baseTypeInfo.getModuleOfUsingTypeClass(usingTypeClassName);
}
return usingTypeClassMap.get(usingTypeClassName);
}
/**
* @return the containing Module.
*/
public Module getModule() {
return module;
}
/**
* Write this ModuleTypeInfo instance to the RecordOutputStream.
* @param s
* @throws IOException
*/
public final void write (RecordOutputStream s) throws IOException {
if (baseTypeInfo != null) {
throw new IOException ("Saving adjunct module " + getModuleName() + ".");
}
s.startRecord(ModuleSerializationTags.MODULE_TYPE_INFO, serializationSchema);
// Module name.
s.writeModuleName(getModuleName());
// friend modules
s.writeInt(friendModulesSet.size());
for (final ModuleName friendModuleName : friendModulesSet) {
s.writeModuleName(friendModuleName);
}
// Imported modules.
s.writeInt(getNImportedModules());
for (int i = 0; i < getNImportedModules(); ++i) {
ModuleTypeInfo imti = getNthImportedModule(i);
s.writeModuleName(imti.getModuleName());
}
// All the using... map members are maps of String -> ModuleName.
// usingFunctionOrClassMethodMap
s.writeInt(usingFunctionOrClassMethodMap.size());
for (final Map.Entry<String, ModuleName> entry : usingFunctionOrClassMethodMap.entrySet()) {
String key = entry.getKey();
ModuleName value = entry.getValue();
s.writeUTF(key);
s.writeModuleName(value);
}
// usingDataConstructorMap
s.writeInt(usingDataConstructorMap.size());
for (final Map.Entry<String, ModuleName> entry : usingDataConstructorMap.entrySet()) {
String key = entry.getKey();
ModuleName value = entry.getValue();
s.writeUTF(key);
s.writeModuleName(value);
}
// usingTypeConstructorMap
s.writeInt(usingTypeConstructorMap.size());
for (final Map.Entry<String, ModuleName> entry : usingTypeConstructorMap.entrySet()) {
String key = entry.getKey();
ModuleName value = entry.getValue();
s.writeUTF(key);
s.writeModuleName(value);
}
// usingTypeClassMap
s.writeInt(usingTypeClassMap.size());
for (final Map.Entry<String, ModuleName> entry : usingTypeClassMap.entrySet()) {
String key = entry.getKey();
ModuleName value = entry.getValue();
s.writeUTF(key);
s.writeModuleName(value);
}
// typeConstructorMap
// String->TypeConstructor
// The String key is the unqualified name of the TypeConstructor so
// we just save the TypeConstructor.
s.writeInt(typeConstructorMap.size());
for (int i = 0; i < typeConstructorMap.size(); ++i) {
TypeConstructor typeCons = (TypeConstructor)typeConstructorMap.getNthValue(i);
typeCons.write(s);
}
// typeClassMap
// String->TypeClass
// The String key is the unqualified name of the TypeClass so we just save the TypeClass.
s.writeInt(typeClassMap.size());
for (int i = 0; i < typeClassMap.size(); ++i) {
TypeClass typeClass = (TypeClass)typeClassMap.getNthValue(i);
typeClass.write(s);
}
// classInstanceMap
// ClassInstanceIdentifier->ClassInstance
// The ClassInstanceIdentifier key is contained in the ClassInstance so we just save the ClassInstance.
s.writeInt(classInstanceMap.size());
for (int i = 0; i < classInstanceMap.size(); ++i) {
ClassInstance ci = classInstanceMap.getNthValue(i);
ci.write(s);
}
// functionMap
// String->Function
// The String key is the unqualified name of the Function so we just save the Function.
s.writeInt(functionMap.size());
for (int i = 0; i < functionMap.size(); ++i) {
Function function = (Function)functionMap.getNthValue(i);
function.write(s);
}
//ModuleSourceMetrics
moduleSourceMetrics.write(s);
// CALDoc comment
boolean hasCALDocComment = (calDocComment != null);
s.writeBoolean(hasCALDocComment);
if (hasCALDocComment) {
calDocComment.write(s);
}
// importedNameOccurrences
s.writeInt(importedNameOccurrences.size());
for (final QualifiedName qualifiedName : importedNameOccurrences) {
s.writeQualifiedName(qualifiedName);
}
s.endRecord();
}
/**
* Adds all the type constructors, type classes, functions, data types and class members that are
* public in imported modules or protected in imported modules that this module is a friend of. Not
* including adjuncts. This is used currently by auto complete.
*
* @return A list of the entities accessible in the current module.
*/
public List<ScopedEntity> getAccessibleEntitiesInScope(){
List<ScopedEntity> entities = new ArrayList<ScopedEntity>();
getAccessibleEntitiesForModule(entities, moduleName);
for(int i = 0; i < importedModuleMap.size(); ++i){
importedModuleMap.getNthValue(i).getAccessibleEntitiesForModule(entities, moduleName);
}
return entities;
}
/**
* Returns a list of entities from this module that are visible in the target module.
* @param targetModuleName the name of the module that the symbols will be used in
* @return a list of entities from the module that would be visible in the target module
*/
public List<ScopedEntity> getAccessibleEntitiesForModule(ModuleName targetModuleName){
List<ScopedEntity> entities = new ArrayList<ScopedEntity>();
getAccessibleEntitiesForModule(entities, targetModuleName);
return entities;
}
private void getAccessibleEntitiesForModule(List<ScopedEntity> entities, ModuleName currentModuleName){
addFromMap(typeConstructorMap, entities, currentModuleName);
addFromMap(typeClassMap, entities, currentModuleName);
addFromMap(functionMap, entities, currentModuleName);
}
private void addFromMap(ArrayMap<String, ScopedEntity> map, List<ScopedEntity> entities, ModuleName currentModuleName) {
final int size = map.size();
for (int i = 0; i < size; ++i) {
final ScopedEntity scopedEntity = map.getNthValue(i);
// I do not call isEntityVisible because that will return true for
// too many symbols. For example,
// it will return true for upFromToBoolean.
if (isAccessibleEntity(scopedEntity, currentModuleName)) {
entities.add(scopedEntity);
}
// Add class methods for typeClasses
if (scopedEntity instanceof TypeClass) {
final TypeClass typeClass = (TypeClass) scopedEntity;
for (int j = 0; j < typeClass.getNClassMethods(); ++j) {
ClassMethod classMethod = typeClass.getNthClassMethod(j);
if (isAccessibleEntity(classMethod, currentModuleName)) {
entities.add(classMethod);
}
}
}
// Add data constructors for typeConstructor
else if (scopedEntity instanceof TypeConstructor) {
TypeConstructor typeConstructor = (TypeConstructor) scopedEntity;
for (int j = 0; j < typeConstructor.getNDataConstructors(); ++j) {
final DataConstructor nthDataConstructor = typeConstructor.getNthDataConstructor(j);
if (isAccessibleEntity(nthDataConstructor, currentModuleName)) {
entities.add(nthDataConstructor);
}
}
}
}
}
private boolean isAccessibleEntity(final ScopedEntity scopedEntity, ModuleName currentModuleName) {
final Scope scope = scopedEntity.getScope();
final boolean isVisible = scope.isPublic() ||
(scope.isProtected() && hasFriendModule(currentModuleName));
return isVisible || scopedEntity.getName().getModuleName().equals(currentModuleName);
}
/**
* Check if a given scopedEntity is in the using clause for this module.
* @param scopedEntity The scopedEntity to check.
* @return True if the given scoped entity is in a using clause in this module.
*/
public boolean isUsingEntity(ScopedEntity scopedEntity){
final QualifiedName name = scopedEntity.getName();
final String unqualifiedName = name.getUnqualifiedName();
final ModuleName moduleName = name.getModuleName();
if (scopedEntity instanceof Function || scopedEntity instanceof ClassMethod){
return isInUsingMap(usingFunctionOrClassMethodMap, moduleName, unqualifiedName);
}
else if (scopedEntity instanceof DataConstructor){
return isInUsingMap(usingDataConstructorMap, moduleName, unqualifiedName);
}
else if (scopedEntity instanceof TypeConstructor){
return isInUsingMap(usingTypeConstructorMap, moduleName, unqualifiedName);
}
else if (scopedEntity instanceof TypeClass){
return isInUsingMap(usingTypeClassMap, moduleName, unqualifiedName);
}
else{
assert false; // new type of scoped entity so update this expression
return false;
}
}
private boolean isInUsingMap(Map<String, ModuleName> map, ModuleName expectedModuleName, String unqualifiedName){
final ModuleName moduleName = map.get(unqualifiedName);
if (moduleName != null){
if (moduleName.equals(expectedModuleName)){
return true;
}
}
return false;
}
/**
* Get a collection of the modules imported by the current module.
*
* @return Collection of modules imported by the current module. The set is unmodifiable.
*/
public Collection<ModuleName> getImportedModules(){
return importedModuleMap.keySet();
}
/**
* Read the content of this ModuleTypeInfo instance from the input stream.
* The read position of the stream is before the record header.
* @param s - the RecordInputStream containing the content.
* @param otherModules - Map of existing modules keyed by ModuleName, used to resolve imports, etc.
* @param msgLogger the logger to which to log deserialization messages.
* @throws IOException
*/
public void readContent (RecordInputStream s, Map<ModuleName, Module> otherModules, CompilerMessageLogger msgLogger) throws IOException {
// The first thing should be the record header for the type info.
RecordHeaderInfo rhi = s.findRecord(ModuleSerializationTags.MODULE_TYPE_INFO);
if (rhi == null) {
throw new IOException ("Unable to find record for ModuleTypeInfo for Module " + getModuleName() + ".");
}
DeserializationHelper.checkSerializationSchema(rhi.getSchema(), serializationSchema, getModuleName(), "ModuleTypeInfo", msgLogger);
try {
// Module name.
// No need to set, that was done when the ModuleTypeInfo instance was created.
s.readUTF();
// friend modules
int nFriendModules = s.readInt();
for (int i = 0; i < nFriendModules; ++i) {
ModuleName friendModuleName = s.readModuleName();
addFriendModule(friendModuleName);
}
// Imported modules.
int nImports = s.readInt();
for (int i = 0; i < nImports; ++i) {
ModuleName importName = s.readModuleName();
Module m = otherModules.get(importName);
if (m == null) {
throw new IOException ("Unable to resolve imported module " + importName + " in " + getModuleName());
}
addImportedModule(m.getModuleTypeInfo());
}
finishAddingImportedModules();
// All the using... map members are maps of String -> String.
// usingFunctionOrClassMethodMap
int n = s.readInt();
for (int i = 0; i < n; ++i) {
String key = s.readUTF();
ModuleName value = s.readModuleName();
usingFunctionOrClassMethodMap.put(key, value);
}
// usingDataConstructorMap
n = s.readInt();
for (int i = 0; i < n; ++i) {
String key = s.readUTF();
ModuleName value = s.readModuleName();
usingDataConstructorMap.put(key, value);
}
// usingTypeConstructorMap
n = s.readInt();
for (int i = 0; i < n; ++i) {
String key = s.readUTF();
ModuleName value = s.readModuleName();
usingTypeConstructorMap.put(key, value);
}
// usingTypeClassMap
n = s.readInt();
for (int i = 0; i < n; ++i) {
String key = s.readUTF();
ModuleName value = s.readModuleName();
usingTypeClassMap.put(key, value);
}
// typeConstructorMap
// String->TypeConstructor
// The String key is the unqualified name of the TypeConstructor so
// we only saved the TypeConstructor.
int nTypeConstructors = s.readInt();
RecordInputStream.Bookmark bookmark = s.bookmark();
// Because types can be mutually recursive we need to do an initial pass in which
// we create all the TypeConstructor objects and then do a second pass where we
// load the content of each.
for (int i = 0; i < nTypeConstructors; ++i) {
TypeConstructor typeCons = TypeConstructor.loadInit(s, this, msgLogger);
String uName = typeCons.getName().getUnqualifiedName();
// Built in TypeConstructor may already be in map if we are loading
// the Prelude module.
if (typeConstructorMap.get(uName) == null) {
addTypeConstructor(typeCons);
}
}
s.reposition(bookmark);
for (int i = 0; i < nTypeConstructors; ++i) {
TypeConstructor typeCons = (TypeConstructor)typeConstructorMap.getNthValue(i);
typeCons.loadFinal(s, this, msgLogger);
}
// typeClassMap
// String->TypeClass
// Because type classes can be mutually recursive we need to do an initial pass in which
// we create all the TypeClass objects and then do a second pass where we
// load the content of each.
int nTypeClasses = s.readInt();
bookmark = s.bookmark();
for (int i = 0; i < nTypeClasses; ++i) {
/*TypeClass typeClass =*/ TypeClass.loadInit(s, this, msgLogger);
}
s.reposition(bookmark);
for (int i = 0; i < nTypeClasses; ++i) {
TypeClass typeClass = (TypeClass)typeClassMap.getNthValue(i);
typeClass.loadFinal(s, this, msgLogger);
}
// classInstanceMap
// ClassInstanceIdentifier->ClassInstance
// The ClassInstanceIdentifier key is contained in the ClassInstance so we just save the ClassInstance.
int nClassInstances = s.readInt();
for (int i = 0; i < nClassInstances; ++i) {
ClassInstance classInstance = ClassInstance.load(s, this, msgLogger);
addClassInstance(classInstance);
}
// functionMap
// String->Function
// The String key is the unqualified name of the Function so we just save the Function.
int nFunctions = s.readInt();
for (int i = 0; i < nFunctions; ++i) {
Function function = Function.load(s, this, msgLogger);
if (function != null) {
functionMap.put(function.getName().getUnqualifiedName(), function);
}
}
// moduleSourceMetrics
moduleSourceMetrics = ModuleSourceMetrics.load(s, this, msgLogger);
// CALDoc comment
boolean hasCALDocComment = s.readBoolean();
if (hasCALDocComment) {
calDocComment = CALDocComment.load(s, getModuleName(), msgLogger);
}
// importedNameOccurrences
importedNameOccurrences = new HashSet<QualifiedName>();
int nOccurrences = s.readInt();
for (int i = 0; i < nOccurrences; i++) {
QualifiedName qualifiedName = s.readQualifiedName();
if(qualifiedName != null) {
importedNameOccurrences.add(qualifiedName);
}
}
s.skipRestOfRecord();
} catch (IOException e) {
throw new IOException ("Unable to load ModuleTypeInfo for Module " + getModuleName() + ": " + e.getLocalizedMessage());
}
}
}