/*
* JBoss, Home of Professional Open Source.
*
* See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing.
*
* See the AUTHORS.txt file distributed with this work for a full listing of individual contributors.
*/
package org.teiid.designer.ui.refactor.rename;
import java.util.Collections;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
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.TextFileChange;
import org.eclipse.ltk.core.refactoring.resource.RenameResourceChange;
import org.teiid.designer.core.refactor.IRefactorModelHandler.RefactorType;
import org.teiid.designer.core.refactor.PathPair;
import org.teiid.designer.core.refactor.RelatedResourceFinder.Relationship;
import org.teiid.designer.core.workspace.ModelUtil;
import org.teiid.designer.ui.UiConstants;
import org.teiid.designer.ui.refactor.AbstractResourcesRefactoring;
import org.teiid.designer.ui.refactor.RefactorResourcesUtils;
import org.teiid.designer.ui.refactor.RefactorResourcesUtils.AbstractResourceCallback;
import org.teiid.designer.ui.refactor.RefactorResourcesUtils.IResourceCallback;
/**
* Refactoring for a rename operation
*/
public class RenameResourceRefactoring extends AbstractResourcesRefactoring {
private class RelatedResourceCallback extends AbstractResourceCallback {
private final PathPair pathPair;
/**
* @param pathPair
*/
public RelatedResourceCallback(PathPair pathPair) {
this.pathPair = pathPair;
}
@Override
public void checkValidFile(IFile relatedFile, RefactoringStatus status) {
checkResource(relatedFile, new NullProgressMonitor(), status);
}
@Override
public void indexFile(IResource resource, IFile relatedFile, RefactoringStatus status) throws Exception {
RefactorResourcesUtils.unloadModelResource(relatedFile);
IPath relatedFilePath = ModelUtil.getLocation(relatedFile).makeAbsolute();
IPath relatedParentPath = relatedFilePath.removeLastSegments(1);
// Convert the path pair to a pair of relative paths
PathPair relativePathPair = RefactorResourcesUtils.getRelativePath(relatedParentPath.toOSString(), pathPair);
// Calculate any text changes made as a result of the relative path pair
TextFileChange textFileChange = RefactorResourcesUtils.calculateTextChanges(relatedFile, Collections.singleton(relativePathPair));
RefactorResourcesUtils.calculateModelImportsElementLChanges(relatedFile, relativePathPair, textFileChange);
if (ModelUtil.isModelFile(getResource())) {
// It is reasonable that only if a model file is being renamed will it impact the SQL of related files.
// There is no reason to expect a project or folder to even be include in an SQL statement.
RefactorResourcesUtils.calculateSQLChanges(relatedFile, relativePathPair, textFileChange);
}
if (addTextChange(relatedFile, textFileChange) || getResource() instanceof IContainer) {
/*
* Calculate the effect on any vdbs containing this related file. File has either been
* modified or had its path changed due to the resource being renamed.
*/
RefactorResourcesUtils.calculateRelatedVdbResources(relatedFile, status, this);
}
}
@Override
public void indexVdb(IResource resource, IFile vdbFile, RefactoringStatus status) {
IPath oldResourcePath = resource.getFullPath();
IPath newResourcePath = null;
if (getResource().equals(resource)) {
newResourcePath = oldResourcePath.removeLastSegments(1).append(getNewResourceName());
} else if (getResource().getFullPath().isPrefixOf(oldResourcePath)) {
/*
* resource being renamed is a parent container of this resource
*/
IPath oldRResPath = getResource().getFullPath();
IPath newRResPath = oldRResPath.removeLastSegments(1).append(getNewResourceName());
int segmentCount = oldRResPath.matchingFirstSegments(oldResourcePath);
newResourcePath = oldResourcePath.removeFirstSegments(segmentCount);
newResourcePath = newRResPath.append(newResourcePath);
} else {
/*
* resource being renamed is not on the path of this resource hence
* the old resource path will be also the new resource path
*/
newResourcePath = oldResourcePath;
}
addVdbChange(vdbFile, oldResourcePath, newResourcePath);
}
}
private String newName;
/**
* Create a new instance
*
* @param selectedResource
*/
public RenameResourceRefactoring(final IResource selectedResource) {
super(RefactorResourcesUtils.getString("RenameRefactoring.title"), Collections.singletonList(selectedResource)); //$NON-NLS-1$
}
IResource getResource() {
return getResources().get(0);
}
/**
* Get the new name for the resource
*
* @return name
*/
public String getNewResourceName() {
return newName;
}
/**
* Set the new name for the resource
*
* @param newName
*/
public void setNewResourceName(String newName) {
this.newName = newName;
}
@Override
protected void checkResource(IResource resource, IProgressMonitor progressMonitor, RefactoringStatus status) {
String readOnlyStatusMsg;
if (getResources().contains(resource)) {
readOnlyStatusMsg = RefactorResourcesUtils.getString("ResourcesRefactoring.readOnlyResourceError", //$NON-NLS-1$
resource.getName());
} else {
readOnlyStatusMsg = RefactorResourcesUtils.getString("ResourcesRefactoring.readOnlyRelatedResourceError", //$NON-NLS-1$
resource.getName());
}
RefactorResourcesUtils.checkResourceExists(resource, status);
if (!status.isOK()) return;
RefactorResourcesUtils.checkResourceSynched(resource, status);
if (!status.isOK()) return;
RefactorResourcesUtils.checkResourceWritable(resource, status, IStatus.ERROR, readOnlyStatusMsg);
if (!status.isOK()) return;
RefactorResourcesUtils.checkExtensionManager(resource, RefactorType.MOVE, progressMonitor, status);
if (!status.isOK()) return;
RefactorResourcesUtils.checkModelResourceWritable(resource, status, IStatus.ERROR, readOnlyStatusMsg);
if (!status.isOK()) return;
RefactorResourcesUtils.checkSavedResource(resource, status);
if (!status.isOK()) return;
RefactorResourcesUtils.checkOpenEditors(resource, status);
}
@Override
public RefactoringStatus checkInitialConditions(final IProgressMonitor progressMonitor) throws OperationCanceledException {
RefactoringStatus status = new RefactoringStatus();
try {
progressMonitor.beginTask(RefactorResourcesUtils.getString("RenameRefactoring.initialConditions"), 1); //$NON-NLS-1$
checkResourcesNotEmpty(status);
for (IResource resource : getResources()) {
checkResource(resource, progressMonitor, status);
if (!status.isOK()) break;
// Check validity of related resources
IResourceCallback callback = new AbstractResourceCallback() {
@Override
public void checkValidFile(IFile relatedFile, RefactoringStatus validityStatus) {
checkResource(relatedFile, progressMonitor, validityStatus);
}
};
RefactorResourcesUtils.calculateRelatedResources(resource, status, callback, Relationship.DEPENDENT);
}
} finally {
progressMonitor.done();
}
return status;
}
@Override
public RefactoringStatus checkFinalConditions(IProgressMonitor progressMonitor) throws CoreException, OperationCanceledException {
/* Clear changes in case we are going back then forward in the wizard */
clearChanges();
RefactoringStatus status = new RefactoringStatus();
try {
progressMonitor.beginTask(RefactorResourcesUtils.getString("RenameRefactoring.finalConditions"), 2); //$NON-NLS-1$
for (IResource resource : getResources()) {
IPath location = ModelUtil.getLocation(resource);
IPath absPath = location.makeAbsolute();
IPath parentFolder = absPath.removeLastSegments(1);
IPath destination = parentFolder.append(getNewResourceName());
PathPair absPathPair = new PathPair(absPath.toOSString(), destination.toOSString());
RelatedResourceCallback callback = new RelatedResourceCallback(absPathPair);
if (ModelUtil.isModelFile(resource)) {
// Add change for updating of any SQL contained in the resource
try {
TextFileChange textFileChange = new TextFileChange(resource.getName(), (IFile) resource);
RefactorResourcesUtils.calculateSQLChanges((IFile) resource, absPathPair, textFileChange);
if (textFileChange.getEdit() != null && textFileChange.getEdit().hasChildren()) {
// Only if the related file is actually being changed do we add the text change
// and calculate the effect on any vdbs containing this related file
addChange(resource, textFileChange);
}
RefactorResourcesUtils.calculateRelatedVdbResources(resource, status, callback);
} catch (Exception ex) {
UiConstants.Util.log(ex);
status.merge(RefactoringStatus.createFatalErrorStatus(ex.getMessage()));
}
}
// Add changes for related resources
RefactorResourcesUtils.calculateRelatedResources(resource, status, callback, Relationship.DEPENDENT);
// Add rename change for the resource
addChange(resource, new RenameResourceChange(resource.getFullPath(), getNewResourceName()));
}
}
finally {
progressMonitor.done();
}
return status;
}
@Override
public Change createChange(IProgressMonitor progressMonitor) throws OperationCanceledException, CoreException {
try {
progressMonitor.beginTask(RefactorResourcesUtils.getString("RenameRefactoring.creatingChange"), 1); //$NON-NLS-1$
CompositeChange change = new RenameResourceCompositeChange( getName(),
getChanges().toArray(new Change[0]),
newName,
getResource());
return change;
} catch (Exception ex) {
throw new CoreException(new Status(IStatus.ERROR, RenameResourceDescriptor.REFACTORING_ID, ex.getMessage()));
} finally {
progressMonitor.done();
}
}
}