/* * 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. */ /* * Perspective.java * Creation date: Oct 3, 2002. * By: Edward Lam */ package org.openquark.cal.services; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import org.openquark.cal.compiler.DataConstructor; import org.openquark.cal.compiler.ModuleName; import org.openquark.cal.compiler.ModuleTypeInfo; import org.openquark.cal.compiler.QualifiedName; import org.openquark.cal.compiler.TypeConstructor; /** * A perspective defines a working context within a workspace. * With a Perspective, one may specify a working module (to define visibility) and, optionally, * a view policy that affects the visibility of entities. * Creation date: Oct 3, 2002. * @author Edward Lam */ public final class Perspective { /** The name of the current working module, providing the perspective on the program. */ private ModuleName workingModuleName; /** The workspace on which the perspective is based. */ private final CALWorkspace workspace; // TypeAnalyzer typeAnalyzer; /** A GemViewer to apply to the perspective. * If this is null, the perspective is not viewed through a viewer. */ private GemViewer gemEntityViewer = null; /** * Constructor for a perspective. * @param workspace the workspace for which this perspective will apply. * @param workingModule the initial working module, or null if there are no modules.. */ public Perspective(CALWorkspace workspace, MetaModule workingModule) { Assert.isNotNullArgument(workspace, "workspace"); if (workspace.getNMetaModules() != 0) { Assert.isNotNullArgument(workingModule, "workingModule"); } this.workspace = workspace; this.workingModuleName = workingModule == null ? null : workingModule.getName(); } /** * Set the working module. * @param newModuleName the name of the new working module. */ public void setWorkingModule(ModuleName newModuleName) { Assert.isNotNullArgument(newModuleName, "newModuleName"); // check that the module exists. MetaModule newWorkingModule = workspace.getMetaModule(newModuleName); if (newWorkingModule == null) { throw new IllegalArgumentException("Module " + newModuleName + " is not a member of the workspace."); } this.workingModuleName = newModuleName; } /** * Get the working module. * @return MetaModule the working module, or null if the working module no longer exists in the current perspective. */ public MetaModule getWorkingModule() { return workspace.getMetaModule(workingModuleName); } /** * Get the name of the current working module. * Note that the module with this name may not exist in the workspace if the module was removed and the perspective was not updated. * @return the name of the current working module. */ public ModuleName getWorkingModuleName() { return workingModuleName; } /** * @return type info about the current working module. */ public ModuleTypeInfo getWorkingModuleTypeInfo() { MetaModule metaModule = getWorkingModule(); return metaModule == null ? null : metaModule.getTypeInfo(); } /** * Get the CALWorkspace represented by this Perspective. * @return CALWorkspace */ public CALWorkspace getWorkspace() { return workspace; } /** * @param moduleName the name of the module * @return the MetaModule with the given module name or null if there is no module with that name */ public MetaModule getMetaModule(ModuleName moduleName) { int numModules = workspace.getNMetaModules(); for (int n = 0; n < numModules; n++) { MetaModule module = workspace.getNthMetaModule(n); if (module.getName().equals(moduleName)) { return module; } } return null; } /** * Get the number of visible modules. * @return int */ public int getNVisibleMetaModules() { MetaModule workingModule = getWorkingModule(); if (workingModule == null) { return 0; } // it's the number of imported modules, plus one for the working module itself return workingModule.getNImportedModules() + 1; } /** * Get the nth visible module. * The first (n-1) visible modules correspond to the nth imported module in the corresponding ModuleTypeInfo. * The nth module will be the working module itself. * @param n 0 based module index * @return MetaModule */ public MetaModule getVisibleMetaModule(int n) { int numVisibleModules = getNVisibleMetaModules(); if (n < 0 || n >= numVisibleModules) { throw new IndexOutOfBoundsException("Must be between 0 and " + numVisibleModules); } MetaModule workingModule = getWorkingModule(); if (n == numVisibleModules - 1) { return workingModule; } if (workingModule == null) { return null; } return workingModule.getNthImportedModule(workspace, n); } /** * Get the modules in the current workspace which are not visible from the current perspective. * @return the MetaModules not visible from the current perspective. */ public List<MetaModule> getInvisibleMetaModules() { List<MetaModule> moduleList = new ArrayList<MetaModule>(); int nModules = workspace.getNMetaModules(); for (int i = 0; i < nModules; i++) { MetaModule nthMetaModule = workspace.getNthMetaModule(i); if (!isVisibleModule(nthMetaModule.getName())) { moduleList.add(nthMetaModule); } } return moduleList; } /** * Get the modules in the current workspace which are visible from the current perspective. * @return the MetaModules visible from the current perspective */ public List<MetaModule> getVisibleMetaModules() { List<MetaModule> moduleList = new ArrayList<MetaModule>(); int nModules = getNVisibleMetaModules(); for (int i = 0; i < nModules; i++) { moduleList.add(getVisibleMetaModule(i)); } return moduleList; } /** * Get all visible entities in a workspace. * Visibility is determined by the current working module. * @return the set of visible entities. */ public Set<GemEntity> getVisibleGemEntities() { Set<GemEntity> visibleEntitySet = new LinkedHashSet<GemEntity>(); // add all visible entities from visible modules int nVisibleModules = getNVisibleMetaModules(); for (int i = 0; i < nVisibleModules; i++) { MetaModule nthModule = getVisibleMetaModule(i); visibleEntitySet.addAll(getVisibleGemEntities(nthModule)); } // Apply the view policy if any. if (gemEntityViewer != null) { visibleEntitySet = new LinkedHashSet<GemEntity>(gemEntityViewer.view(visibleEntitySet)); } return visibleEntitySet; } /** * Get the visible entities in a module. * Visibility is determined by the current working module. * @param module the module in which to look. * @return the set of visible entities. */ public Set<GemEntity> getVisibleGemEntities(MetaModule module) { // Check that module != null Assert.isNotNullArgument(module, "module"); int nEntities = module.getNGemEntities(); ModuleTypeInfo workingModuleTypeInfo = getWorkingModuleTypeInfo(); // Add all entities from the working module, or only public entities from non-working modules. Set<GemEntity> visibleEntitySet = new LinkedHashSet<GemEntity>(); for (int i = 0; i < nEntities; i++) { GemEntity gemEntity = module.getNthGemEntity(i); if (workingModuleTypeInfo.isEntityVisible(gemEntity.getFunctionalAgent())) { visibleEntitySet.add(gemEntity); } } // Apply the view policy if any. if (gemEntityViewer != null) { visibleEntitySet = new LinkedHashSet<GemEntity>(gemEntityViewer.view(visibleEntitySet)); } return visibleEntitySet; } /** * Get a given visible GemEntity. * Visibility is determined by the current working module. * @param entityName the entity's name * @return the entity, or null if not visible or it doesn't exist. */ public GemEntity getVisibleGemEntity(QualifiedName entityName) { GemEntity gemEntity = workspace.getGemEntity(entityName); if (gemEntity == null) { return null; } // check for visibility. if (isVisibleModule(entityName.getModuleName())) { return gemEntity; } // not visible return null; } /** * Gets the type constructor with the given name if it is visible from the current module. * @param typeConstructorName the name of the type constructor. * @return the type constructor, or null if the given type constructor is not visible. */ public TypeConstructor getTypeConstructor(QualifiedName typeConstructorName) { MetaModule workingModule = getWorkingModule(); return workingModule == null ? null : workingModule.getTypeInfo().getVisibleTypeConstructor(typeConstructorName); } /** * Returns whether a given name is the name of a visible module. * @param moduleName * @return boolean */ public boolean isVisibleModule(ModuleName moduleName) { MetaModule workingModule = getWorkingModule(); if (workingModule == null) { return false; } if (workingModule.getName().equals(moduleName)) { return true; } return workingModule.getTypeInfo().getImportedModule(moduleName) != null; } /** * Set the new entity viewer for this perspective. * @param newViewer the new viewer, or null to reset to the default (do-not-alter) policy. */ public void setGemEntityViewer(GemViewer newViewer) { this.gemEntityViewer = newViewer; } /** * Obtain the entity for this function (supercombinator or class method). * Returns null if the entity is not found or if the resolution is ambiguous according * to the scoping rules for an unqualified function in CAL defined in the current module. * * @param unqualifiedName the name of the entity * @return the corresponding entity, or null. */ public GemEntity resolveGemEntity(String unqualifiedName) { Assert.isNotNullArgument(unqualifiedName, "unqualifiedName"); MetaModule workingModule = getWorkingModule(); if (workingModule == null) { return null; } // check if the unqualifiedName can be resolved in the current module GemEntity gemEntity = workingModule.getGemEntity(unqualifiedName); if (gemEntity != null) { return gemEntity; } int foundIndex = -1; int nImportedModules = workingModule.getNImportedModules(); ModuleTypeInfo workingModuleTypeInfo = workingModule.getTypeInfo(); for (int i = 0; i < nImportedModules; ++i) { gemEntity = workingModule.getNthImportedModule(workspace, i).getGemEntity(unqualifiedName); if (gemEntity != null && workingModuleTypeInfo.isEntityVisible(gemEntity.getFunctionalAgent())) { foundIndex = i; break; } } //Check for an ambiguous import if (foundIndex != -1) { for (int i = foundIndex + 1; i < nImportedModules; ++i) { GemEntity otherEntity = workingModule.getNthImportedModule(workspace, i).getGemEntity(unqualifiedName); if (otherEntity != null && workingModuleTypeInfo.isEntityVisible(otherEntity.getFunctionalAgent())) { //ambigious symbol resolution return null; } } } return gemEntity; } /** * Obtain the entity for this function (supercombinator or class method), which * may be an ambiguous import (that is, an entity with the specified unqualified * name may be contained in more than one imported module). * * Returns null if the entity is not found * * @param unqualifiedName the name of the entity * @return GemEntity */ public GemEntity resolveAmbiguousGemEntity(String unqualifiedName) { Assert.isNotNullArgument(unqualifiedName, "unqualifiedName"); MetaModule workingModule = getWorkingModule(); if (workingModule == null) { return null; } // check if the unqualifiedName can be resolved in the current module GemEntity gemEntity = workingModule.getGemEntity(unqualifiedName); if (gemEntity != null) { return gemEntity; } // check if the unqualifiedName can be resolved in imported modules int nImportedModules = workingModule.getNImportedModules(); ModuleTypeInfo workingModuleTypeInfo = workingModule.getTypeInfo(); for (int i = 0; i < nImportedModules; ++i) { gemEntity = workingModule.getNthImportedModule(workspace, i).getGemEntity(unqualifiedName); if (gemEntity != null && workingModuleTypeInfo.isEntityVisible(gemEntity.getFunctionalAgent())) { break; } } return gemEntity; } /** * Find out whether a given type constructor is an Enum type. * What this means is that the type is an algebraic data type (introduced by a non-foreign data declaration), * all data constructors have arity 0, and there is at least one data constructor. * @param typeConstructorName the name of the type constructor. * @return true if the type constructor is a visible enumerated type. */ public boolean isEnumDataType(QualifiedName typeConstructorName) { MetaModule workingModule = getWorkingModule(); return workingModule == null ? false : workingModule.isEnumDataType(typeConstructorName); } /** * @param typeConstructorName the name of the type constructor * @return the array with all visible data constructors for the given type constructor. * Will be null if the type constructor is not visible or zero length if there are no * visible data constructors. */ public DataConstructor[] getDataConstructorsForType(QualifiedName typeConstructorName) { MetaModule workingModule = getWorkingModule(); return workingModule == null ? null : workingModule.getDataConstructorsForType(typeConstructorName); } /** * @return all type constructor entities visible from the current module. Null if there is no current module. */ public TypeConstructor[] getTypeConstructors() { MetaModule workingModule = getWorkingModule(); return workingModule == null ? null : workingModule.getTypeConstructors(); } /** * GemCutter: every edit. Errors only shown for working module * Not implemented * @param gemEntity */ void saveEntityToWorkingModule(GemEntity gemEntity) { throw new UnsupportedOperationException("not implemented"); } /** * Not implemented * @param entity */ void deleteEntityFromWorkingModule(GemEntity entity) { throw new UnsupportedOperationException("not implemented"); } /** * Not implemented */ void refreshWorkingModuleFromWorkspace() { throw new UnsupportedOperationException("not implemented"); } }