/*
* 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.
*/
/*
* CALWorkspace.java
* Creation date: Apr 28, 2003.
* By: Edward Lam
*/
package org.openquark.cal.services;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.StringTokenizer;
import java.util.TreeSet;
import java.util.logging.Logger;
import org.openquark.cal.compiler.ClassInstance;
import org.openquark.cal.compiler.ClassInstanceIdentifier;
import org.openquark.cal.compiler.CompilerMessageLogger;
import org.openquark.cal.compiler.FunctionalAgent;
import org.openquark.cal.compiler.LanguageInfo;
import org.openquark.cal.compiler.ModuleContainer;
import org.openquark.cal.compiler.ModuleName;
import org.openquark.cal.compiler.ModuleSourceDefinition;
import org.openquark.cal.compiler.ModuleSourceDefinitionGroup;
import org.openquark.cal.compiler.ModuleTypeInfo;
import org.openquark.cal.compiler.QualifiedName;
import org.openquark.cal.compiler.ScopedEntity;
import org.openquark.cal.compiler.SourceMetrics;
import org.openquark.cal.compiler.SourceMetricsManager;
import org.openquark.cal.compiler.TypeClass;
import org.openquark.cal.compiler.TypeConstructor;
import org.openquark.cal.compiler.ProgramModifier.ProgramChangeListener;
import org.openquark.cal.machine.Module;
import org.openquark.cal.machine.ProgramResourceRepository;
import org.openquark.cal.metadata.CALFeatureMetadata;
import org.openquark.cal.metadata.MetadataManager;
import org.openquark.cal.metadata.ScopedEntityMetadata;
import org.openquark.cal.module.Cal.Core.CAL_Prelude;
import org.openquark.cal.runtime.ResourceAccess;
import org.openquark.cal.services.Vault.VaultProvider;
import org.openquark.cal.services.WorkspaceResource.SyncTime;
import org.openquark.util.FileSystemHelper;
import org.w3c.dom.Document;
/**
* A CALWorkspace represents a set of modules from which a program can be compiled.
* A metaprogram encapsulates a compiled program and all related metadata. It holds a bunch of metamodules.
* <p>
* A particular workspace within an environment can be located by its discrete workspace id.
* ie. a workspace instantiated with a particular id will point to the location of any previous workspace instantiated with that id.
* A special case is where the id is null, in which case the workspace is considered "nullary".
* This means that the workspace location coincides with the StandardVault.
*
* @author Edward Lam
*/
public abstract class CALWorkspace implements ResourceAccess, ProgramChangeListener{
/*
* TODOEL:
* 1) add a workspace listener to listen to changes in the workspace composition? (ie. add / remove modules).
* 2) revert a modification (add module, revert..)
*/
/*
* Logging
*/
/** The namespace for log messages from this package. */
public static final String SERVICES_LOGGER_NAMESPACE = CALWorkspace.class.getPackage().getName();
/** An instance of a Logger for messages from this package. */
static final Logger SERVICES_LOGGER = Logger.getLogger(SERVICES_LOGGER_NAMESPACE);
/*
* Members
*/
/** The registry which contains the registered vault providers. */
private final VaultRegistry vaultRegistry = new VaultRegistry();
/** Map from module name to module */
private final Map<ModuleName, MetaModule> nameToMetaModuleMap;
/** the list of metamodules in this program,
* in the order in which they were originally added. */
private final List<MetaModule> metaModuleList = new ArrayList<MetaModule>();
/**
* the first module name encountered when the workspace declaration file are loaded.
*/
private ModuleName firstModule;
/** The ModuleSourceDefinitionGroup for the current Workspace. */
private ModuleSourceDefinitionGroup sourceDefinitionGroup;
/** The resource path to the workspace description file. */
private final ResourcePath.FilePath workspaceDescriptionFile;
/** The virtual resource manager. */
private final VirtualResourceManager virtualResourceManager;
/** Map from module name to the vault info for that module. */
private final Map<ModuleName, VaultElementInfo> moduleNameToVaultInfoMap = new HashMap<ModuleName, VaultElementInfo>();
/** The revision info for the resources for modules in the workspace.
* Does not contain info for StandardVault modules if workspace is nullary. */
private final ResourceRevision.Info resourceRevisionInfo = new ResourceRevision.Info();
/** Sync time info for resources managed by this manager. */
private final SyncTimeInfo resourceSyncTimeInfo = new SyncTimeInfo();
/** The persistence manager for the workspace. */
private final WorkspacePersistenceManager persistenceManager;
/** The source metrics for the workspace */
private final SourceMetricsManager sourceMetrics;
/** The module container for this workspace */
private final ModuleContainer moduleContainer;
/**
* A simple container class to hold info about a managed resource.
* @author Edward Lam
*/
static class ResourceInfo {
private final ResourceRevision.Info resourceRevisionInfo;
private final SyncTimeInfo resourceSyncTimeInfo;
ResourceInfo(ResourceRevision.Info resourceRevisionInfo, SyncTimeInfo resourceSyncTimeInfo) {
this.resourceRevisionInfo = resourceRevisionInfo;
this.resourceSyncTimeInfo = resourceSyncTimeInfo;
}
/**
* @return the resource RevisionInfo.
*/
public ResourceRevision.Info getResourceRevisionInfo() {
return resourceRevisionInfo;
}
/**
* @return Returns the resourceSyncTimeInfo.
*/
public SyncTimeInfo getResourceSyncTimeInfo() {
return resourceSyncTimeInfo;
}
}
/**
* A class to track sync time for managed module resources.
* @author Edward Lam
*/
static class SyncTimeInfo {
/**
* The map which tracks resource sync times.
* Map from resource type to a map for that type.
* The map for the type maps ResourceName to SyncTime for that type.
*/
private final Map<String, Map<ResourceName,SyncTime>> resourceSyncTimeMap = new HashMap<String, Map<ResourceName,SyncTime>>();
/**
* Constructor for an empty SyncTimeInfo.
*/
public SyncTimeInfo() {
}
/**
* Constructor for a SyncTimeInfo.
* @param syncTimeList the associated SyncTimes
*/
public SyncTimeInfo(List<SyncTime> syncTimeList) {
updateResourceSyncTimeInfo(syncTimeList);
}
/**
* Update some sync time info in this object.
* @param resourceSyncTime an updated sync time.
*/
void updateResourceSyncTime(WorkspaceResource.SyncTime resourceSyncTime) {
String resourceType = resourceSyncTime.getResourceType();
ResourceName resourceName = resourceSyncTime.getIdentifier().getResourceName();
Map<ResourceName, SyncTime> resourceTypeMap = resourceSyncTimeMap.get(resourceType);
if (resourceTypeMap == null) {
resourceTypeMap = new HashMap<ResourceName, SyncTime>();
resourceSyncTimeMap.put(resourceType, resourceTypeMap);
}
resourceTypeMap.put(resourceName, resourceSyncTime);
}
/**
* Update some sync time info in this object.
* @param updatedInfo some updated sync time info.
*/
void updateResourceSyncTimeInfo(SyncTimeInfo updatedInfo) {
for (final String updatedFeatureType : updatedInfo.resourceSyncTimeMap.keySet()) {
Map<ResourceName,SyncTime> updatedFeatureNameToSyncTimeMap = updatedInfo.resourceSyncTimeMap.get(updatedFeatureType);
for (final WorkspaceResource.SyncTime updatedSyncTime : updatedFeatureNameToSyncTimeMap.values()) {
updateResourceSyncTime(updatedSyncTime);
}
}
}
/**
* Update some sync time info in this object.
* @param updatedInfoList the info with which to update.
*/
void updateResourceSyncTimeInfo(List<SyncTime> updatedInfoList) {
for (final SyncTime resourceSyncTime : updatedInfoList) {
updateResourceSyncTime(resourceSyncTime);
}
}
/**
* @return the resource types in this object.
*/
public String[] getResourceTypes() {
Set<String> resourceTypeSet = resourceSyncTimeMap.keySet();
String[] returnVal = new String[resourceTypeSet.size()];
return resourceTypeSet.toArray(returnVal);
}
/**
* Get the resource sync time for a given module resource.
* @param resourceIdentifier the identifier of the resource.
* @return the sync time of that resource, or 0L if that resource is not in this object.
*/
public long getResourceSyncTime(ResourceIdentifier resourceIdentifier) {
Map<ResourceName,SyncTime> resourceTypeMap = resourceSyncTimeMap.get(resourceIdentifier.getResourceType());
if (resourceTypeMap == null) {
return 0L;
}
WorkspaceResource.SyncTime resourceSyncTime = resourceTypeMap.get(resourceIdentifier.getResourceName());
if (resourceSyncTime == null) {
return 0L;
}
return resourceSyncTime.getSyncTime();
}
/**
* Get the resource sync times for a given resource type.
* @param resourceType the type for which the resource sync times should be returned.
* @return the resource sync times for that type.
*/
public Set<SyncTime> getResourceSyncTimes(String resourceType) {
Map<ResourceName,SyncTime> resourceTypeMap = resourceSyncTimeMap.get(resourceType);
if (resourceTypeMap == null) {
return null;
}
return new HashSet<SyncTime>(resourceTypeMap.values());
}
/**
* @param resourceIdentifier the identifier of the resource whose sync info should be removed.
* @return true if there was a sync info to remove.
*/
public boolean removeResourceSyncTime(ResourceIdentifier resourceIdentifier) {
Map<ResourceName,SyncTime> resourceTypeMap = resourceSyncTimeMap.get(resourceIdentifier.getResourceType());
if (resourceTypeMap != null) {
return resourceTypeMap.remove(resourceIdentifier.getResourceName()) != null;
}
return false;
}
/**
* @param moduleName the name of the module whose info to remove
*/
public void removeModule(ModuleName moduleName) {
// Iterate over the maps for the resource types.
for (final Map<ResourceName,SyncTime> resourceNameToSyncTimeMap : resourceSyncTimeMap.values()) {
// Iterate over the mappings in the map for that resource type.
for (Iterator<ResourceName> it = resourceNameToSyncTimeMap.keySet().iterator(); it.hasNext(); ) {
ResourceName resourceName = it.next();
CALFeatureName featureName = (CALFeatureName)resourceName.getFeatureName();
// Check that the feature name has a module name which is the same as the one we're looking for.
if (featureName.hasModuleName() && featureName.toModuleName().equals(moduleName)) {
it.remove();
}
}
}
}
/**
* Clear all sync time info from this object.
*/
void clear() {
resourceSyncTimeMap.clear();
}
}
/**
* Warning- this class should only be used by the CAL services implementation. It is not part of the
* external API of the CAL platform.
* <p>
* A class to represent the results of a sync operation.
* @author Edward Lam
*/
public static class SyncInfo {
/** Module resources which were updated. */
private final Set<ResourceIdentifier> updatedResourceIdentifierSet = new HashSet<ResourceIdentifier>();
/** Module resources which were not updated because the workspace resource was modified.
* Merge is required.*/
private final Set<ResourceIdentifier> conflictSet = new HashSet<ResourceIdentifier>();
/** Module resources which were not updated because the import operation failed. */
private final Set<ResourceIdentifier> importFailureSet = new HashSet<ResourceIdentifier>();
/** Module resources which were deleted . */
private final Set<ResourceIdentifier> deletedResourceSet = new HashSet<ResourceIdentifier>();
/**
* Constructor for a SyncInfo.
*/
public SyncInfo() {
}
/**
* Augment the sync info with other sync info.
* @param info the other sync info object with which this object will be updated.
*/
public void addInfo(SyncInfo info) {
updatedResourceIdentifierSet.addAll(info.updatedResourceIdentifierSet);
conflictSet.addAll(info.conflictSet);
importFailureSet.addAll(info.importFailureSet);
deletedResourceSet.addAll(info.deletedResourceSet);
}
void addUpdatedResource(ResourceIdentifier identifier) {
updatedResourceIdentifierSet.add(identifier);
}
void addSyncConflict(ResourceIdentifier identifier) {
conflictSet.add(identifier);
}
void addImportFailure(ResourceIdentifier identifier) {
importFailureSet.add(identifier);
}
void addDeletedResource(ResourceIdentifier identifier) {
deletedResourceSet.add(identifier);
}
/**
* @return Module resources which were updated.
*/
public Set<ResourceIdentifier> getUpdatedResourceIdentifiers() {
return new HashSet<ResourceIdentifier>(updatedResourceIdentifierSet);
}
/**
* @return Module resources which were not updated because the workspace resource was modified.
* Merge is required.
*/
public Set<ResourceIdentifier> getSyncConflictIdentifiers() {
return new HashSet<ResourceIdentifier>(conflictSet);
}
/**
* @return Module resources which were not updated because the import operation failed. */
public Set<ResourceIdentifier> getResourceImportFailures() {
return new HashSet<ResourceIdentifier>(importFailureSet);
}
/**
* @return Module resources which were deleted. */
public Set<ResourceIdentifier> getDeletedResourceIdentifiers() {
return new HashSet<ResourceIdentifier>(deletedResourceSet);
}
}
/**
* Constructor for a CALWorkspace
* @param workspaceDescriptionFile the file containing the workspace description.
* This will be used with the workspace is saved and loaded.
*/
protected CALWorkspace(ResourcePath.FilePath workspaceDescriptionFile) {
this.nameToMetaModuleMap = new HashMap<ModuleName, MetaModule>();
this.persistenceManager = new WorkspacePersistenceManager(this);
this.moduleContainer = new ModuleContainer () {
@Override
public int getNModules() {
return CALWorkspace.this.getNMetaModules();
}
@Override
public ModuleTypeInfo getNthModuleTypeInfo(int i) {
return CALWorkspace.this.getNthMetaModule(i).getModule().getModuleTypeInfo();
}
@Override
public ModuleTypeInfo getModuleTypeInfo(ModuleName moduleName) {
return CALWorkspace.this.getMetaModule(moduleName).getModule().getModuleTypeInfo();
}
@Override
public ModuleSourceDefinition getSourceDefinition(ModuleName moduleName) {
return CALWorkspace.this.getSourceDefinition(moduleName);
}
@Override
public boolean containsModule(ModuleName moduleName) {
return CALWorkspace.this.containsModule(moduleName);
}
@Override
public ResourceManager getResourceManager(ModuleName moduleName, String resourceType) {
return CALWorkspace.this.getResourceManager(moduleName, resourceType);
}
@Override
public boolean saveMetadata(CALFeatureMetadata metadata, Status saveStatus) {
return CALWorkspace.this.saveMetadata(metadata, saveStatus);
}
@Override
public boolean renameFeature(CALFeatureName oldFeatureName, CALFeatureName newFeatureName, Status renameStatus) {
return CALWorkspace.this.renameFeature(oldFeatureName, newFeatureName, renameStatus);
}
@Override
public ISourceManager getSourceManager(final ModuleName moduleName) {
return
new ISourceManager(){
private final CALSourceManager sourceManager = CALWorkspace.this.getSourceManager(moduleName);
public boolean isWriteable(ModuleName moduleName) {
return sourceManager.isWriteable(moduleName);
}
public String getSource(ModuleName moduleName, CompilerMessageLogger messageLogger){
ModuleSourceDefinition moduleSourceDefinition = sourceManager.getSource(moduleName, new Status("getting module source"));
return moduleSourceDefinition.getSourceText(new Status("reading source for refactoring"), messageLogger);
}
public boolean saveSource(ModuleName moduleName, String moduleDefinition, Status saveStatus) {
return sourceManager.saveSource(moduleName, moduleDefinition, saveStatus);
}
};
}
};
this.sourceMetrics = new SourceMetricsManager(moduleContainer);
this.workspaceDescriptionFile = workspaceDescriptionFile;
this.virtualResourceManager = new VirtualResourceManager();
}
/**
* @return the workspace id. Null for a nullary workspace.
*/
public abstract String getWorkspaceID();
/**
* @return a readable string identifying the location of the workspace.
*/
public abstract String getWorkspaceLocationString();
/**
* @return whether this is the "nullary" workspace.
* What this means is that the workspace points to the same location as the StandardVault.
*/
private boolean isNullary() {
return getWorkspaceID() == null;
}
public ModuleContainer asModuleContainer(){
return moduleContainer;
}
/**
* Get the ModuleSourceDefinitionGroup for the current workspace.
* @return ModuleSourceDefinitionGroup the source definitions for this workspace.
*/
synchronized final ModuleSourceDefinitionGroup getSourceDefinitionGroup() {
return sourceDefinitionGroup;
}
/**
* Returns a ProgramResourceRepository.Provider that provides Car-aware ProgramResourceRepository instances.
*
* @param baseProvider the base repository for use by the Car-aware ProgramResourceRepository when
* a module name is not found in the Car mapping.
*
* @return the provider for Car-aware ProgramResourceRepository instances.
*/
ProgramResourceRepository.Provider getProviderForCarAwareProgramResourceRepository(ProgramResourceRepository.Provider baseProvider) {
return virtualResourceManager.getProviderForCarAwareProgramResourceRepository(baseProvider);
}
/**
* Add a module to this program.
* @param module the module to add.
*/
synchronized private final void addMetaModule(MetaModule module) {
Assert.isNotNullArgument(module, "module");
nameToMetaModuleMap.put(module.getName(), module);
metaModuleList.add(module);
}
/**
* Remove a named module from this program.
* @param moduleName the name of the module to remove
*/
synchronized private final MetaModule removeMetaModule(ModuleName moduleName) {
MetaModule module = nameToMetaModuleMap.remove(moduleName);
metaModuleList.remove(module);
return module;
}
/**
* Gets the module with a given moduleName.
* @param moduleName the module name
* @return the module with a given module name
*/
synchronized public final MetaModule getMetaModule(ModuleName moduleName) {
// Get the module object with the given the module name
return nameToMetaModuleMap.get(moduleName);
}
/**
* Get the number of modules in this program
* @return int the number of modules held by this program.
*/
synchronized public final int getNMetaModules() {
return metaModuleList.size();
}
/**
* Get the nth metamodule.
* The index n is based on the order in which modules are originally added to the program.
* @param n 0-based index of the module to return.
* @return MetaModule the module.
*/
synchronized public final MetaModule getNthMetaModule(int n) {
return metaModuleList.get(n);
}
/**
* Get an entity by name.
* @param entityName the name of the entity to retrieve.
* @return the corresponding entity, or null if the entity or module do not exist.
*/
public GemEntity getGemEntity(QualifiedName entityName) {
Assert.isNotNullArgument(entityName, "entityName");
MetaModule module = getMetaModule(entityName.getModuleName());
if (module == null) {
return null;
}
return module.getGemEntity(entityName.getUnqualifiedName());
}
/**
* Get a scoped entity by name.
* @param featureName the name to get a scoped entity for
* @return the scoped entity for the name or null if no such entity
* @throws IllegalArgumentException if the feature name is not for a scoped entity
*/
public ScopedEntity getScopedEntity(CALFeatureName featureName) {
QualifiedName name = featureName.toQualifiedName();
MetaModule metaModule = getMetaModule(name.getModuleName());
if (metaModule == null) {
return null;
}
ModuleTypeInfo moduleInfo = metaModule.getTypeInfo();
CALFeatureName.FeatureType type = featureName.getType();
if (type == CALFeatureName.FUNCTION) {
return moduleInfo.getFunctionalAgent(name.getUnqualifiedName());
} else if (type == CALFeatureName.TYPE_CONSTRUCTOR) {
return moduleInfo.getTypeConstructor(name.getUnqualifiedName());
} else if (type == CALFeatureName.TYPE_CLASS) {
return moduleInfo.getTypeClass(name.getUnqualifiedName());
} else if (type == CALFeatureName.DATA_CONSTRUCTOR) {
return moduleInfo.getDataConstructor(name.getUnqualifiedName());
} else if (type == CALFeatureName.CLASS_METHOD) {
return moduleInfo.getClassMethod(name.getUnqualifiedName());
}
throw new IllegalArgumentException("feature type is not a for a scoped entity: " + type);
}
/**
* Get a class instance by name.
* @param featureName the name to get a class instance for
* @return the class instance for the name or null if the name does not represent a class instance, or if there is no such instance
* @throws IllegalArgumentException if the feature name is not for a class instance
*/
public ClassInstance getClassInstance(CALFeatureName featureName) {
ModuleName moduleName = featureName.toModuleName();
ClassInstanceIdentifier identifier = featureName.toInstanceIdentifier();
MetaModule metaModule = getMetaModule(moduleName);
if (metaModule == null) {
return null;
}
return metaModule.getTypeInfo().getClassInstance(identifier);
}
/**
* Saves the given source definition to the manager.
* @param sourceDefinition the source definition to save.
* @param saveStatus the tracking status object.
* @return whether the source was successfully saved.
*/
private boolean saveSource(ModuleSourceDefinition sourceDefinition, Status saveStatus) {
CALSourceManager sourceManager = virtualResourceManager.getSourceManager(sourceDefinition.getModuleName());
if (sourceManager == null) {
saveStatus.add(new Status(Status.Severity.WARNING, "No registered source manager."));
return false;
}
return sourceManager.importResource(sourceDefinition, saveStatus) != null;
}
/**
* @param moduleName the name of the module whose resource manager is to be returned.
* @param resourceType the resource type identifier.
* @return the manager for that resource type and for the given module in the workspace.
*/
public ResourceManager getResourceManager(ModuleName moduleName, String resourceType) {
return virtualResourceManager.getModuleSpecificResourceManager(moduleName, resourceType);
}
/**
* @return the registered workspace manager, if any.
*/
public WorkspaceDeclarationManager getWorkspaceDeclarationManager() {
return virtualResourceManager.getWorkspaceDeclarationManager();
}
/**
* Get the metadata for a scoped entity.
* @param entity the entity to get metadata for
* @param locale the locale associated with the metadata.
* @return the metadata for the entity. If the entity has no metadata, then default metadata is returned.
*/
public ScopedEntityMetadata getMetadata(ScopedEntity entity, Locale locale) {
if (entity instanceof FunctionalAgent) {
GemEntity gemEntity = getGemEntity(entity.getName());
if (gemEntity == null) {
throw new IllegalStateException("The given entity does not exist in the workspace.");
}
return gemEntity.getMetadata(locale);
} else if (entity instanceof TypeClass) {
return (ScopedEntityMetadata)getMetadata(CALFeatureName.getTypeClassFeatureName(entity.getName()), locale);
} else if (entity instanceof TypeConstructor) {
return (ScopedEntityMetadata)getMetadata(CALFeatureName.getTypeConstructorFeatureName(entity.getName()), locale);
} else {
throw new UnsupportedOperationException("Unknown entity class: " + entity.getClass());
}
}
/**
* @param featureName the name of the feature to get metadata for. This feature must live in a module in this workspace.
* @param locale the locale associated with the metadata.
* @return the metadata for the feature. If the feature has no metadata, then default
* metadata is returned.
*/
public CALFeatureMetadata getMetadata(CALFeatureName featureName, Locale locale) {
MetaModule metaModule = getMetaModule(featureName.toModuleName());
if (metaModule == null) {
throw new IllegalStateException("The module for the given entity does not exist in the workspace.");
}
MetadataManager metadataManager = virtualResourceManager.getMetadataManager(featureName.toModuleName());
if (metadataManager == null) {
return MetadataManager.getEmptyMetadata(featureName, locale);
}
return metadataManager.getMetadata(featureName, locale);
}
/**
* {@inheritDoc}
*/
public InputStream getUserResource(String moduleNameAsString, String name, String extension, Locale locale) {
ModuleName moduleName = ModuleName.make(moduleNameAsString);
UserResourceManager userResourceManager = virtualResourceManager.getUserResourceManager(moduleName);
return userResourceManager.getUserResource(moduleName, name, extension, locale);
}
/**
* Returns the resource manager for user resources of the specified module.
* @param moduleName the module name.
* @return the resource manager for user resources of the specified module.
*/
UserResourceManager getUserResourceManager(final ModuleName moduleName) {
return virtualResourceManager.getUserResourceManager(moduleName);
}
/**
* Used for errors that occur during the renameFeature() operation.
* @author Peter Cardwell
*/
private static final class RenamingException extends Exception {
private static final long serialVersionUID = 3985993651784449695L;
RenamingException (String errorMsg) {
super(errorMsg);
}
String getErrorMessage() {
return getMessage();
}
}
/**
* A helper method to collect a list of design resources that should be renamed.
* @param renamings
* @param oldFeatureName
* @param newFeatureName
* @param renameStatus
*/
private void collectDesignRenamings(List<Renaming> renamings, CALFeatureName oldFeatureName, CALFeatureName newFeatureName, Status renameStatus) {
GemDesignManager oldDesignManager = virtualResourceManager.getDesignManager(oldFeatureName.toModuleName());
if (oldDesignManager == null) {
renameStatus.add(new Status(Status.Severity.WARNING, "The design manager for the old design does not exist."));
return;
}
GemDesignManager newDesignManager = virtualResourceManager.getDesignManager(newFeatureName.toModuleName());
if (newDesignManager == null) {
renameStatus.add(new Status(Status.Severity.WARNING, "The design manager for the new design does not exist."));
return;
}
ResourceName oldResourceName = new ResourceName(oldFeatureName);
if (oldFeatureName.getType().equals(CALFeatureName.MODULE)) {
MetaModule metaModule = getMetaModule(oldFeatureName.toModuleName());
if (metaModule == null) {
return; //throw new IllegalStateException("The module for the given entity does not exist in the workspace.");
}
for (int i = 0, n = metaModule.getNGemEntities(); i < n; i++) {
GemEntity nextGemEntity = metaModule.getNthGemEntity(i);
if (oldDesignManager.hasGemDesign(nextGemEntity)) {
QualifiedName nextEntityName = nextGemEntity.getName();
if (!nextEntityName.getModuleName().equals(oldFeatureName.toModuleName())) {
throw new IllegalStateException("Feature name associated with, but not contained in, the renamed module.");
}
QualifiedName nextEntityNameRenamed = QualifiedName.make(newFeatureName.toModuleName(), nextEntityName.getUnqualifiedName());
CALFeatureName nextFeatureName = CALFeatureName.getFunctionFeatureName(nextEntityName);
CALFeatureName nextFeatureNameRenamed = CALFeatureName.getFunctionFeatureName(nextEntityNameRenamed);
renamings.add(new Renaming(new ResourceName(nextFeatureName), new ResourceName(nextFeatureNameRenamed), "design", oldDesignManager, newDesignManager));
}
}
} else if (oldFeatureName.getType() == CALFeatureName.FUNCTION && oldDesignManager.getResourceStore().hasFeature(oldResourceName)) {
renamings.add(new Renaming(oldResourceName, new ResourceName(newFeatureName), "design", oldDesignManager, newDesignManager));
}
}
/**
* A helper method to collect a list of user resources that should be renamed.
* @param renamings
* @param oldFeatureName
* @param newFeatureName
* @param renameStatus
*/
private void collectUserResourceRenamings(List<Renaming> renamings, CALFeatureName oldFeatureName, CALFeatureName newFeatureName, Status renameStatus) {
ModuleName oldFeatureModuleName = oldFeatureName.toModuleName();
UserResourceManager oldUserResourceManager = virtualResourceManager.getUserResourceManager(oldFeatureModuleName);
if (oldUserResourceManager == null) {
renameStatus.add(new Status(Status.Severity.WARNING, "The user resource manager for the old feature's module does not exist."));
return;
}
ModuleName newFeatureModuleName = newFeatureName.toModuleName();
UserResourceManager newUserResourceManager = virtualResourceManager.getUserResourceManager(newFeatureModuleName);
if (newUserResourceManager == null) {
renameStatus.add(new Status(Status.Severity.WARNING, "The user resource manager for the new feature's module does not exist."));
return;
}
if (oldFeatureName.getType().equals(CALFeatureName.MODULE)) {
List<ResourceName> oldUserResourceNames = ((UserResourceStore)oldUserResourceManager.getResourceStore()).getModuleResourceNameList(oldFeatureModuleName);
for (int i = 0, n = oldUserResourceNames.size(); i < n; i++) {
ResourceName oldUserResourceName = oldUserResourceNames.get(i);
Locale locale = LocalizedResourceName.localeOf(oldUserResourceName);
UserResourceFeatureName oldUserResourceFeatureName = (UserResourceFeatureName)oldUserResourceName.getFeatureName();
UserResourceFeatureName newUserResourceFeatureName = new UserResourceFeatureName(newFeatureModuleName, oldUserResourceFeatureName.getName(), oldUserResourceFeatureName.getExtension());
ResourceName newUserResourceName = new LocalizedResourceName(newUserResourceFeatureName, locale);
renamings.add(new Renaming(oldUserResourceName, newUserResourceName, "userResource", oldUserResourceManager, newUserResourceManager));
}
}
}
/**
* A helper method to collect a list of metadata resources that should be renamed.
* @param renamings
* @param oldFeatureName
* @param newFeatureName
* @param renameStatus
*/
private void collectMetadataRenamings(List<Renaming> renamings, CALFeatureName oldFeatureName, CALFeatureName newFeatureName, Status renameStatus) {
MetadataManager oldMetadataManager = virtualResourceManager.getMetadataManager(oldFeatureName.toModuleName());
if (oldMetadataManager == null) {
renameStatus.add(new Status(Status.Severity.WARNING, "The metadata manager for the old metadata resource does not exist."));
return;
}
MetadataManager newMetadataManager = virtualResourceManager.getMetadataManager(newFeatureName.toModuleName());
if (newMetadataManager == null) {
renameStatus.add(new Status(Status.Severity.WARNING, "The metadata manager for the new metadata resource does not exist."));
return;
}
// If this is a module, we need to update the metadata for every entity contained by it.
if (oldFeatureName.getType().equals(CALFeatureName.MODULE)) {
MetaModule metaModule = getMetaModule(oldFeatureName.toModuleName());
if (metaModule == null) {
return;
}
Set<CALFeatureName> featureNames = metaModule.getFeatureNames();
for (final CALFeatureName nextFeatureName : featureNames) {
List<ResourceName> metadataResourceNames = metaModule.getMetadataResourceNamesForAllLocales(nextFeatureName);
if (!metadataResourceNames.isEmpty()) {
// Calculate the new name for this feature
CALFeatureName nextFeatureNameRenamed;
if (nextFeatureName.getType().equals(CALFeatureName.MODULE)) {
nextFeatureNameRenamed = newFeatureName;
} else if (nextFeatureName.getType().equals(CALFeatureName.CLASS_INSTANCE)) {
throw new IllegalStateException("Class instances shouldn't have metadata associated with them.");
} else {
QualifiedName qualifiedNextFeatureName = QualifiedName.makeFromCompoundName(nextFeatureName.getName());
if (!qualifiedNextFeatureName.getModuleName().equals(oldFeatureName.toModuleName())) {
throw new IllegalStateException("Feature name associated with, but not contained in, the renamed module.");
}
QualifiedName qualifiedNextFeatureNameRenamed = QualifiedName.make(newFeatureName.toModuleName(), qualifiedNextFeatureName.getUnqualifiedName());
nextFeatureNameRenamed = new CALFeatureName(nextFeatureName.getType(), qualifiedNextFeatureNameRenamed.getQualifiedName());
}
for (int i = 0, n = metadataResourceNames.size(); i < n; i++) {
ResourceName metadataResourceName = metadataResourceNames.get(i);
Locale locale = LocalizedResourceName.localeOf(metadataResourceName);
renamings.add(new Renaming(new LocalizedResourceName(nextFeatureName, locale), new LocalizedResourceName(nextFeatureNameRenamed, locale), "metadata", oldMetadataManager, newMetadataManager));
}
}
}
} else {
if (oldFeatureName.hasModuleName()) {
MetaModule metaModule = getMetaModule(oldFeatureName.toModuleName());
if (metaModule == null) {
return;
}
List<ResourceName> metadataResourceNames = metaModule.getMetadataResourceNamesForAllLocales(oldFeatureName);
for (int i = 0, n = metadataResourceNames.size(); i < n; i++) {
ResourceName metadataResourceName = metadataResourceNames.get(i);
Locale locale = LocalizedResourceName.localeOf(metadataResourceName);
renamings.add(new Renaming(new LocalizedResourceName(oldFeatureName, locale), new LocalizedResourceName(newFeatureName, locale), "metadata", oldMetadataManager, newMetadataManager));
}
}
}
}
/**
* A helper class representing a renaming operation. It encapsulates all the information that is needed by the workspace
* to actually perform the renaming.
* @author Peter Cardwell
*/
private class Renaming {
final ResourceName fromName;
final ResourceName toName;
final String renamingType;
final ResourceManager fromResourceManager;
final ResourceManager toResourceManager;
Renaming(ResourceName fromName, ResourceName toName, String renamingType, ResourceManager fromResourceManager, ResourceManager toResourceManager) {
this.fromName = fromName;
this.toName = toName;
this.renamingType = renamingType;
this.fromResourceManager = fromResourceManager;
this.toResourceManager = toResourceManager;
}
}
/**
* Rename a module in the workspace. This will only change the name of any related design,
* metadata, and module resources and update any internal references to it in the workspace.
* It will NOT update references to the module within any resources themselves
* (ie. The contents of CAL sources, designs, metadata, etc. will not be updated).
* @param oldFeatureName The name of the module to rename
* @param newFeatureName The new name to rename the module to
* @param renameStatus the tracking status object.
* @return boolean whether the module was successfully renamed.
*/
synchronized public final boolean renameFeature(CALFeatureName oldFeatureName, CALFeatureName newFeatureName, Status renameStatus) {
if (!oldFeatureName.getType().equals(newFeatureName.getType())) {
throw new IllegalArgumentException("oldFeatureName type is not the same as newFeatureName type");
}
// Collect a list of renamings to perform.
List<Renaming> renamings = new ArrayList<Renaming>();
collectMetadataRenamings(renamings, oldFeatureName, newFeatureName, renameStatus);
collectDesignRenamings(renamings, oldFeatureName, newFeatureName, renameStatus);
collectUserResourceRenamings(renamings, oldFeatureName, newFeatureName, renameStatus);
if (oldFeatureName.getType().equals(CALFeatureName.MODULE)) {
renamings.add(new Renaming(
new ResourceName(oldFeatureName), new ResourceName(newFeatureName), "CAL source",
virtualResourceManager.getSourceManager(oldFeatureName.toModuleName()),
virtualResourceManager.getSourceManager(newFeatureName.toModuleName())));
}
List<Renaming> undoList = new ArrayList<Renaming>();
for (final Renaming renaming : renamings) {
try {
if (!renaming.fromResourceManager.getResourceStore().isWriteable(renaming.fromName)) {
throw new RenamingException("The "+ renaming.renamingType + " resource for " + renaming.fromName.getFeatureName().getName() + " is not writeable.");
}
if (!renaming.fromResourceManager.getResourceStore().renameResource(renaming.fromName, renaming.toName, renaming.toResourceManager.getResourceStore(), new Status("Resource renaming"))) {
throw new RenamingException("Error renaming the " + renaming.renamingType + " resource for " + renaming.fromName.getFeatureName().getName() + ".");
}
undoList.add(renaming);
} catch (RenamingException e) {
renameStatus.add(new Status(Status.Severity.ERROR, e.getErrorMessage()));
// Renaming failed, undo all renamings done so far and return
for (int i = undoList.size() - 1; i >= 0; i--) {
Renaming undoRenaming = undoList.get(i);
if (!undoRenaming.toResourceManager.getResourceStore().renameResource(undoRenaming.toName, undoRenaming.fromName, undoRenaming.fromResourceManager.getResourceStore(), new Status("Undo rename status"))) {
String msg = "FATAL: Error undoing the renaming of the " + undoRenaming.renamingType + " resource for " + undoRenaming.fromName + ".";
renameStatus.add(new Status(Status.Severity.ERROR, msg));
}
}
return false;
}
}
// If the entity being renamed is a module, we need to rename the cal source itself.
if (oldFeatureName.getType().equals(CALFeatureName.MODULE)) {
ModuleSourceDefinition sourceToRename = sourceDefinitionGroup.getModuleSource(oldFeatureName.toModuleName());
ModuleSourceDefinition renamedSource = virtualResourceManager.getSourceManager(newFeatureName.toModuleName()).getSource(newFeatureName.toModuleName(), renameStatus);
// Now remove the reference to the old module from the source definition group and add the new module
List<ModuleSourceDefinition> newModuleSources = new ArrayList<ModuleSourceDefinition>(Arrays.asList(sourceDefinitionGroup.getModuleSources()));
newModuleSources.remove(sourceToRename);
newModuleSources.add(renamedSource);
ModuleSourceDefinition[] newModuleSourceArray = newModuleSources.toArray(ModuleSourceDefinition.EMPTY_ARRAY);
this.sourceDefinitionGroup = new ModuleSourceDefinitionGroup(newModuleSourceArray);
// Update the vault info map..
VaultElementInfo newVaultInfo = VaultElementInfo.makeBasic("NonExistent", newFeatureName.getName(), null, 0);
updateVaultInfo(newFeatureName.toModuleName(), newVaultInfo);
ModuleName oldFeatureNameAsModuleName = oldFeatureName.toModuleName();
moduleNameToVaultInfoMap.remove(oldFeatureNameAsModuleName);
resourceRevisionInfo.removeModule(oldFeatureNameAsModuleName);
resourceSyncTimeInfo.removeModule(oldFeatureNameAsModuleName);
// Finally, remove the old module from the meta module list.
removeMetaModule(oldFeatureNameAsModuleName);
}
return true;
}
/**
* Saves the given metadata object to the workspace.
* @param metadata the metadata object to save
* @return true if metadata was saved, false otherwise
*/
public boolean saveMetadata(CALFeatureMetadata metadata) {
return saveMetadata(metadata, new Status("Save status"));
}
/**
* Saves the given metadata object to the workspace.
* @param metadata the metadata object to save
* @param saveStatus the tracking status object.
* @return true if metadata was saved, false otherwise
*/
public boolean saveMetadata(CALFeatureMetadata metadata, Status saveStatus) {
MetadataManager metadataManager = virtualResourceManager.getMetadataManager(metadata.getFeatureName().toModuleName());
if (metadataManager == null) {
saveStatus.add(new Status(Status.Severity.WARNING, "No registered metadata manager."));
return false;
}
return metadataManager.saveMetadata(metadata, saveStatus);
}
/**
* Save a design to the workspace.
* @param design the design to save.
* @param saveStatus the tracking status object
*/
public void saveDesign(GemDesign design, Status saveStatus) {
GemDesignManager designManager = virtualResourceManager.getDesignManager(design.getDesignName().getModuleName());
if (designManager == null) {
saveStatus.add(new Status(Status.Severity.WARNING, "No registered design manager."));
return;
}
designManager.saveGemDesign(design, saveStatus);
}
/**
* @param typeConsName the name of the type constructor.
* @return the type constructor entity with the given name.
*/
public TypeConstructor getTypeConstructor(QualifiedName typeConsName) {
MetaModule metaModule = getMetaModule(typeConsName.getModuleName());
if (metaModule == null) {
return null;
}
return metaModule.getTypeInfo().getTypeConstructor(typeConsName.getUnqualifiedName());
}
/**
* {@inheritDoc}
*/
public void moduleLoaded (Module module) {
addMetaModule (new MetaModule (module, virtualResourceManager));
sourceMetrics.addModuleMetrics(module.getModuleTypeInfo());
}
/**
* {@inheritDoc}
*/
public void moduleRemoved (Module module) {
removeMetaModule (module.getName ());
sourceMetrics.removeModuleMetrics(module.getModuleTypeInfo());
}
/**
* returns the name of the first module specified in the workspace definition.
* this may return null if the workspace is not initialized, or there are no
* modules.
* @return the first module in the workspace, or null if the workspace is not
* properly initialized
*/
synchronized public final ModuleName getFirstModuleName() {
return firstModule;
}
/**
* Initialize the workspace from the given workspace definition.
* Any current workspace definition will be replaced with the new one.
* @param storedModules the modules which comprise the initial workspace definition.
*/
synchronized public final Status initializeWorkspace(StoredVaultElement.Module[] storedModules, Status initStatus) {
// Clear existing modules from the resource manager..
removeAllStoredModules(initStatus);
// record the name of the first module in the stored module list
if (storedModules != null && storedModules.length > 0) {
firstModule = ModuleName.make(storedModules[0].getName());
} else {
firstModule = null;
}
// Reset the source definition group.
this.sourceDefinitionGroup = new ModuleSourceDefinitionGroup(ModuleSourceDefinition.EMPTY_ARRAY);
// Add new modules..
// Import the stored module resources.
List<StoredVaultElement.Module> storedModulesList = new ArrayList<StoredVaultElement.Module>(Arrays.asList(storedModules));
for (Iterator<StoredVaultElement.Module> it = storedModulesList.iterator(); it.hasNext(); ) {
StoredVaultElement.Module storedModule = it.next();
if (isNullary() && !isTopLevelStoredModuleElementInStandardVault(storedModule) && !isCarBasedStoredModuleElementInStandardVault(storedModule)) {
String warningString = "Module " + storedModule.getName() +
" was not imported. Only Modules from the StandardVault or from a Car file may be used to initialize a nullary workspace.";
initStatus.add(new Status(Status.Severity.WARNING, warningString));
it.remove();
continue;
}
importStoredModule(storedModule, initStatus);
}
// Create the module sources, and the source provider.
ModuleSourceDefinition[] sourceDefinitions = new ModuleSourceDefinition[storedModulesList.size()];
int index = 0;
for (final StoredVaultElement.Module storedModule : storedModulesList) {
ModuleName moduleName = ModuleName.make(storedModule.getName());
CALSourceManager sourceManager = virtualResourceManager.getSourceManager(moduleName);
if (sourceManager == null) {
initStatus.add(new Status(Status.Severity.WARNING, "No registered source manager."));
return initStatus;
}
sourceDefinitions[index] = sourceManager.getSource(moduleName, initStatus);
index++;
}
this.sourceDefinitionGroup = new ModuleSourceDefinitionGroup(sourceDefinitions);
// Persist the workspace description.
if (!isNullary() && sourceDefinitionGroup.getNModules() > 0) {
saveWorkspaceDescription(initStatus);
}
// Return the import status.
return initStatus;
}
/**
* Load the persisted workspace, if any.
*
* @param loadStatus the tracking status object.
* If a previously-persisted workspace exists but could not be successfully loaded, there will be errors.
* Otherwise, there may be info or warnings.
* @return true if there was a previously-persisted workspace, and it was successfully loaded.
*/
public boolean loadPersistedWorkspace(Status loadStatus) {
// Do not load persisted workspace if nullary.
if (isNullary()) {
return false;
}
// workspace description..
boolean workspaceLoaded = loadWorkspaceDescription(loadStatus);
// source definition group..
calculateSourceDefinitionGroup();
return workspaceLoaded;
}
/**
* Calculate the source definition group managed by the workspace.
* The sourceDefinitionGroup member will be set based on the module names in the moduleNameToVaultInfo map.
*/
synchronized private final void calculateSourceDefinitionGroup() {
// Get the names of the modules for which resources currently managed by this manager.
Set<ModuleName> moduleNameSet = moduleNameToVaultInfoMap.keySet();
List<ModuleSourceDefinition> sourceDefinitionList = new ArrayList<ModuleSourceDefinition>();
// Add the source definitions for the given module names.
for (final ModuleName moduleName : moduleNameSet) {
CALSourceManager sourceManager = virtualResourceManager.getSourceManager(moduleName);
if (sourceManager != null) {
ModuleSourceDefinition sourceDefinition = sourceManager.getSource(moduleName, new Status("Source Status"));
if (sourceDefinition != null) {
sourceDefinitionList.add(sourceDefinition);
}
}
}
// Convert the list of source definitions to an array.
ModuleSourceDefinition[] sourceDefinitions = new ModuleSourceDefinition[sourceDefinitionList.size()];
sourceDefinitionList.toArray(sourceDefinitions);
this.sourceDefinitionGroup = new ModuleSourceDefinitionGroup(sourceDefinitions);
}
/**
* Load the internal state of the workspace from its saved description.
*
* @param loadStatus the tracking status object.
* If a previously-persisted workspace exists but could not be successfully loaded, there will be errors.
* Otherwise, there may be info or warnings.
* @return true if there was a previously-persisted workspace, and it was successfully loaded.
*/
private boolean loadWorkspaceDescription(Status loadStatus) {
ResourcePath.FilePath workspaceDescriptionFilePath = getWorkspaceDescriptionFile();
InputStream inputStream = NullaryEnvironment.getNullaryEnvironment().getInputStream(workspaceDescriptionFilePath, loadStatus);
if (inputStream == null) {
return false;
}
try {
return persistenceManager.loadWorkspaceDescription(inputStream, loadStatus);
} finally {
try {
inputStream.close();
} catch (IOException e) {
}
}
}
/**
* Persist the workspace description.
* @param saveStatus the tracking status object.
* @return whether the description was successfully saved.
*/
private boolean saveWorkspaceDescription(Status saveStatus) {
// Get the workspace file..
ResourcePath.FilePath workspaceDescriptionFilePath = getWorkspaceDescriptionFile();
File workspaceFile = NullaryEnvironment.getNullaryEnvironment().getFile(workspaceDescriptionFilePath, true); // assumes a file system file.
// The file (if any) temporarily containing the workspace file being replaced.
File oldDescriptionFile;
if (FileSystemHelper.fileExists(workspaceFile)) {
// Get a temp file for the old description.
try {
// TODOEL: create then delete?!
oldDescriptionFile = File.createTempFile("workspace", "old");
if (!oldDescriptionFile.delete()) {
saveStatus.add(new Status(Status.Severity.ERROR, "Could not save workspace description."));
return false;
}
} catch (IOException e) {
saveStatus.add(new Status(Status.Severity.ERROR, "Could not save workspace description.", e));
return false;
}
// Rename to the temporary file.
workspaceFile.renameTo(oldDescriptionFile);
} else {
oldDescriptionFile = null;
}
boolean newWorkspaceCreated = false;
// Create an output stream on the workspace file.
OutputStream outputStream = null;
try {
outputStream = new FileOutputStream(workspaceFile);
// Save to the new file..
ResourceInfo resourceInfo = new ResourceInfo(resourceRevisionInfo, resourceSyncTimeInfo);
persistenceManager.saveWorkspaceDescription(outputStream, resourceInfo);
newWorkspaceCreated = true;
// } catch (FileNotFoundException e) {
// saveStatus.add(new Status(Status.Severity.ERROR, "Could not create file for workspace description.", e));
//
} catch (Exception e) {
saveStatus.add(new Status(Status.Severity.ERROR, "Could not create file for workspace description.", e));
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
}
}
}
if (!newWorkspaceCreated) {
// Delete the failed workspace file.
if (workspaceFile.exists()) {
workspaceFile.delete();
}
// Restore the old workspace file.
if (oldDescriptionFile != null && !oldDescriptionFile.renameTo(workspaceFile)) {
// This is really bad.
saveStatus.add(new Status(Status.Severity.ERROR, "(FATAL) Could not restore old workspace description."));
}
return false;
}
// Delete the old file.
if (oldDescriptionFile != null) {
oldDescriptionFile.delete();
}
return true;
}
/**
* Get the file which describes the contents of the workspace.
* @return the file which contains the description of the workspace.
*/
private ResourcePath.FilePath getWorkspaceDescriptionFile() {
return workspaceDescriptionFile;
}
/**
* Add a module to the workspace.
* @param storedModule the module to add.
* @param checkExisting if true, this operation will fail (with appropriate Status) if the module already exists in the workspace.
* If false, the added module will replace any existing module resources in the workspace.
* @param addStatus the tracking status object.
* @return boolean whether the module was successfully added. This will fail if the module already exists.
*/
synchronized public final boolean addModule(StoredVaultElement.Module storedModule, boolean checkExisting, Status addStatus) {
// check that the module does not already exist in the workspace.
ModuleName moduleName = ModuleName.make(storedModule.getName());
if (moduleName == null) {
addStatus.add(new Status(Status.Severity.ERROR, "Could not determine module name.", null));
return false;
}
if (checkExisting) {
if (containsModule(moduleName)) {
addStatus.add(new Status(Status.Severity.ERROR, "Module " + moduleName + " already exists in the current workspace.", null));
return false;
}
// If this is a nullary workspace, check that we are not importing something into the StandardVault which already exists.
if (isNullary() && !isTopLevelStoredModuleElementInStandardVault(storedModule) && !isCarBasedStoredModuleElementInStandardVault(storedModule) &&
StandardVault.getInstance().getSourceStore().hasFeature(new ResourceName(CALFeatureName.getModuleFeatureName(moduleName)))) { // The get shouldn't fail.
String errorString = "Module " + moduleName + " conflicts with a module which exists in the StandardVault.";
addStatus.add(new Status(Status.Severity.ERROR, errorString, null));
return false;
}
}
// Import the resources.
importStoredModule(storedModule, addStatus);
CALSourceManager sourceManager = virtualResourceManager.getSourceManager(moduleName);
if (sourceManager == null) {
addStatus.add(new Status(Status.Severity.WARNING, "No registered source manager."));
return true; // true or false??
}
// Update the module source provider.
ModuleSourceDefinition newModuleSourceDefinition = sourceManager.getSource(moduleName, addStatus);
List<ModuleSourceDefinition> newModuleSources = new ArrayList<ModuleSourceDefinition>(Arrays.asList(sourceDefinitionGroup.getModuleSources()));
ModuleSourceDefinition existingModuleSource = sourceDefinitionGroup.getModuleSource(moduleName);
if (existingModuleSource != null) {
newModuleSources.remove(existingModuleSource);
}
newModuleSources.add(newModuleSourceDefinition);
ModuleSourceDefinition[] newModuleSourceArray = newModuleSources.toArray(ModuleSourceDefinition.EMPTY_ARRAY);
this.sourceDefinitionGroup = new ModuleSourceDefinitionGroup(newModuleSourceArray);
// Persist the workspace description if not nullary.
if (!isNullary()) {
saveWorkspaceDescription(addStatus);
}
return true;
}
/**
* Remove a set of modules from the workspace.
* @param moduleNames
* @param removeStatus
* @return boolean whether the modules were successfully removed from the workspace.
*/
synchronized public final boolean removeModules (Set<ModuleName> moduleNames, Status removeStatus) {
List<ModuleSourceDefinition> sourcesToRemove = new ArrayList<ModuleSourceDefinition> ();
// Check that all listed modules exist.
for (final ModuleName moduleName : moduleNames) {
if (moduleName.equals(CAL_Prelude.MODULE_NAME)) {
removeStatus.add(new Status(Status.Severity.ERROR, "Cannot remove Cal.Core.Prelude module.", null));
return false;
}
ModuleSourceDefinition sourceToRemove = sourceDefinitionGroup.getModuleSource(moduleName);
if (sourceToRemove == null) {
return false;
}
sourcesToRemove.add (sourceToRemove);
}
List<ModuleSourceDefinition> newModuleSources = new ArrayList<ModuleSourceDefinition>(Arrays.asList(sourceDefinitionGroup.getModuleSources()));
for (final ModuleSourceDefinition sourceToRemove : sourcesToRemove) {
newModuleSources.remove(sourceToRemove);
ModuleName moduleName = sourceToRemove.getModuleName();
// Remove module resources from the resource manager if not nullary.
// We can also remove if nullary, if we imported (during this session) from another vault.
VaultElementInfo vaultInfo = getVaultInfo(moduleName);
if (!isNullary() || vaultInfo == null || !isStandardVaultDescriptor(vaultInfo.getVaultDescriptor())) {
removeStoredModule(moduleName, removeStatus);
}
}
ModuleSourceDefinition[] newModuleSourceArray = newModuleSources.toArray(ModuleSourceDefinition.EMPTY_ARRAY);
this.sourceDefinitionGroup = new ModuleSourceDefinitionGroup(newModuleSourceArray);
// Persist the workspace description (if not nullary).
if (!isNullary()) {
boolean descriptionSaved = saveWorkspaceDescription(removeStatus);
if (!descriptionSaved) {
return false;
}
}
return true;
}
/**
* Remove a module from the workspace.
* @param moduleName the name of the module to remove.
* @param removeStatus the tracking status object.
* @return boolean whether the module was successfully removed from the workspace.
*/
public boolean removeModule(ModuleName moduleName, Status removeStatus) {
Set<ModuleName> moduleNames = new HashSet<ModuleName>();
moduleNames.add (moduleName);
return removeModules (moduleNames, removeStatus);
}
/**
* Remove the stored resources for a module from the workspace.
* @param moduleName the name of the module.
* @param removeStatus the tracking status object.
*/
private void removeStoredModule(ModuleName moduleName, Status removeStatus) {
for (final ResourceManager resourceManager : virtualResourceManager.getResourceManagersForModule(moduleName)) {
if (resourceManager instanceof ModuleResourceManager) {
((ModuleResourceManager)resourceManager).removeModuleResources(moduleName, removeStatus);
}
}
virtualResourceManager.unlinkModuleFromCars(moduleName);
moduleNameToVaultInfoMap.remove(moduleName);
resourceRevisionInfo.removeModule(moduleName);
resourceSyncTimeInfo.removeModule(moduleName);
}
/**
* Remove all stored module resources from the manager.
* @param removeStatus the tracking status object.
*/
private void removeAllStoredModules(Status removeStatus) {
if (!isNullary()) {
// Remove all resources from the respective managers.
virtualResourceManager.removeAllResources(removeStatus);
}
// Clear the info maps.
moduleNameToVaultInfoMap.clear();
resourceRevisionInfo.clear();
resourceSyncTimeInfo.clear();
}
/**
* @return The WorkspaceSourceMetricsManager associated with this workspace. This return value is guaranteed to be non-null.
*/
public SourceMetrics getSourceMetrics() {
return sourceMetrics;
}
/**
* @param moduleName the name of a module.
* @return the source definition for the specified module, or null if there is no source definition for that module.
*/
public ModuleSourceDefinition getSourceDefinition(ModuleName moduleName) {
return getSourceDefinitionGroup().getModuleSource(moduleName);
}
/**
* @param moduleName the name of a module.
* @return whether a module with the given name exists in the workspace.
*/
synchronized public final boolean containsModule(ModuleName moduleName) {
return getSourceDefinition(moduleName) != null;
}
/**
* @param moduleName the name of a module.
* @return debugging information about the module, e.g. the actual location of the module's resources. Can be null if the module does not exist.
*/
public String getDebugInfoForModule(ModuleName moduleName) {
CALSourceManager sourceManager = virtualResourceManager.getSourceManager(moduleName);
if (sourceManager == null) {
return null;
} else {
CALFeatureName moduleFeatureName = CALFeatureName.getModuleFeatureName(moduleName);
ResourceName moduleResourceName = new ResourceName(moduleFeatureName);
return sourceManager.getDebugInfo(moduleResourceName);
}
}
/**
* @return debugging information about the modules, e.g. the actual location of each module's resources.
*/
public String getDebugInfo() {
StringBuilder details = new StringBuilder();
ModuleName[] moduleNames = getModuleNames();
Arrays.sort(moduleNames);
int nModules = moduleNames.length;
if (nModules > 0) {
for (int i = 0; i < nModules; i++) {
details.append(moduleNames[i]);
VaultElementInfo vaultInfo = getVaultInfo(moduleNames[i]);
if (vaultInfo != null) {
long moduleRevision = vaultInfo.getRevision();
String descriptor = vaultInfo.getVaultDescriptor();
String locationString = vaultInfo.getLocationString();
details.append("\n - " + descriptor);
if (locationString != null) {
details.append("(" + locationString + ")");
}
details.append(" revision " + moduleRevision);
}
details.append("\n");
String debugInfo = getDebugInfoForModule(moduleNames[i]);
if (debugInfo != null) {
details.append(" - ").append(debugInfo).append("\n");
}
}
} else {
details.append("No modules in workspace");
}
return details.toString();
}
/**
* Get the vault associated with a module.
* @param moduleName the name of the module.
* @return the vault associated with the module, or null if there is no such vault.
*/
public Vault getVault(ModuleName moduleName) {
VaultElementInfo currentVaultInfo = getVaultInfo(moduleName);
if (currentVaultInfo instanceof VaultElementInfo.Nested) {
if (currentVaultInfo.getVaultDescriptor().equals(CarVault.getVaultClassDescriptor())) {
VaultElementInfo.Nested nestedVaultInfo = (VaultElementInfo.Nested)currentVaultInfo;
VaultElementInfo outerVaultElementInfo = nestedVaultInfo.getOuterVaultElementInfo();
String outerVaultDescriptor = outerVaultElementInfo.getVaultDescriptor();
String outerVaultLocationString = outerVaultElementInfo.getLocationString();
Vault outerVault = vaultRegistry.getVault(outerVaultDescriptor, outerVaultLocationString);
Status status = new Status("Getting a vault");
StoredVaultElement.Car car = outerVault.getCar(outerVaultElementInfo.getElementName(), outerVaultElementInfo.getRevision(), status);
if (car == null) {
return null;
} else {
return car.getCarVault();
}
} else {
return null;
}
} else { // currentVaultInfo is a VaultElementInfo.Basic instance
String vaultDescriptor = currentVaultInfo.getVaultDescriptor();
String locationString = currentVaultInfo.getLocationString();
return vaultRegistry.getVault(vaultDescriptor, locationString);
}
}
/**
* Get the storage info for a module.
* @param moduleName the name of a module.
* @return the storage info for the given module, or null if there is no such info known to this manager.
*/
public VaultElementInfo getVaultInfo(ModuleName moduleName) {
return moduleNameToVaultInfoMap.get(moduleName);
}
/**
* Update the vault info associated with a module.
* @param moduleName the name of the module whose vault info should be updated.
* @param vaultInfo the updated vault info for the module.
*/
void updateVaultInfo(ModuleName moduleName, VaultElementInfo vaultInfo) {
moduleNameToVaultInfoMap.put(moduleName, vaultInfo);
}
/**
* Update the resource info for resources in the workspace.
* @param resourceInfo updated resource info.
*/
void updateResourceInfo(ResourceInfo resourceInfo) {
ResourceRevision.Info revisionInfo = resourceInfo.getResourceRevisionInfo();
resourceRevisionInfo.updateResourceRevisionInfo(revisionInfo);
SyncTimeInfo syncTimeInfo = resourceInfo.getResourceSyncTimeInfo();
resourceSyncTimeInfo.updateResourceSyncTimeInfo(syncTimeInfo);
}
/**
* @return Returns the vaultRegistry.
*/
public VaultRegistry getVaultRegistry() {
return vaultRegistry;
}
/**
* Register a Vault Provider with this registry.
* @param newProvider the provider to register.
*/
public void registerVaultProvider(VaultProvider newProvider) {
vaultRegistry.registerVaultProvider(newProvider);
}
/**
* Register a Vault Authenticator with this registry.
* @param newAuthenticator the authenticator to register.
*/
public void registerVaultAuthenticator(VaultAuthenticator newAuthenticator) {
vaultRegistry.registerVaultAuthenticator(newAuthenticator);
}
/**
* Register a non-Car-aware resource manager with the workspace.
* @param resourceManager the manager to register.
*/
public void registerNonCarAwareResourceManager(ResourceManager resourceManager) {
virtualResourceManager.registerNonCarAwareResourceManager(resourceManager);
}
/**
* Register a Car-aware resource manager with the workspace through a provider.
* @param provider the provider for the Car-aware resource manager.
*/
public void registerCarAwareResourceManager(VirtualResourceManager.CarAwareResourceManagerProvider provider) {
virtualResourceManager.registerCarAwareResourceManager(provider);
}
/**
* Get the names of the modules in the workspace.
* @return the names of the modules in the workspace.
*/
synchronized public final ModuleName[] getModuleNames() {
int nModules = sourceDefinitionGroup.getNModules();
ModuleName[] moduleNames = new ModuleName[nModules];
for (int i = 0; i < nModules; i++) {
ModuleSourceDefinition sourceDefinition = sourceDefinitionGroup.getModuleSource(i);
moduleNames[i] = sourceDefinition.getModuleName();
}
return moduleNames;
}
/**
* Check whether the file definition of a CAL entity contains a specified string.
* @param moduleName the module name of the entity.
* @param unqualifiedName the unqualified name of the entity.
* @param searchText the text to match within the CAL definition.
*
* @return true if search string is found; false if not
*/
public boolean checkDefinitionContent(ModuleName moduleName, String unqualifiedName, String searchText) throws IOException {
QualifiedName qualifiedScName = QualifiedName.make(moduleName, unqualifiedName);
String beginString = "// @@@begin ";
String endString = "// @@@end ";
String beginNameString = beginString + qualifiedScName;
String endNameString = endString + qualifiedScName;
// Flag indicating whether search string was found
boolean found = false;
// Flag indicating whether currently searching inside proper section
boolean insideSection = false;
Reader sourceReader = null;
try {
ModuleSourceDefinition sourceDefinition = getSourceDefinition(moduleName);
if (sourceDefinition == null) {
return false;
}
sourceReader = sourceDefinition.getSourceReader(new Status("Reader status"));
if (sourceReader == null) {
return false;
}
BufferedReader bufferedReader = new BufferedReader(sourceReader);
for (String lineString = bufferedReader.readLine(); lineString != null; lineString = bufferedReader.readLine()) {
if (!insideSection) {
// We are looking for section start
if (lineString.startsWith(beginNameString)) {
// Make sure that we didn't find part of another name
// Grab the part of the line string that starts with the gem name
StringTokenizer tokenizer =
new StringTokenizer(lineString.substring(beginString.length()));
if (tokenizer.hasMoreElements() && tokenizer.nextToken().equals(qualifiedScName.getQualifiedName())) {
insideSection = true;
}
}
} else {
// We are inside section, looking for match or section end
if (lineString.indexOf(searchText) >= 0) {
found = true;
break;
}
if (lineString.startsWith(endNameString)) {
// Make sure that we didn't find part of another name
// Grab the part of the line string that starts with the gem name
StringTokenizer tokenizer =
new StringTokenizer(lineString.substring(endString.length()));
if (tokenizer.hasMoreElements() && tokenizer.nextToken().equals(qualifiedScName.getQualifiedName())) {
insideSection = false;
break;
}
}
}
}
} catch (FileNotFoundException fnfe) {
// Couldn't find the file for the given module.
// Re-throw the exception with a proper error message.
throw new FileNotFoundException("Couldn't find the file for module " + moduleName + ".");
} finally {
// Close the file now
try {
if (sourceReader != null){
sourceReader.close();
}
} catch (IOException e) { // do nothing
}
}
return found;
}
/**
* Save an entity to the workspace.
* @param entityName the name of the entity to save.
* @param definitionText the text of the definition.
* @param metadata any metadata to save with the entity. If null, no metadata will be associated with the entity.
* @param gemDesignDocument any design to associate with the saved entity. If null, no design will be associated with the entity.
* @return Status the status of the save. If the definition could not be saved, this will be level Status.Severity.ERROR.
* Other failures will result in WARNING. Otherwise, they will be Status.Severity.OK.
*/
public Status saveEntity(QualifiedName entityName, String definitionText, ScopedEntityMetadata metadata, Document gemDesignDocument) {
boolean scExists = getGemEntity(entityName) != null;
if (LanguageInfo.isValidFunctionName(entityName.getUnqualifiedName())) {
scExists = getGemEntity(entityName) != null;
} else {
scExists = getTypeConstructor(entityName) != null;
}
Status saveStatus = new Status("Save status");
// Save it out.
boolean saveSucceeded = saveDefinition(entityName, definitionText, scExists, saveStatus);
if (!saveSucceeded) {
saveStatus.add(new Status(Status.Severity.ERROR, "Failed to save gem definition.", null));
return saveStatus;
}
if (metadata != null) {
boolean metadataSaveSucceeded = saveMetadata(metadata, saveStatus);
if (!metadataSaveSucceeded) {
saveStatus.add(new Status(Status.Severity.WARNING, "Failed to save gem metadata.", null));
}
}
if (gemDesignDocument != null) {
saveDesign(new GemDesign(entityName, gemDesignDocument), saveStatus);
}
return saveStatus;
}
/**
* Save out the definition for a CAL entity.
* @param entityName the name of the entity.
* @param definitionText the text of the CAL definition.
* @param scExists whether the entity already exists in this module.
* @param saveStatus the tracking status object.
* @return whether the definition was sucessfully saved.
*/
private boolean saveDefinition(QualifiedName entityName, String definitionText, boolean scExists, Status saveStatus) {
final ModuleName moduleName = entityName.getModuleName();
String beginString = "// @@@begin ";
String endString = "// @@@end ";
String beginNameString = beginString + entityName;
String endNameString = endString + entityName;
ModuleSourceDefinition sourceDefinition = getSourceDefinition(moduleName);
if (sourceDefinition == null) {
return false;
}
Reader sourceReader = sourceDefinition.getSourceReader(null);
if (sourceReader == null) {
String message = "Couldn't read definition for \"" + entityName + "\".";
saveStatus.add(new Status(Status.Severity.ERROR, message));
return false;
}
BufferedReader bufferedReader = new BufferedReader(sourceReader);
try {
// The new file text..
StringBuilder newFileTextBuilder = new StringBuilder();
if (!scExists) {
// The gem does not exist. Simply append its text to the end of the file.
try {
// Add all the file text
for (String lineString = bufferedReader.readLine(); lineString != null; lineString = bufferedReader.readLine()) {
newFileTextBuilder.append(lineString + "\n");
}
} catch (IOException ioe) {
String message = "Couldn't read definition for \"" + entityName + "\".";
saveStatus.add(new Status(Status.Severity.ERROR, message, ioe));
return false;
}
// Add the gem definition.
newFileTextBuilder.append(getDefinition(definitionText, beginNameString, endNameString));
} else {
// The gem exists. Attempt to replace its text in the file.
// Read in all the file text
List<String> fileText = new ArrayList<String>();
int beginIndex = -1;
int endIndex = -1;
boolean shouldEatOneMoreLine = false;
try {
for (String lineString = bufferedReader.readLine(); lineString != null; lineString = bufferedReader.readLine()) {
// Add the text to the List
fileText.add(lineString);
int lineIndex = fileText.size() - 1; // ArrayList size is a (faster) variable lookup
// Keep track of where to replace the gem text
if (beginIndex < 0){
if (lineString.startsWith(beginNameString)) {
// Make sure that we didn't find part of another name
// Grab the part of the line string that starts with the gem name
String endLineString = lineString.substring(beginString.length());
// the first token should be the gem's name
StringTokenizer tokenizer = new StringTokenizer(endLineString);
if (tokenizer.hasMoreElements() && tokenizer.nextToken().equals(entityName.getQualifiedName())) {
beginIndex = lineIndex;
}
}
} else if (endIndex < 0) {
if (lineString.startsWith(endNameString)) {
// Make sure that we didn't find part of another name
// Grab the part of the line string that starts with the gem name
String endLineString = lineString.substring(endString.length());
// the first token should be the gem's name
StringTokenizer tokenizer = new StringTokenizer(endLineString);
if (tokenizer.hasMoreElements() && tokenizer.nextToken().equals(entityName.getQualifiedName())) {
endIndex = lineIndex;
}
}
}
// See if the line following the line with the end marker is blank.
if (endIndex >= 0 && lineIndex == endIndex + 1) {
// Convert to a char array
char[] charArray = lineString.toCharArray();
// Now check each character (if any) to see if they're whitespace
boolean isBlank = true;
for (int i = 0; i < charArray.length; i++) {
if (!Character.isWhitespace(charArray[i])) {
isBlank = false;
break;
}
}
shouldEatOneMoreLine = isBlank;
}
}
} catch (IOException ioe) {
String message = "Couldn't read definition for \"" + entityName + "\".";
saveStatus.add(new Status(Status.Severity.ERROR, message, ioe));
return false;
}
// Replace the line following the line with the end marker, if appropriate
if (shouldEatOneMoreLine) {
endIndex++;
}
if (beginIndex < 0 || endIndex < 0) {
// couldn't find where to replace the lines..
// the finally clause should take care of closing everything
String markersString;
if (beginIndex < 0 && endIndex < 0) {
markersString = "begin or end";
} else if (beginIndex < 0) {
markersString = "begin";
} else {
markersString = "end";
}
String message = "Couldn't find " + markersString + " marker for \"" + entityName + "\".";
saveStatus.add(new Status(Status.Severity.ERROR, message, null));
return false;
}
//
// Add all the file text
//
List<String> beforeText = fileText.subList(0, beginIndex);
List<String> afterText = fileText.subList(endIndex + 1, fileText.size());
// Add text appearing before the gem definition
for (final String writeString : beforeText) {
newFileTextBuilder.append(writeString + "\n");
}
// Add the gem definition
newFileTextBuilder.append(getDefinition(definitionText, beginNameString, endNameString));
// Add text appearing after the gem definition
for (final String writeString : afterText) {
newFileTextBuilder.append(writeString + "\n");
}
}
// Convert to a moduleSource.
final String newFileTextString = newFileTextBuilder.toString();
ModuleSourceDefinition sourceToSave = new StringModuleSourceDefinition(moduleName, newFileTextString);
// Save..
return saveSource(sourceToSave, saveStatus);
} finally {
try {
bufferedReader.close();
} catch (IOException e) {
}
}
}
/**
* Get CAL definition text..
*
* @param definitionText the text of a CAL definition.
* @param beginString the string with which to start the definition. Should be a CAL comment.
* @param endString the string with which to end the definition. Should be a CAL comment.
* @return the string definition.
*/
private static String getDefinition(String definitionText, String beginString, String endString) {
StringBuilder sb = new StringBuilder();
Date date = new Date();
sb.append(beginString + " saved " + date + ".\n");
sb.append("// Warning: this section may be automatically regenerated by the GemCutter.\n");
sb.append(definitionText);
sb.append(endString + "\n");
sb.append("\n");
return sb.toString();
}
/**
* @param moduleName the name of the module whose associated source manager is to be returned.
* @return a source manager for the specified module.
*/
public CALSourceManager getSourceManager(ModuleName moduleName) {
// TODOEL: reduce scope. This is here for hacks in the GemCutter, the ExcludeTestModulesFilter, and JFit.
// todo-jowong edit the note above when ExcludeTestModulesFilter is refactored
return virtualResourceManager.getSourceManager(moduleName);
}
/**
* @return a ModuleNameToResourceManagerMapping that can be used to fetch the
* source manager for a particular module.
*/
public VirtualResourceManager.ModuleNameToResourceManagerMapping getModuleNameToSourceManagerMapping() {
// TODOEL: reduce scope. This is here for the RenameRefactorer, which is in the compiler package.
return virtualResourceManager.getModuleNameToSourceManagerMapping();
}
/**
* Import stored module resources into the workspace.
* @param storedModule the stored module resource.
* @param importStatus the tracking status object.
*/
private void importStoredModule(StoredVaultElement.Module storedModule, Status importStatus) {
ModuleName moduleName = ModuleName.make(storedModule.getName());
VaultElementInfo storedModuleVaultInfo = storedModule.getVaultInfo();
if (storedModuleVaultInfo instanceof VaultElementInfo.Nested) {
importStoredModuleFromNestedVault(moduleName, storedModuleVaultInfo, importStatus);
} else {
importStoredModuleFromTopLevelVault(storedModule, moduleName, storedModuleVaultInfo, importStatus);
}
}
/**
* Import the resources of a stored module that resides in a top-level vault (e.g. the StandardVault or the EnterpriseVault).
* @param storedModule the stored module.
* @param moduleName the name of the stored module.
* @param storedModuleVaultInfo the vault element info for the stored module.
* @param importStatus the tracking status object.
*/
private void importStoredModuleFromTopLevelVault(StoredVaultElement.Module storedModule, ModuleName moduleName, VaultElementInfo storedModuleVaultInfo, Status importStatus) {
// Check for an import from the StandardVault into a nullary workspace.
if (!(isNullary() && isTopLevelStoredModuleElementInStandardVault(storedModule))) {
for (Iterator<WorkspaceResource> it = storedModule.getResourceIterator(); it.hasNext(); ) {
WorkspaceResource storedModuleResource = it.next();
String resourceType = storedModuleResource.getResourceType();
ResourceManager resourceManager = virtualResourceManager.getModuleSpecificResourceManager(moduleName, resourceType);
if (resourceManager == null) {
importStatus.add(new Status(Status.Severity.ERROR, "Could not find resource manager for resource type " + resourceType + " for module " + moduleName));
continue;
}
WorkspaceResource.SyncTime syncTime = resourceManager.importResource(storedModuleResource, importStatus);
if (syncTime == null) {
importStatus.add(new Status(Status.Severity.ERROR, "Could not import resource " + storedModuleResource.getIdentifier()));
continue;
}
resourceSyncTimeInfo.updateResourceSyncTime(syncTime);
}
// Note: only update the resource revision info if necessary, as this can be expensive to calculate.
resourceRevisionInfo.updateResourceRevisionInfo(storedModule.getResourceRevisionInfo());
}
// disassociate this module from any Cars
virtualResourceManager.unlinkModuleFromCars(moduleName);
// Update the stored module info maps.
updateVaultInfo(moduleName, storedModuleVaultInfo);
}
/**
* Import the resources of a stored module that resides in a nested vault (e.g. from a Car).
* @param moduleName the name of the stored module.
* @param storedModuleVaultInfo the vault element info for the stored module.
* @param importStatus the tracking status object.
*/
private void importStoredModuleFromNestedVault(ModuleName moduleName, VaultElementInfo storedModuleVaultInfo, Status importStatus) {
if (!storedModuleVaultInfo.getVaultDescriptor().equals(CarVault.getVaultClassDescriptor())) {
importStatus.add(new Status(Status.Severity.ERROR, "The nested vault " + storedModuleVaultInfo.toString() + " is not supported"));
return;
}
VaultElementInfo.Nested nestedElementInfo = (VaultElementInfo.Nested)storedModuleVaultInfo;
VaultElementInfo.Basic outerElementInfo = nestedElementInfo.getOuterVaultElementInfo();
String carName = outerElementInfo.getElementName();
CarManager carManager = virtualResourceManager.getCarManager();
// Check for an import from the StandardVault into a nullary workspace.
if (!(isNullary() && isStandardVaultDescriptor(outerElementInfo.getVaultDescriptor()))) {
boolean carAlreadyImported = carManager.getResourceStore().hasFeature(new ResourceName(CarFeatureName.getCarFeatureName(carName)));
// import the Car if it is not already imported
if (!carAlreadyImported) {
Vault vaultContainingCar = vaultRegistry.getVault(outerElementInfo.getVaultDescriptor(), outerElementInfo.getLocationString());
if (vaultContainingCar == null) {
importStatus.add(new Status(Status.Severity.ERROR, "Error getting the " + outerElementInfo.getVaultDescriptor() + " containing the Car"));
return;
}
Car car = vaultContainingCar.getCarAsResource(carName, outerElementInfo.getRevision(), importStatus);
if (car == null) {
importStatus.add(new Status(Status.Severity.ERROR, "Error getting the Car " + carName + " from the " + outerElementInfo.getVaultDescriptor()));
return;
}
WorkspaceResource.SyncTime syncTime = carManager.importResource(car, importStatus);
if (syncTime == null) {
importStatus.add(new Status(Status.Severity.ERROR, "Could not import resource " + car.getIdentifier()));
return;
}
resourceSyncTimeInfo.updateResourceSyncTime(syncTime);
// update the resource revision info.
ResourceRevision.Info resourceRevisionInfo = new ResourceRevision.Info(
Collections.singletonList(
new ResourceRevision(car.getIdentifier(), outerElementInfo.getRevision())));
resourceRevisionInfo.updateResourceRevisionInfo(resourceRevisionInfo);
// associate this module with the Car
virtualResourceManager.linkModuleWithCar(moduleName, car.getAccessor(importStatus));
} else {
CarStore carStore = (CarStore)carManager.getResourceStore();
Car car = carStore.getCar(carName);
if (car == null) {
importStatus.add(new Status(Status.Severity.ERROR, "Error getting the Car " + carName + " from the CarStore"));
return;
}
// associate this module with the Car
virtualResourceManager.linkModuleWithCar(moduleName, car.getAccessor(importStatus));
}
} else {
CarStore carStore = (CarStore)carManager.getResourceStore();
Car car = carStore.getCar(carName);
if (car == null) {
importStatus.add(new Status(Status.Severity.ERROR, "Error getting the Car " + carName + " from the CarStore"));
return;
}
// associate this module with the Car
virtualResourceManager.linkModuleWithCar(moduleName, car.getAccessor(importStatus));
}
// now import the stored module
updateVaultInfo(moduleName, storedModuleVaultInfo);
}
/**
* @param storedModule a stored module.
* @return whether the given stored module is from the StandardVault
*/
private boolean isTopLevelStoredModuleElementInStandardVault(StoredVaultElement.Module storedModule) {
return isStandardVaultDescriptor(storedModule.getVaultInfo().getVaultDescriptor());
}
/**
* @param descriptorString a vault descriptor string.
* @return whether the given string is the descriptor for the StandardVault.
*/
private boolean isStandardVaultDescriptor(String descriptorString) {
return descriptorString.equals(StandardVault.getVaultClassProvider().getVaultDescriptor());
}
/**
* @param storedModule a stored module.
* @return whether the given stored module is from a Car in the StandardVault.
*/
private boolean isCarBasedStoredModuleElementInStandardVault(StoredVaultElement.Module storedModule) {
VaultElementInfo vaultInfo = storedModule.getVaultInfo();
if (vaultInfo instanceof VaultElementInfo.Nested) {
VaultElementInfo.Nested nestedVaultInfo = (VaultElementInfo.Nested)vaultInfo;
return nestedVaultInfo.getVaultDescriptor().equals(CarVault.getVaultClassDescriptor()) &&
isStandardVaultDescriptor(nestedVaultInfo.getOuterVaultElementInfo().getVaultDescriptor());
}
return false;
}
/**
* Synchronize a module with the vault with which it is associated.
*
* @param moduleName the name of the module to synchronize.
* @param syncStatus the tracking status object.
* @return the result of the sync.
*/
public SyncInfo syncModule(ModuleName moduleName, Status syncStatus) {
return syncModuleToRevision(moduleName, -1, false, syncStatus);
}
/**
* Synchronize a module with a given revision, with respect to the vault with which it is associated.
*
* @param moduleName the name of the module to synchronize.
* @param revisionNum the revision number of the module.
* If <0, will sync to the latest revision.
* Otherwise, will sync to the given revision, even if that revision is less than the current revision.
* @param syncStatus the tracking status object.
* @param force if true, merge / conflicts will be ignored. Any changes to resources will be clobbered.
* @return the result of the sync.
*/
public SyncInfo syncModuleToRevision(ModuleName moduleName, int revisionNum, boolean force, Status syncStatus) {
SyncInfo syncInfo = new SyncInfo();
// Get the vault for the module.
Vault vault = getVault(moduleName);
if (vault == null) {
syncStatus.add(new Status(Status.Severity.ERROR, "The vault for module " + moduleName + " is not accessible."));
return syncInfo;
}
// Check for a sync from the StandardVault to the nullary workspace, or a sync from the CarVault (which is an unmodifiable vault)
String vaultDescriptor = vault.getVaultProvider().getVaultDescriptor();
if ((isNullary() && isStandardVaultDescriptor(vaultDescriptor)) || CarVault.getVaultClassDescriptor().equals(vaultDescriptor)) {
// Always sync'd.
return syncInfo;
}
if (!(vault instanceof NonExistentVault)) {
// Get the stored module from the vault.
StoredVaultElement.Module vaultStoredModule = vault.getStoredModule(moduleName, revisionNum, syncStatus);
if (vaultStoredModule == null) {
syncStatus.add(new Status(Status.Severity.ERROR, "Could not retrieve info for module " + moduleName + "."));
return syncInfo;
}
syncModuleToStoredModule(vaultStoredModule, force, syncStatus, syncInfo);
}
return syncInfo;
}
/**
* Synchronize a module with a given stored module.
*
* @param storedModule the stored module to with which to synchronize.
* @param syncStatus the tracking status object.
* @param force if true, merge / conflicts will be ignored. Any changes to resources will be clobbered.
* if false, merge / conflicts will be noted, but affected resources will not be updated.
* @return the result of the sync.
*/
public SyncInfo syncModuleToStoredModule(StoredVaultElement.Module storedModule, boolean force, Status syncStatus) {
SyncInfo syncInfo = new SyncInfo();
syncModuleToStoredModule(storedModule, force, syncStatus, syncInfo);
return syncInfo;
}
/**
* Synchronize a module with a given stored module.
*
* @param storedModule the stored module to with which to synchronize.
* @param syncStatus the tracking status object.
* @param force if true, merge / conflicts will be ignored. Any changes to resources will be clobbered.
* if false, merge / conflicts will be noted, but affected resources will not be updated.
* @param syncInfo the object to which to add the result of the sync.
*/
synchronized private final void syncModuleToStoredModule(StoredVaultElement.Module storedModule, boolean force, Status syncStatus, SyncInfo syncInfo) {
// TODOEL: syncing to a non-existent module is equivalent to addModule().
// However, in the case of a sync, the results should be recorded in the sync info object.
ModuleName moduleName = ModuleName.make(storedModule.getName());
int revisionNum = storedModule.getVaultInfo().getRevision();
VaultElementInfo currentVaultInfo = getVaultInfo(moduleName);
if (!force && currentVaultInfo != null && !currentVaultInfo.sameVault(storedModule.getVaultInfo())) {
String statusMessage = "Module \"" + moduleName + "\" not synced." +
" Attempt to sync to a vault different from the module's originating vault.";
syncStatus.add(new Status(Status.Severity.WARNING, statusMessage));
return;
}
// Get the resource revision info for the vault store module.
ResourceRevision.Info vaultResourceRevisionInfo = storedModule.getResourceRevisionInfo();
// Map from resource type to the resources of that type from the module revision from the vault.
Map<String, Set<WorkspaceResource>> resourceTypeToResourcesFromVaultMap = new HashMap<String, Set<WorkspaceResource>>();
// Populate the map.
for (Iterator<WorkspaceResource> it = storedModule.getResourceIterator(); it.hasNext(); ) {
WorkspaceResource vaultStoredModuleResource = it.next();
String resourceType = vaultStoredModuleResource.getResourceType();
Set<WorkspaceResource> resourceTypeSet = resourceTypeToResourcesFromVaultMap.get(resourceType);
if (resourceTypeSet == null) {
resourceTypeSet = new HashSet<WorkspaceResource>();
resourceTypeToResourcesFromVaultMap.put(resourceType, resourceTypeSet);
}
resourceTypeSet.add(vaultStoredModuleResource);
}
// To calculate deleted resources, first get all the resources.
// Later, we'll remove the resources which sync'd.
Set<ResourceIdentifier> deletedResourceIdentifierSet = new HashSet<ResourceIdentifier>();
for (final ResourceManager resourceManager : virtualResourceManager.getResourceManagersForModule(moduleName)) {
// Iterate over the resources managed by the manager. Add the identifier.
for (Iterator<WorkspaceResource> it = resourceManager.getResourceStore().getResourceIterator(); it.hasNext(); ) {
WorkspaceResource workspaceResource = it.next();
FeatureName featureName = workspaceResource.getIdentifier().getFeatureName();
if (featureName instanceof CALFeatureName) {
CALFeatureName calFeatureName = (CALFeatureName)featureName;
if (calFeatureName.hasModuleName() && calFeatureName.toModuleName().equals(moduleName)) {
deletedResourceIdentifierSet.add(workspaceResource.getIdentifier());
}
}
}
}
// Whether this is an absolute revision num, ie. sync to a given revision num. -1 would mean sync to latest.
boolean syncToSpecificRevision = revisionNum > 0;
// Now populate each of the affected managers.
for (final Map.Entry<String, Set<WorkspaceResource>> entry : resourceTypeToResourcesFromVaultMap.entrySet()) {
final String resourceType = entry.getKey();
ResourceManager resourceManager = virtualResourceManager.getModuleSpecificResourceManager(moduleName, resourceType);
if (resourceManager == null) {
String statusMessage = "Resources could not be imported for resources of type " + resourceType + " -- no registered resource manager.";
syncStatus.add(new Status(Status.Severity.WARNING, statusMessage));
continue;
}
// Either / both of: out of date, modified.
// If not a forced sync, bring up to date ONLY if out of date and not modified.
Set<WorkspaceResource> vaultResourceSet = entry.getValue();
for (final WorkspaceResource moduleResource : vaultResourceSet) {
ResourceIdentifier resourceIdentifier = moduleResource.getIdentifier();
long currentRevision = resourceRevisionInfo.getResourceRevision(resourceIdentifier);
long revisionToWhichToSync = syncToSpecificRevision ?
revisionNum : vaultResourceRevisionInfo.getResourceRevision(resourceIdentifier);
long resourceSyncTime = resourceSyncTimeInfo.getResourceSyncTime(resourceIdentifier);
long lastModified = resourceManager.getTimeStamp(resourceIdentifier.getResourceName());
// Check for eligibility for import.
boolean resourceWantsUpdate;
if (force) {
resourceWantsUpdate = resourceSyncTime != lastModified;
} else {
// This includes the case where current revision == 0 (ie. does not exist).
resourceWantsUpdate = currentRevision != revisionToWhichToSync;
}
if (resourceWantsUpdate) {
// Check that the resource is not modified, such that it requires merging.
if (force || resourceSyncTime == 0 || resourceSyncTime == lastModified) {
// Import the updated resource.
WorkspaceResource.SyncTime newSyncTime = resourceManager.importResource(moduleResource, syncStatus);
// Update the sync time and add to the set of imported resources.
if (newSyncTime == null) {
syncInfo.addImportFailure(moduleResource.getIdentifier());
} else {
resourceRevisionInfo.updateResourceRevision(resourceIdentifier, revisionToWhichToSync);
resourceSyncTimeInfo.updateResourceSyncTime(newSyncTime);
syncInfo.addUpdatedResource(moduleResource.getIdentifier());
}
// Update the stored module info map.
} else {
// Merge / conflict
syncInfo.addSyncConflict(moduleResource.getIdentifier());
}
}
// Remove from the deleted resources set.
deletedResourceIdentifierSet.remove(resourceIdentifier);
}
}
// Delete deleted resources.
// If a resource exists in the workspace but not in the repository, it is either
// 1) Added by the workspace session, or
// 2) Deleted from the repository.
// We can distinguish these, because if a resource comes from a vault, it has a sync time
// (at least, for non-nullary std vault case) and we are in case 1.
for (final ResourceIdentifier deletedResourceIdentifier : deletedResourceIdentifierSet) {
String resourceType = deletedResourceIdentifier.getResourceType();
ResourceName resourceName = deletedResourceIdentifier.getResourceName();
// Remove the resource from the manager.
ResourceManager resourceManager = virtualResourceManager.getModuleSpecificResourceManager(moduleName, resourceType);
resourceManager.removeResource(resourceName, syncStatus);
// If removed, update the sync info.
if (!resourceManager.getResourceStore().hasFeature(resourceName)) {
syncInfo.addDeletedResource(deletedResourceIdentifier);
resourceRevisionInfo.removeResourceRevision(deletedResourceIdentifier);
resourceSyncTimeInfo.removeResourceSyncTime(deletedResourceIdentifier);
}
}
// Update the stored module info map.
updateVaultInfo(moduleName, storedModule.getVaultInfo());
CALSourceManager sourceManager = virtualResourceManager.getSourceManager(moduleName);
if (sourceManager == null) {
syncStatus.add(new Status(Status.Severity.WARNING, "No registered source manager."));
} else {
// Get the old module sources, remove the old source definition (if any) and add the new one.
List<ModuleSourceDefinition> newModuleSources = new ArrayList<ModuleSourceDefinition>(Arrays.asList(sourceDefinitionGroup.getModuleSources()));
for (Iterator<ModuleSourceDefinition> it = newModuleSources.iterator(); it.hasNext(); ) {
ModuleSourceDefinition moduleSourceDefinition = it.next();
if (moduleSourceDefinition.getModuleName().equals(moduleName)) {
it.remove();
break;
}
}
ModuleSourceDefinition newModuleSourceDefinition = sourceManager.getSource(moduleName, syncStatus);
newModuleSources.add(newModuleSourceDefinition);
// Create the new source provider.
ModuleSourceDefinition[] newModuleSourceArray = newModuleSources.toArray(ModuleSourceDefinition.EMPTY_ARRAY);
this.sourceDefinitionGroup = new ModuleSourceDefinitionGroup(newModuleSourceArray);
// Persist the workspace description.
if (!isNullary() && sourceDefinitionGroup.getNModules() > 0) {
saveWorkspaceDescription(syncStatus);
}
}
}
/**
* Get the vault status of module resources in the workspace.
* @return the vault-related status of the module resources in the workspace.
*/
public VaultStatus getVaultStatus() {
VaultStatus vaultStatus = new VaultStatus();
// Iterate over the modules in the workspace which have any vault info.
for (final ModuleName moduleName : moduleNameToVaultInfoMap.keySet()) {
getVaultStatus(moduleName, vaultStatus);
}
return vaultStatus;
}
/**
* Get the vault status for a given module.
* @param moduleName the name of the module.
* @return the vault-related status of the module's resources in the workspace.
*/
public VaultStatus getVaultStatus(ModuleName moduleName) {
VaultStatus vaultStatus = new VaultStatus();
getVaultStatus(moduleName, vaultStatus);
return vaultStatus;
}
/**
* Get the vault status for a given module.
* @param moduleName the name of the module.
* @param vaultStatus the vault status object into which the module's vault status should be stored.
*/
private void getVaultStatus(ModuleName moduleName, VaultStatus vaultStatus) {
VaultElementInfo currentVaultInfo = moduleNameToVaultInfoMap.get(moduleName);
String vaultDescriptor = currentVaultInfo.getVaultDescriptor();
boolean isNestedVault = (currentVaultInfo instanceof VaultElementInfo.Nested);
// Get the vault associated with the module.
Vault vault = getVault(moduleName);
if (vault == null) {
// The vault is inaccessible.
vaultStatus.addInaccessibleVaultInfo(moduleName, currentVaultInfo);
return;
}
// Get the latest module revision.
Status getStatus = new Status("Get status");
StoredVaultElement.Module latestStoredModule = vault.getStoredModule(moduleName, -1, getStatus);
// Check whether module exists in the vault.
if (latestStoredModule == null) {
if (getStatus.getSeverity().compareTo(Status.Severity.ERROR) >= 0) {
// An error occurs when retrieving the stored module info.
vaultStatus.addInaccessibleModule(moduleName);
} else {
// The module does not exist in the vault.
// Note: this means either that the module is newly-created, or that it has been deleted from the vault.
vaultStatus.addWorkspaceOnlyModule(moduleName);
}
return;
}
// The rest is not applicable if nullary and this is a standard vault module.
if (isNullary() && isStandardVaultDescriptor(vaultDescriptor)) {
return;
}
// Check for out-of-date module.
long currentModuleRevision = currentVaultInfo.getRevision();
long latestModuleRevision = latestStoredModule.getVaultInfo().getRevision();
if (latestModuleRevision > currentModuleRevision) {
vaultStatus.addOutOfDateModule(moduleName);
}
// Get the revision info for the latest module revision.
ResourceRevision.Info latestResourceRevisionInfo = latestStoredModule.getResourceRevisionInfo();
// Resources: modified, out of date, workspace only, vault only, unmanaged (no resource manager).
// Map from resource type to the resources of that type from the latest module revision.
Map<String, Set<WorkspaceResource>> resourceTypeToLatestResourcesMap = new HashMap<String, Set<WorkspaceResource>>();
// Populate the map.
for (Iterator<WorkspaceResource> it = latestStoredModule.getResourceIterator(); it.hasNext(); ) {
WorkspaceResource latestStoredModuleResource = it.next();
String resourceType = latestStoredModuleResource.getResourceType();
Set<WorkspaceResource> resourceTypeSet = resourceTypeToLatestResourcesMap.get(resourceType);
if (resourceTypeSet == null) {
resourceTypeSet = new HashSet<WorkspaceResource>();
resourceTypeToLatestResourcesMap.put(resourceType, resourceTypeSet);
}
resourceTypeSet.add(latestStoredModuleResource);
}
// Now iterate over each of the resource types, if the module is not contained a nested vault (e.g. a Car vault).
// We omit nested vaults because currently, resources contained in such vaults are not synchronized independently,
// and a nested vault cannot contain multiple revisions of the same resource.
if (!isNestedVault) {
for (final Map.Entry<String, Set<WorkspaceResource>> entry : resourceTypeToLatestResourcesMap.entrySet()) {
final String resourceType = entry.getKey();
Set<WorkspaceResource> latestTypeResourceSet = entry.getValue();
// Get the resource manager for this resource type and for the resource's module.
ResourceManager resourceManager = virtualResourceManager.getModuleSpecificResourceManager(moduleName, resourceType);
// Iterate over the resources for this type.
for (final WorkspaceResource moduleResource : latestTypeResourceSet) {
ResourceIdentifier resourceIdentifier = moduleResource.getIdentifier();
if (resourceManager == null) {
vaultStatus.addUnmanagedResource(resourceIdentifier);
} else {
// Get the revisions.
long currentRevision = resourceRevisionInfo.getResourceRevision(resourceIdentifier);
long latestRevision = latestResourceRevisionInfo.getResourceRevision(resourceIdentifier);
// Compare revisions.
if (latestRevision > currentRevision) {
vaultStatus.addOutOfDateResource(resourceIdentifier);
}
long resourceSyncTime = resourceSyncTimeInfo.getResourceSyncTime(resourceIdentifier);
long lastModified = resourceManager.getTimeStamp(resourceIdentifier.getResourceName());
if (resourceSyncTime == 0) {
// Never synced (vault only).
vaultStatus.addVaultOnlyResource(resourceIdentifier);
} else if (resourceSyncTime < lastModified) {
vaultStatus.addModifiedResource(resourceIdentifier);
}
}
}
}
}
// Create the set of identifiers for resource in the latest stored module.
Set<ResourceIdentifier> latestStoredModuleResourceIdentifierSet = new HashSet<ResourceIdentifier>();
for (final Set<WorkspaceResource> latestTypeResourceSet : resourceTypeToLatestResourcesMap.values()) {
for (final WorkspaceResource moduleResource : latestTypeResourceSet) {
latestStoredModuleResourceIdentifierSet.add(moduleResource.getIdentifier());
}
}
// Get the workspace-only resources.
for (final ResourceManager resourceManager : virtualResourceManager.getResourceManagersForModule(moduleName)) {
ResourceStore resourceStore = resourceManager.getResourceStore();
if (!(resourceStore instanceof ResourceStore.Module)) {
continue;
}
for (Iterator<WorkspaceResource> it = ((ResourceStore.Module)resourceStore).getResourceIterator(moduleName); it.hasNext(); ) {
WorkspaceResource moduleResource = it.next();
ResourceIdentifier resourceIdentifier = moduleResource.getIdentifier();
if (!latestStoredModuleResourceIdentifierSet.contains(resourceIdentifier)) {
vaultStatus.addWorkspaceOnlyResource(resourceIdentifier);
}
}
}
}
/**
* @return the resource managers for the given module.
*/
public Set<ResourceManager> getResourceManagersForModule(ModuleName moduleName) {
return virtualResourceManager.getResourceManagersForModule(moduleName);
}
/**
* Get the abstract path to a given resource.
* @param identifier the identifier of the resource.
* @return the path to that resource, or null if there is no such path
* (eg. a resource manager is not registered for that resource type).
*/
public ResourcePath.FilePath getResourcePath(ResourceIdentifier identifier) {
ResourceManager resourceManager = virtualResourceManager.getResourceManagerForResource(identifier);
if (resourceManager == null) {
return null;
}
// Note that the workspace uses path-based resource stores.
return ((ResourcePathStore)resourceManager.getResourceStore()).getResourcePath(identifier.getResourceName());
}
/**
* Returns a DependencyFinder that can be used for finding module dependencies.
* @param rootModuleNames the names of the modules whose dependencies are to be collected.
* @return a new DependencyFinder instance.
*/
public DependencyFinder getDependencyFinder(Collection<ModuleName> rootModuleNames) {
return new DependencyFinder(this, rootModuleNames);
}
/**
* A helper class for finding module dependencies.
*
* @author James Wright
*/
public static final class DependencyFinder {
/** workspace to search */
private final CALWorkspace workspace;
/** Set of module names that we are checking the dependencies of */
private final SortedSet<ModuleName> rootModuleSet;
/** Set of module names that are imported by rootModuleSet */
private final SortedSet<ModuleName> importedModuleSet = new TreeSet<ModuleName>();
private DependencyFinder(CALWorkspace workspace, Collection<ModuleName> rootModules) {
if(workspace == null || rootModules == null) {
throw new NullPointerException();
}
this.workspace = workspace;
this.rootModuleSet = new TreeSet<ModuleName>(rootModules);
for(final ModuleName moduleName: rootModuleSet) {
findModuleDependencies(moduleName);
}
}
/**
* Add all the dependencies for module moduleName to importedModuleSet.
* @param moduleName name of module
*/
private void findModuleDependencies(ModuleName moduleName) {
MetaModule metaModule = workspace.getMetaModule(moduleName);
ModuleTypeInfo moduleTypeInfo = metaModule.getTypeInfo();
for(int i = 0, nImports = moduleTypeInfo.getNImportedModules(); i < nImports; i++) {
ModuleTypeInfo importedModule = moduleTypeInfo.getNthImportedModule(i);
ModuleName importedModuleName = importedModule.getModuleName();
if(rootModuleSet.contains(importedModuleName) || importedModuleSet.contains(importedModuleName)) {
continue;
}
importedModuleSet.add(importedModuleName);
findModuleDependencies(importedModuleName);
}
}
/**
* @return SortedSet of root module names
*/
public final SortedSet<ModuleName> getRootSet() {
return Collections.unmodifiableSortedSet(rootModuleSet);
}
/**
* @return SortedSet of names of modules imported (directly or indirectly) by
* the modules in the root set.
*/
public final SortedSet<ModuleName> getImportedModulesSet() {
return Collections.unmodifiableSortedSet(importedModuleSet);
}
}
}