/*******************************************************************************
* Copyright (c) 2007, 2008 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.ltk.internal.core.refactoring.resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.mapping.IResourceChangeDescriptionFactory;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.CompositeChange;
import org.eclipse.ltk.core.refactoring.RefactoringChangeDescriptor;
import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext;
import org.eclipse.ltk.core.refactoring.participants.MoveArguments;
import org.eclipse.ltk.core.refactoring.participants.MoveParticipant;
import org.eclipse.ltk.core.refactoring.participants.MoveProcessor;
import org.eclipse.ltk.core.refactoring.participants.ParticipantManager;
import org.eclipse.ltk.core.refactoring.participants.RefactoringParticipant;
import org.eclipse.ltk.core.refactoring.participants.ResourceChangeChecker;
import org.eclipse.ltk.core.refactoring.participants.SharableParticipants;
import org.eclipse.ltk.core.refactoring.resource.MoveResourceChange;
import org.eclipse.ltk.core.refactoring.resource.MoveResourcesDescriptor;
import org.eclipse.ltk.internal.core.refactoring.BasicElementLabels;
import org.eclipse.ltk.internal.core.refactoring.Messages;
import org.eclipse.ltk.internal.core.refactoring.RefactoringCoreMessages;
import org.eclipse.ltk.internal.core.refactoring.Resources;
/**
* A move processor for {@link IResource resources}. The processor will move the resources and
* load move participants if references should be move as well.
*
* @since 3.4
*/
public class MoveResourcesProcessor extends MoveProcessor {
private final IResource[] fResourcesToMove;
private IContainer fDestination;
private boolean fUpdateReferences;
private MoveArguments fMoveArguments; // set after checkFinalConditions
/**
* Creates a new move resource processor.
*
* @param resourcesToMove the resources to move
*/
public MoveResourcesProcessor(IResource[] resourcesToMove) {
if (resourcesToMove == null) {
throw new IllegalArgumentException("resources must not be null"); //$NON-NLS-1$
}
fResourcesToMove= resourcesToMove;
fDestination= null;
fUpdateReferences= true;
}
/**
* Returns the resources to move.
*
* @return the resources to move.
*/
public IResource[] getResourcesToMove() {
return fResourcesToMove;
}
/**
* Sets the move destination
*
* @param destination the move destination
*/
public void setDestination(IContainer destination) {
Assert.isNotNull(destination);
fDestination= destination;
}
/**
* Returns <code>true</code> if the refactoring processor also updates references
*
* @return <code>true</code> if the refactoring processor also updates references
*/
public boolean isUpdateReferences() {
return fUpdateReferences;
}
/**
* Specifies if the refactoring processor also updates references. The default behavior is to update references.
*
* @param updateReferences <code>true</code> if the refactoring processor should also updates references
*/
public void setUpdateReferences(boolean updateReferences) {
fUpdateReferences= updateReferences;
}
/* (non-Javadoc)
* @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#checkInitialConditions(org.eclipse.core.runtime.IProgressMonitor)
*/
public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException {
RefactoringStatus result= new RefactoringStatus();
result.merge(RefactoringStatus.create(Resources.checkInSync(fResourcesToMove)));
return result;
}
/* (non-Javadoc)
* @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#checkFinalConditions(org.eclipse.core.runtime.IProgressMonitor, org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext)
*/
public RefactoringStatus checkFinalConditions(IProgressMonitor pm, CheckConditionsContext context) throws CoreException {
pm.beginTask("", 1); //$NON-NLS-1$
try {
RefactoringStatus status= validateDestination(fDestination);
if (status.hasFatalError()) {
return status;
}
fMoveArguments= new MoveArguments(fDestination, isUpdateReferences());
ResourceChangeChecker checker= (ResourceChangeChecker) context.getChecker(ResourceChangeChecker.class);
IResourceChangeDescriptionFactory deltaFactory= checker.getDeltaFactory();
for (int i= 0; i < fResourcesToMove.length; i++) {
IResource resource= fResourcesToMove[i];
IResource newResource= fDestination.findMember(resource.getName());
if (newResource != null) {
status.addWarning(Messages.format(RefactoringCoreMessages.MoveResourcesProcessor_warning_destination_already_exists, BasicElementLabels.getPathLabel(newResource.getFullPath(), false)));
deltaFactory.delete(newResource);
}
ResourceModifications.buildMoveDelta(deltaFactory, fResourcesToMove[i], fMoveArguments);
}
return status;
} finally {
pm.done();
}
}
/**
* Validates if the a destination is valid. This method does not change the destination settings on the refactoring. It is intended to be used
* in a wizard to validate user input.
*
* @param destination the destination to validate
* @return returns the resulting status of the validation
*/
public RefactoringStatus validateDestination(IContainer destination) {
Assert.isNotNull(destination, "container is null"); //$NON-NLS-1$
if (destination instanceof IWorkspaceRoot)
return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.MoveResourceProcessor_error_invalid_destination);
if (!destination.exists()) {
return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.MoveResourceProcessor_error_destination_not_exists);
}
IPath destinationPath= destination.getFullPath();
for (int i= 0; i < fResourcesToMove.length; i++) {
IPath path= fResourcesToMove[i].getFullPath();
if (path.isPrefixOf(destinationPath) || path.equals(destinationPath)) {
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.MoveResourceProcessor_destination_inside_moved, BasicElementLabels.getPathLabel(path, false)));
}
if (path.removeLastSegments(1).equals(destinationPath)) {
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.MoveResourceProcessor_destination_same_as_moved, BasicElementLabels.getPathLabel(path, false)));
}
}
return new RefactoringStatus();
}
private String getMoveDescription() {
if (fResourcesToMove.length == 1) {
return Messages.format(RefactoringCoreMessages.MoveResourceProcessor_description_multiple, new Object[] { new Integer(fResourcesToMove.length), BasicElementLabels.getResourceName(fDestination) });
} else {
return Messages.format(RefactoringCoreMessages.MoveResourceProcessor_description_single, new String[] { BasicElementLabels.getResourceName(fResourcesToMove[0]), BasicElementLabels.getResourceName(fDestination) });
}
}
protected MoveResourcesDescriptor createDescriptor() {
MoveResourcesDescriptor descriptor= new MoveResourcesDescriptor();
descriptor.setProject(fDestination.getProject().getName());
descriptor.setDescription(getMoveDescription());
if (fResourcesToMove.length == 1) {
descriptor.setComment(descriptor.getDescription());
} else {
StringBuffer buf= new StringBuffer();
for (int i= 0; i < fResourcesToMove.length; i++) {
if (i > 0)
buf.append(", "); //$NON-NLS-1$
buf.append(fResourcesToMove[i].getName());
}
descriptor.setComment(Messages.format(RefactoringCoreMessages.MoveResourceProcessor_comment, new String[] { BasicElementLabels.getResourceName(fResourcesToMove[0]), BasicElementLabels.getResourceName(fDestination) }));
}
descriptor.setFlags(RefactoringDescriptor.STRUCTURAL_CHANGE | RefactoringDescriptor.MULTI_CHANGE | RefactoringDescriptor.BREAKING_CHANGE);
descriptor.setDestination(fDestination);
descriptor.setUpdateReferences(isUpdateReferences());
descriptor.setResourcesToMove(fResourcesToMove);
return descriptor;
}
/* (non-Javadoc)
* @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#createChange(org.eclipse.core.runtime.IProgressMonitor)
*/
public Change createChange(IProgressMonitor pm) throws CoreException {
pm.beginTask("", fResourcesToMove.length); //$NON-NLS-1$
try {
CompositeChange compositeChange= new CompositeChange(getMoveDescription());
compositeChange.markAsSynthetic();
RefactoringChangeDescriptor descriptor= new RefactoringChangeDescriptor(createDescriptor());
for (int i= 0; i < fResourcesToMove.length; i++) {
MoveResourceChange moveChange= new MoveResourceChange(fResourcesToMove[i], fDestination);
moveChange.setDescriptor(descriptor);
compositeChange.add(moveChange);
}
return compositeChange;
} finally {
pm.done();
}
}
/* (non-Javadoc)
* @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#getElements()
*/
public Object[] getElements() {
return fResourcesToMove;
}
/* (non-Javadoc)
* @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#getIdentifier()
*/
public String getIdentifier() {
return "org.eclipse.ltk.core.refactoring.moveResourcesProcessor"; //$NON-NLS-1$
}
/* (non-Javadoc)
* @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#getProcessorName()
*/
public String getProcessorName() {
return RefactoringCoreMessages.MoveResourceProcessor_processor_name;
}
/* (non-Javadoc)
* @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#isApplicable()
*/
public boolean isApplicable() {
for (int i= 0; i < fResourcesToMove.length; i++) {
if (!canMove(fResourcesToMove[i])) {
return false;
}
}
return true;
}
private static boolean canMove(IResource res) {
return (res instanceof IFile || res instanceof IFolder) && res.exists() && !res.isPhantom();
}
/* (non-Javadoc)
* @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#loadParticipants(org.eclipse.ltk.core.refactoring.RefactoringStatus, org.eclipse.ltk.core.refactoring.participants.SharableParticipants)
*/
public RefactoringParticipant[] loadParticipants(RefactoringStatus status, SharableParticipants shared) throws CoreException {
String[] affectedNatures= ResourceProcessors.computeAffectedNatures(fResourcesToMove);
List result= new ArrayList();
for (int i= 0; i < fResourcesToMove.length; i++) {
MoveParticipant[] participants= ParticipantManager.loadMoveParticipants(status, this, fResourcesToMove[i], fMoveArguments, null, affectedNatures, shared);
result.addAll(Arrays.asList(participants));
}
return (RefactoringParticipant[]) result.toArray(new RefactoringParticipant[result.size()]);
}
}