package org.eclipse.ltk.internal.core.refactoring.resource;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
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.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceVisitor;
import org.eclipse.core.resources.mapping.IResourceChangeDescriptionFactory;
import org.eclipse.core.filebuffers.FileBuffers;
import org.eclipse.core.filebuffers.ITextFileBuffer;
import org.eclipse.core.filebuffers.LocationKind;
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.DeleteArguments;
import org.eclipse.ltk.core.refactoring.participants.DeleteParticipant;
import org.eclipse.ltk.core.refactoring.participants.DeleteProcessor;
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.DeleteResourceChange;
import org.eclipse.ltk.core.refactoring.resource.DeleteResourcesDescriptor;
import org.eclipse.ltk.core.refactoring.resource.Resources;
import org.eclipse.ltk.internal.core.refactoring.BasicElementLabels;
import org.eclipse.ltk.internal.core.refactoring.Messages;
import org.eclipse.ltk.internal.core.refactoring.RefactoringCoreMessages;
public class DeleteResourcesProcessor extends DeleteProcessor {
private IResource[] fResources;
private boolean fDeleteContents;
public DeleteResourcesProcessor(IResource[] resources) {
this(resources, false);
}
public DeleteResourcesProcessor(IResource[] resources, boolean deleteContents) {
fResources= removeDescendants(resources);
fDeleteContents= deleteContents;
}
public IResource[] getResourcesToDelete() {
return fResources;
}
public boolean isDeleteContents() {
return fDeleteContents;
}
public void setDeleteContents(boolean deleteContents) {
fDeleteContents= deleteContents;
}
@Override
public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException, OperationCanceledException {
if (!(Resources.containsOnlyProjects(fResources) || Resources.containsOnlyNonProjects(fResources))) {
return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.DeleteResourcesProcessor_delete_error_mixed_types);
}
return new RefactoringStatus();
}
@Override
public RefactoringStatus checkFinalConditions(IProgressMonitor pm, CheckConditionsContext context) throws CoreException, OperationCanceledException {
pm.beginTask("", 1);
try {
RefactoringStatus result= new RefactoringStatus();
for (IResource resource : fResources) {
if (!isSynchronizedExcludingLinkedResources(resource)) {
String pathLabel= BasicElementLabels.getPathLabel(resource.getFullPath(), false);
String locationLabel= null;
IPath location= resource.getLocation();
if (location != null) {
locationLabel= BasicElementLabels.getPathLabel(location, true);
} else {
URI uri= resource.getLocationURI();
if (uri != null) {
locationLabel= BasicElementLabels.getURLPart(uri.toString());
}
}
String warning;
if (resource instanceof IFile) {
if (locationLabel != null) {
warning= Messages.format(RefactoringCoreMessages.DeleteResourcesProcessor_warning_out_of_sync_file_loc, new Object[] { pathLabel, locationLabel });
} else {
warning= Messages.format(RefactoringCoreMessages.DeleteResourcesProcessor_warning_out_of_sync_file, pathLabel);
}
} else {
if (locationLabel != null) {
warning= Messages.format(RefactoringCoreMessages.DeleteResourcesProcessor_warning_out_of_sync_container_loc, new Object[] { pathLabel, locationLabel });
} else {
warning= Messages.format(RefactoringCoreMessages.DeleteResourcesProcessor_warning_out_of_sync_container, pathLabel);
}
}
result.addWarning(warning);
}
}
checkDirtyResources(result);
ResourceChangeChecker checker= context.getChecker(ResourceChangeChecker.class);
IResourceChangeDescriptionFactory deltaFactory= checker.getDeltaFactory();
for (IResource fResource : fResources) {
if (fResource.isPhantom()) {
result.addFatalError(Messages.format(RefactoringCoreMessages.DeleteResourcesProcessor_delete_error_phantom, BasicElementLabels.getPathLabel(fResource.getFullPath(), false)));
} else if (fDeleteContents && Resources.isReadOnly(fResource)) {
result.addFatalError(Messages.format(RefactoringCoreMessages.DeleteResourcesProcessor_delete_error_read_only, BasicElementLabels.getPathLabel(fResource.getFullPath(), false)));
} else {
deltaFactory.delete(fResource);
}
}
return result;
} finally {
pm.done();
}
}
public boolean isSynchronizedExcludingLinkedResources(IResource resource) throws CoreException {
boolean[] result= { true };
resource.accept(new IResourceVisitor() {
@Override
public boolean visit(IResource visitedResource) throws CoreException {
if (!result[0] || visitedResource.isLinked())
return false;
if (!visitedResource.isSynchronized(IResource.DEPTH_ZERO)) {
result[0]= false;
return false;
}
return true;
}
}, IResource.DEPTH_INFINITE, IContainer.DO_NOT_CHECK_EXISTENCE);
return result[0];
}
private void checkDirtyResources(final RefactoringStatus result) throws CoreException {
for (IResource resource : fResources) {
if (resource instanceof IProject && !((IProject) resource).isOpen())
continue;
resource.accept(new IResourceVisitor() {
@Override
public boolean visit(IResource visitedResource) throws CoreException {
if (visitedResource instanceof IFile) {
checkDirtyFile(result, (IFile)visitedResource);
}
return true;
}
}, IResource.DEPTH_INFINITE, false);
}
}
private void checkDirtyFile(RefactoringStatus result, IFile file) {
if (!file.exists())
return;
ITextFileBuffer buffer= FileBuffers.getTextFileBufferManager().getTextFileBuffer(file.getFullPath(), LocationKind.IFILE);
if (buffer != null && buffer.isDirty()) {
String message= RefactoringCoreMessages.DeleteResourcesProcessor_warning_unsaved_file;
if (buffer.isStateValidated() && buffer.isSynchronized()) {
result.addWarning(Messages.format(message, BasicElementLabels.getPathLabel(file.getFullPath(), false)));
} else {
result.addFatalError(Messages.format(message, BasicElementLabels.getPathLabel(file.getFullPath(), false)));
}
}
}
@Override
public Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException {
pm.beginTask(RefactoringCoreMessages.DeleteResourcesProcessor_create_task, fResources.length);
try {
RefactoringChangeDescriptor descriptor= new RefactoringChangeDescriptor(createDescriptor());
CompositeChange change= new CompositeChange(RefactoringCoreMessages.DeleteResourcesProcessor_change_name);
change.markAsSynthetic();
for (IResource fResource : fResources) {
pm.worked(1);
DeleteResourceChange dc= new DeleteResourceChange(fResource.getFullPath(), true, fDeleteContents);
dc.setDescriptor(descriptor);
change.add(dc);
}
return change;
} finally {
pm.done();
}
}
protected DeleteResourcesDescriptor createDescriptor() {
DeleteResourcesDescriptor descriptor= new DeleteResourcesDescriptor();
descriptor.setProject(null);
descriptor.setDescription(getDeleteDescription());
descriptor.setComment(descriptor.getDescription());
descriptor.setFlags(RefactoringDescriptor.STRUCTURAL_CHANGE | RefactoringDescriptor.MULTI_CHANGE | RefactoringDescriptor.BREAKING_CHANGE);
descriptor.setDeleteContents(fDeleteContents);
descriptor.setResources(fResources);
return descriptor;
}
private String getDeleteDescription() {
if (fResources.length == 1) {
return Messages.format(RefactoringCoreMessages.DeleteResourcesProcessor_description_single, BasicElementLabels.getPathLabel(fResources[0].getFullPath(), false));
}
return Messages.format(RefactoringCoreMessages.DeleteResourcesProcessor_description_multi, Integer.valueOf(fResources.length));
}
@Override
public Object[] getElements() {
return fResources;
}
@Override
public String getIdentifier() {
return "org.eclipse.ltk.core.refactoring.deleteResourcesProcessor";
}
@Override
public String getProcessorName() {
return RefactoringCoreMessages.DeleteResourcesProcessor_processor_name;
}
@Override
public boolean isApplicable() throws CoreException {
for (IResource fResource : fResources) {
if (!canDelete(fResource)) {
return false;
}
}
return true;
}
private boolean canDelete(IResource res) {
return res.isAccessible() && !res.isPhantom();
}
@Override
public RefactoringParticipant[] loadParticipants(RefactoringStatus status, SharableParticipants sharedParticipants) throws CoreException {
final ArrayList<DeleteParticipant> result= new ArrayList<>();
if (!isApplicable()) {
return new RefactoringParticipant[0];
}
final String[] affectedNatures= ResourceProcessors.computeAffectedNatures(fResources);
final DeleteArguments deleteArguments= new DeleteArguments(fDeleteContents);
for (IResource fResource : fResources) {
result.addAll(Arrays.asList(ParticipantManager.loadDeleteParticipants(status, this, fResource, deleteArguments, affectedNatures, sharedParticipants)));
}
return result.toArray(new RefactoringParticipant[result.size()]);
}
private static IResource[] removeDescendants(IResource[] resources) {
ArrayList<IResource> result= new ArrayList<>();
for (IResource resource : resources) {
addToList(result, resource);
}
return result.toArray(new IResource[result.size()]);
}
private static void addToList(ArrayList<IResource> result, IResource curr) {
IPath currPath= curr.getFullPath();
for (int k= result.size() - 1; k >= 0 ; k--) {
IResource other= result.get(k);
IPath otherPath= other.getFullPath();
if (otherPath.isPrefixOf(currPath)) {
return;
}
if (currPath.isPrefixOf(otherPath)) {
result.remove(k);
}
}
result.add(curr);
}
public RefactoringStatus setResources(IResource[] resources, IProgressMonitor pm) throws OperationCanceledException, CoreException {
this.fResources= resources;
return checkInitialConditions(pm);
}
}