/* * Copyright (c) 2014, IETR/INSA of Rennes * 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 the IETR/INSA of Rennes 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. */ package net.sf.orcc.ui.refactoring; import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; import net.sf.orcc.util.OrccUtil; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.ltk.core.refactoring.Change; import org.eclipse.ltk.core.refactoring.CompositeChange; import org.eclipse.ltk.core.refactoring.RefactoringStatus; import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext; import org.eclipse.ltk.core.refactoring.participants.ISharableParticipant; import org.eclipse.ltk.core.refactoring.participants.MoveParticipant; import org.eclipse.ltk.core.refactoring.participants.RefactoringArguments; import org.eclipse.ltk.core.refactoring.resource.MoveResourceChange; /** * This sharable move participant perform all updates necessary when 1 or more * files are moved across packages. * * @author Antoine Lorence * */ public class OrccMoveParticipant extends MoveParticipant implements ISharableParticipant { private final ChangesFactory factory; private IFolder destinationFolder; private IProject originalProject; private final List<IFile> files; public OrccMoveParticipant() { super(); factory = new ChangesFactory(); destinationFolder = null; files = new ArrayList<IFile>(); } /** * Main initialization. Called once per move action, with the first file to * move as argument. * * We use this method to perform some cleans (if this instance is reused * from a previous move), initialize the target folder and register the * first file to store. */ @Override protected boolean initialize(Object element) { files.clear(); factory.clearConfiguration(); factory.resetResults(); final Object dest = getArguments().getDestination(); if (dest instanceof IFolder) { destinationFolder = (IFolder) dest; if (element instanceof IFile) { originalProject = ((IFile) element).getProject(); registerFile((IFile) element); return true; } } return false; } /** * Add another file in the list of files to move. At this point, we are sure * that all the files added are originally contained in the same package. */ @Override public void addElement(Object element, RefactoringArguments arguments) { if (element instanceof IFile) { registerFile((IFile) element); } } @Override public String getName() { return "Orcc Move participant"; } /** * Store the given file to the list of files to move, and register * replacements to apply on other files specific to its type, its name, its * qualified name, etc. * * @param file */ private void registerFile(IFile file) { files.add(file); final String suffix = file.getFileExtension(); if(suffix != null) { if (OrccUtil.CAL_SUFFIX.equals(suffix)) { registerCalUpdates(file); } else if (OrccUtil.NETWORK_SUFFIX.equals(suffix)) { registerNetworksUpdates(file); } } } @Override public RefactoringStatus checkConditions(IProgressMonitor pm, CheckConditionsContext context) throws OperationCanceledException { return new RefactoringStatus(); } /** * <p> * Generates the Change instance whose update all the files that will be * moved in this action. These changes will be applied before really moving * the file. * </p> * * <p> * For CAL files (actor & units), the package information have to be * updated. For diagrams and network, we must ensure if only 1 file is * moved, the other is also. * </p> * * <p> * Note: In some specific case, if a file to move references another file to * move, the updates have to be applied now (before moving files) to avoid * issues in the resulting file * </p> */ @Override public Change createPreChange(IProgressMonitor pm) throws CoreException, OperationCanceledException { final CompositeChange change = new CompositeChange("Pre-move updates"); for (final IFile file : files) { if (OrccUtil.CAL_SUFFIX.equals(file.getFileExtension())) { final String origPackage = OrccUtil.getQualifiedPackage(file); final String newPackage = OrccUtil .getQualifiedPackage(destinationFolder.getFile(file .getName())); final Pattern pattern = Pattern.compile("package(\\s+)" + origPackage + "(\\s*);"); final String replacement = "package$1" + newPackage + "$2;"; factory.addSpecificFileReplacement(file, pattern, replacement); } else if (OrccUtil.NETWORK_SUFFIX.equals(file.getFileExtension())) { final IFile diagFile = file.getProject().getFile( file.getProjectRelativePath().removeFileExtension() .addFileExtension(OrccUtil.DIAGRAM_SUFFIX)); if(diagFile.exists() && !files.contains(diagFile)) { change.add(new MoveResourceChange(diagFile, destinationFolder)); } } else if (OrccUtil.DIAGRAM_SUFFIX.equals(file.getFileExtension())) { final IFile netFile = file.getProject().getFile( file.getProjectRelativePath().removeFileExtension() .addFileExtension(OrccUtil.NETWORK_SUFFIX)); if(netFile.exists() && !files.contains(netFile)) { registerNetworksUpdates(netFile); change.add(new MoveResourceChange(netFile, destinationFolder)); } } } // Specific case, we get the changes for the list of moved files. change.add(factory.getAllChanges(files, "Pre-move updates")); factory.resetResults(); return change.getChildren().length > 0 ? change : null; } /** * Create the change object which will manage changes for all files but the * moved ones. */ @Override public Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException { return factory.getAllChanges(originalProject, "Update depending files", files); } /** * Register updates to perform in files (actors, units, networks and * diagrams) when a CAL file is moved. * * @param file */ private void registerCalUpdates(IFile file) { final IFile destinationFile = destinationFolder.getFile(file.getName()); final String origQualifiedName = OrccUtil.getQualifiedName(file); final String newQualifiedName = OrccUtil .getQualifiedName(destinationFile); final Pattern importPattern = Pattern.compile("import(\\s+)" + origQualifiedName + "(\\.(\\*|\\w+))(\\s*);"); final String replacement = "import$1" + newQualifiedName + "$2$4;"; factory.addReplacement(OrccUtil.CAL_SUFFIX, importPattern, replacement); factory.addReplacement(OrccUtil.NETWORK_SUFFIX, "<Class name=\"" + origQualifiedName + "\"/>", "<Class name=\"" + newQualifiedName + "\"/>"); final IFile irFile = OrccUtil.getFile(file.getProject(), OrccUtil.getQualifiedName(file), OrccUtil.IR_SUFFIX); final String originalRefinement = irFile.getFullPath().toString(); final String originalRelativeFolder = file.getParent() .getProjectRelativePath().removeFirstSegments(1).toString(); final String newRelativeFolder = destinationFolder .getProjectRelativePath().removeFirstSegments(1).toString(); final String newRefinement = originalRefinement.replace( originalRelativeFolder, newRelativeFolder); factory.addReplacement(OrccUtil.DIAGRAM_SUFFIX, "key=\"refinement\" value=\"" + originalRefinement + "\"", "key=\"refinement\" value=\"" + newRefinement + "\""); } /** * Register updates to perform in files (networks and diagrams) when a * network file is moved. * * @param file */ private void registerNetworksUpdates(IFile file) { final IFile destinationFile = destinationFolder.getFile(file.getName()); final IPath newNetworkPath = destinationFile.getFullPath(); final IWorkspaceRoot wpRoot = ResourcesPlugin.getWorkspace().getRoot(); final String oldQualifiedName = OrccUtil.getQualifiedName(file); final String newQualifiedName = OrccUtil.getQualifiedName(wpRoot .getFile(newNetworkPath)); factory.addReplacement(OrccUtil.NETWORK_SUFFIX, "<Class name=\"" + oldQualifiedName + "\"/>", "<Class name=\"" + newQualifiedName + "\"/>"); final String originalRefinement = file.getFullPath().toString(); final String newRefinement = newNetworkPath.toString(); factory.addReplacement(OrccUtil.DIAGRAM_SUFFIX, "key=\"refinement\" value=\"" + originalRefinement + "\"", "key=\"refinement\" value=\"" + newRefinement + "\""); } }