Package org.openquark.cal.metadata

Source Code of org.openquark.cal.metadata.MetadataRenameUpdater$StatusListener

/*
* 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.
*/


/*
* MetadataRenameUpdater.java
* Created: Apr 4, 2005
* By: Peter Cardwell
*/

package org.openquark.cal.metadata;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Set;

import org.openquark.cal.compiler.CodeAnalyser;
import org.openquark.cal.compiler.CodeQualificationMap;
import org.openquark.cal.compiler.MessageLogger;
import org.openquark.cal.compiler.ModuleContainer;
import org.openquark.cal.compiler.ModuleName;
import org.openquark.cal.compiler.ModuleNameResolver;
import org.openquark.cal.compiler.ModuleTypeInfo;
import org.openquark.cal.compiler.QualifiedName;
import org.openquark.cal.compiler.SourceIdentifier;
import org.openquark.cal.compiler.TypeChecker;
import org.openquark.cal.compiler.CodeAnalyser.QualificationResults;
import org.openquark.cal.compiler.CompilerMessage.Severity;
import org.openquark.cal.compiler.SourceIdentifier.Category;
import org.openquark.cal.services.CALFeatureName;
import org.openquark.cal.services.LocalizedResourceName;
import org.openquark.cal.services.ResourceName;
import org.openquark.cal.services.Status;
import org.openquark.cal.services.WorkspaceResource;


/**
* Provides functionality to update metadata resources to reflect a renaming.
*
* This class can be used to update metadata whenever a gem, type class, or type constructor
* is renamed.
*
* @author Peter Cardwell
*/
public class MetadataRenameUpdater {
   
    /**
     * Interface for listeners wishing to receive status updates from this updater.
     * @author Peter Cardwell
     */
    public static interface StatusListener {
        /**
         * Refactorer is about to use the specified resource.
         * @param resource module to be accessed
         */
        public void willUseResource(WorkspaceResource resource);
       
        /**
         * Refactorer has finished using the specified resource.
         * @param resource module which has been accessed
         */
        public void doneUsingResource(WorkspaceResource resource);
    }
   
    /**
     * Used for errors that occur during the updateMetadata operation.
     * @author Peter Cardwell
     */
    private static class RenamingException extends Exception {
       
        private static final long serialVersionUID = -6565271241591817292L;
       
        private final String errorMessage;
       
        RenamingException (String errorMessage) {
            super(errorMessage);
            this.errorMessage = errorMessage;
        }
       
        String getErrorMessage() {
            return errorMessage;
        }
    }
   
    private final Status status;
    private final TypeChecker typeChecker;
    private final QualifiedName newName;
    private final QualifiedName oldName;
    private final SourceIdentifier.Category category;
   
    /** Status listener, if any */
    private StatusListener statusListener = null;
   
    /**
     * Constructs a new MetadataRenameUpdater
     * @param status A status object to keep track of any error messages.
     * @param typeChecker The type checker to use for the renaming of code expressions (ie. in code gems).
     * @param newName The new name of the identifier after it was renamed.
     * @param oldName The old name of the identifier before it was renamed.
     * @param category The category of the identifier.
     */
    public MetadataRenameUpdater(Status status, TypeChecker typeChecker, QualifiedName newName, QualifiedName oldName, SourceIdentifier.Category category) {
        this.status = status;
        this.typeChecker = typeChecker;
        this.newName = newName;
        this.oldName = oldName;
        this.category = category;
    }
   
    /**
     * Returns the new CALFeatureName that the given metadata should be updated with, or
     * if updating is not necessary, the old metadata's featureName.
     */
    private CALFeatureName getNewMetadataFeatureName(CALFeatureMetadata metadata) {
        // If we are renaming a module, we have to update the module name of metadata for all CALFeatures within that module.
        if (category == SourceIdentifier.Category.MODULE_NAME) {
            if (metadata.getFeatureName().getType() == CALFeatureName.MODULE) {
                if (oldName.getUnqualifiedName().equals(metadata.getFeatureName().getName())) {
                    return CALFeatureName.getModuleFeatureName(newName.getModuleName());
                }
            } else {
                QualifiedName oldMetadataName = QualifiedName.makeFromCompoundName(metadata.getFeatureName().getName());
                if (oldName.getModuleName().equals(oldMetadataName.getModuleName())) {
                    QualifiedName newMetadataName = QualifiedName.make(newName.getModuleName(), oldMetadataName.getUnqualifiedName());
                    return new CALFeatureName(metadata.getFeatureName().getType(), newMetadataName.getQualifiedName());
                }
            }
        // If we are not renaming a module, we only need to rename the metadata if is corresponds to the entity which we are renaming.
        } else {
            if (metadata.getFeatureName().getType() != CALFeatureName.MODULE) {
                QualifiedName oldMetadataName = QualifiedName.makeFromCompoundName(metadata.getFeatureName().getName());
                if (oldName.equals(oldMetadataName)) {
                   return new CALFeatureName(metadata.getFeatureName().getType(), newName.getQualifiedName());
                }
            }
        }
        return metadata.getFeatureName();
    }
   
    /**
     * Updates the examples for the given metadata object to reflect the renaming.
     * @return true if any examples were changed, false otherwise
     */
    private boolean updateExamples(FunctionalAgentMetadata functionalAgentMetadata, ModuleContainer moduleContainer) {
        // Get the array of examples and then loop through them and update them if necessary
        CALExample[] examples = functionalAgentMetadata.getExamples();
        boolean examplesChanged = false;
        for (int i = 0, n = examples.length; i < n; i++) {
            CALExample curExample = examples[i];
            CALExpression oldExpression = curExample.getExpression();
           
            // Get the qualified version of the new string
            ModuleTypeInfo moduleTypeInfo = moduleContainer.getModuleTypeInfo(oldExpression.getModuleContext());
            if (moduleTypeInfo == null) {
                // The module is not in the workspace.
                String featureName = functionalAgentMetadata.getFeatureName().getName();
                ModuleName moduleContext = oldExpression.getModuleContext();
                String errorMessage = "Can not update the metadata for " + featureName + ", example " + (i + 1) +
                                      ", since its module context " + moduleContext + " is not in the workspace.";
               
                // Add a warning.
                // Unfortunately, warnings are ignored. 
                // However, throwing a RenamingException will cause renaming to fail, which is not what we want.
                status.add(new Status(Status.Severity.WARNING, errorMessage));
                continue;
               
//                throw new RenamingException(errorMessage);
            }
           
            ModuleNameResolver oldExpressionContextModuleNameResolver = moduleTypeInfo.getModuleNameResolver();
            String newExpressionString = typeChecker.calculateUpdatedCodeExpression(oldExpression.getExpressionText(), oldExpression.getModuleContext(), oldExpressionContextModuleNameResolver, oldExpression.getQualificationMap(), oldName, newName, category, null);
           
            CodeAnalyser codeAnalyser = new CodeAnalyser(typeChecker, moduleTypeInfo, false, false);
            MessageLogger logger = new MessageLogger();
            QualificationResults qualificationResults = codeAnalyser.qualifyExpression(newExpressionString, null, oldExpression.getQualificationMap(), logger);
            if (qualificationResults == null) {
                // failed to parse.
                String featureName = functionalAgentMetadata.getFeatureName().getName();
                String errorMessage =
                    "Can not update the metadata for " + featureName + ", example " + (i + 1) + ", since it could not be parsed.\n" +
                    "Compiler messages (if any): " + logger.getCompilerMessages(Severity.ERROR);
               
                status.add(new Status(Status.Severity.WARNING, errorMessage));
                continue;
            }
           
            String qualifiedNewExpressionString = qualificationResults.getQualifiedCode();
           
            // Update the qualification map
            CodeQualificationMap newQualifiedMap = oldExpression.getQualificationMap();                       
            boolean qualificationMapWasUpdated = updateQualificationMap(newQualifiedMap);
            boolean moduleContextNeedsUpdating =
                (category == Category.MODULE_NAME && oldName.getModuleName().equals(oldExpression.getModuleContext()));
           
            if (!newExpressionString.equals(oldExpression.getExpressionText()) || qualificationMapWasUpdated || moduleContextNeedsUpdating) {
                // If changes were made, create a new expression and save the example.
               
                ModuleName newModuleContext;
                if (moduleContextNeedsUpdating) {
                    newModuleContext = newName.getModuleName();
                } else {
                    newModuleContext = oldExpression.getModuleContext();
                }
                CALExpression newExpression = new CALExpression(newModuleContext, newExpressionString, newQualifiedMap, qualifiedNewExpressionString);
                CALExample newExample = new CALExample(newExpression, curExample.getDescription(), curExample.evaluateExample());
                examples[i] = newExample;
                examplesChanged = true;
               
            }
        }
        if (examplesChanged) {
            // Replace the metadata's examples with the new contents of the examples array and saave the metadata
            functionalAgentMetadata.setExamples(examples);
            return true;
        }
        return false;
    }
   
    /**
     * Updates all the metadata files stored by the given workspace to reflect the renaming.
     * @param moduleContainer The workspace containing all the metadatas the user wishes to update
     * @return True if any metadata was updated
     */
    public boolean updateMetadata(ModuleContainer moduleContainer) {
        List<CALFeatureMetadata> undoList = new ArrayList<CALFeatureMetadata>(); // A list of metadata files that have been updated and should be undone if an error is encountered.
        try {
            ModuleName[] workspaceModuleNames = moduleContainer.getModuleNames();
           
            // loop through each module in the workspace
            for (final ModuleName moduleName : workspaceModuleNames) {
               
                MetadataManager metadataManager = (MetadataManager) moduleContainer.getResourceManager(moduleName, WorkspaceResource.METADATA_RESOURCE_TYPE);
               
                // Loop over all metadata resources for the module in the workspace and get their corresponding metadata file.
                for (Iterator<WorkspaceResource> it = ((MetadataStore)metadataManager.getResourceStore()).getResourceIterator(moduleName); it.hasNext(); ) {
                    boolean metadataUpdated = false;
                   
                    WorkspaceResource metadataResource = it.next();
                    if (statusListener != null) {
                        statusListener.willUseResource(metadataResource);
                    }
                   
                    ResourceName oldMetadataResourceName = metadataResource.getIdentifier().getResourceName();
                    Locale metadataLocale = LocalizedResourceName.localeOf(oldMetadataResourceName);
                   
                    // We want to keep a copy of the old metadata without any changes made to it so we can restore it later if an error occurs.
                    CALFeatureMetadata oldMetadata = metadataManager.getMetadata((CALFeatureName)oldMetadataResourceName.getFeatureName(), metadataLocale);
                   
                    // Make sure the metadata's module is in the current workspace
                    ModuleName metadataModuleName = oldMetadata.getFeatureName().toModuleName();
                    if (moduleContainer.containsModule(metadataModuleName)) {
                        continue;
                    }
                   
                    CALFeatureName newMetadataFeatureName = getNewMetadataFeatureName(oldMetadata);              
                    CALFeatureMetadata newMetadata = oldMetadata.copy(newMetadataFeatureName, metadataLocale);
                    if (!newMetadataFeatureName.equals(oldMetadata.getFeatureName())) {
                        metadataUpdated = true;
                    }
                   
                    // Try to update the examples if the metadata is for a functional agent.
                    if (newMetadata instanceof FunctionalAgentMetadata) {
                        if (updateExamples((FunctionalAgentMetadata)newMetadata, moduleContainer)) {
                            metadataUpdated = true;
                        }
                    }
                   
                    if (metadataUpdated) {
                        if (!metadataManager.getResourceStore().isWriteable(oldMetadataResourceName)) {
                            throw new RenamingException("Can not update the metadata for " + newMetadata.getFeatureName().getName() + " because it is not writeable.");
                        }
                       
                        Status saveMetadataStatus = new Status("Save metadata status");
                        metadataManager.saveMetadata(newMetadata, oldMetadata.getFeatureName(), metadataLocale, saveMetadataStatus);
                        if (saveMetadataStatus.getSeverity().equals(Status.Severity.ERROR)) {
                            throw new RenamingException("Error saving the updated metadata for " + newMetadata.getFeatureName().getName() + ".");
                        }
                       
                        undoList.add(oldMetadata);
                    }
                   
                    if (statusListener != null) {
                        statusListener.doneUsingResource(metadataResource);
                    }
                }
            }
           
            // Return true if the undoList has contents (ie. changes have been made)
            return !undoList.isEmpty();
       
        } catch (RenamingException e) {
            status.add(new Status(Status.Severity.ERROR, e.getErrorMessage()));
           
            for (int i = undoList.size() - 1; i >= 0; i--) {
                CALFeatureMetadata metadata = undoList.get(i);
               
                Status undoStatus = new Status("Undo metadata update status");
                moduleContainer.saveMetadata(metadata, undoStatus);
               
                if (undoStatus.getSeverity().equals(Status.Severity.ERROR)) {
                    String msg = "FATAL: Error undoing the metadata update for " + metadata.getDisplayName();
                    status.add(new Status(Status.Severity.ERROR, msg));
                }
            }
            return false;
        }
    }
   
    /**
     * Updates all references to the renamed entity in the given CodeQualificationMap. If the entity type is a module,
     * this can include any identifiers that use that module name. Otherwise, the qualification for the entity itself will be
     * updated if it is in the map.
     * @param qualificationMap The map to update
     * @return True if the qualification map is changed.
     */
    private boolean updateQualificationMap(CodeQualificationMap qualificationMap) {
        boolean changesMade = false;
       
        SourceIdentifier.Category[] categories = new SourceIdentifier.Category[] {
                SourceIdentifier.Category.TOP_LEVEL_FUNCTION_OR_CLASS_METHOD,
                SourceIdentifier.Category.DATA_CONSTRUCTOR,
                SourceIdentifier.Category.TYPE_CONSTRUCTOR,
                SourceIdentifier.Category.TYPE_CLASS                   
        };
       
        for (final Category sectionCategory : categories) {
            Set<String> unqualifiedNames = qualificationMap.getUnqualifiedNames(sectionCategory);
            for (final String unqualifiedName : unqualifiedNames) {
                QualifiedName qualifiedName = qualificationMap.getQualifiedName(unqualifiedName, sectionCategory);
               
                if (category == sectionCategory && oldName.equals(qualifiedName)) {
                    qualificationMap.removeQualification(unqualifiedName, sectionCategory);
                    qualificationMap.putQualification(newName.getUnqualifiedName(), newName.getModuleName(), sectionCategory);
                    changesMade = true;
                } else if (category == SourceIdentifier.Category.MODULE_NAME && oldName.getModuleName().equals(qualifiedName.getModuleName())) {
                    qualificationMap.removeQualification(unqualifiedName, sectionCategory);
                    qualificationMap.putQualification(unqualifiedName, newName.getModuleName(), sectionCategory);
                    changesMade = true;
                }
            }
        }
       
        return changesMade;
    }
   
    /** Set status listener to use */
    public void setStatusListener(StatusListener statusListener) {
        this.statusListener = statusListener;
    }
}
TOP

Related Classes of org.openquark.cal.metadata.MetadataRenameUpdater$StatusListener

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