Skip to content

Commit

Permalink
Let LocalFile.internalDelete return IStatus #275
Browse files Browse the repository at this point in the history
By returning an IStatus instead of a boolean, the method can better tell
callers what went wrong when deleting. The idea is to replace the
MultiStatus returned by the "delete" method with a more describing
status.
  • Loading branch information
fedejeanne committed Jul 4, 2023
1 parent 326077e commit 6f76904
Showing 1 changed file with 65 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,39 @@
*******************************************************************************/
package org.eclipse.core.internal.filesystem.local;

import java.io.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.nio.file.*;
import org.eclipse.core.filesystem.*;
import java.nio.file.AccessDeniedException;
import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileInfo;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.filesystem.IFileSystem;
import org.eclipse.core.filesystem.URIUtil;
import org.eclipse.core.filesystem.provider.FileInfo;
import org.eclipse.core.filesystem.provider.FileStore;
import org.eclipse.core.internal.filesystem.*;
import org.eclipse.core.runtime.*;
import org.eclipse.core.internal.filesystem.FileStoreUtil;
import org.eclipse.core.internal.filesystem.Messages;
import org.eclipse.core.internal.filesystem.Policy;
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.MultiStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.osgi.util.NLS;

/**
Expand Down Expand Up @@ -134,9 +157,7 @@ public void delete(int options, IProgressMonitor monitor) throws CoreException {
monitor = new InfiniteProgress(monitor);
try {
monitor.beginTask(NLS.bind(Messages.deleting, this), 200);
String message = Messages.deleteProblem;
MultiStatus result = new MultiStatus(Policy.PI_FILE_SYSTEM, EFS.ERROR_DELETE, message, null);
internalDelete(file, filePath, result, monitor);
IStatus result = internalDelete(file, filePath, monitor);
if (!result.isOK())
throw new CoreException(result);
} finally {
Expand Down Expand Up @@ -212,31 +233,34 @@ public int hashCode() {
* the provided status object. The filePath is passed as a parameter
* to optimize java.io.File object creation.
*/
private boolean internalDelete(File target, String pathToDelete, MultiStatus status, IProgressMonitor monitor) {
private IStatus internalDelete(File target, String pathToDelete, IProgressMonitor monitor) {
if (monitor.isCanceled()) {
throw new OperationCanceledException();
}
try {
try {
// First try to delete - this should succeed for files and symbolic links to directories.
Files.deleteIfExists(target.toPath());
return true;
return Status.OK_STATUS;
} catch (AccessDeniedException e) {
// If the file is read only, it can't be deleted via Files.deleteIfExists()
// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=500306
if (target.delete()) {
return true;
return Status.OK_STATUS;
}
throw e;
}
} catch (DirectoryNotEmptyException e) {
// The target is a directory and it's not empty
monitor.subTask(NLS.bind(Messages.deleting, target));
String[] list = target.list();
if (list == null)
list = EMPTY_STRING_ARRAY;
String[] directoryElements = target.list();
if (directoryElements == null)
directoryElements = EMPTY_STRING_ARRAY;
int parentLength = pathToDelete.length();
boolean failedRecursive = false;
for (String element : list) {

// Try to delete all children.
MultiStatus deleteChildrenStatus = new MultiStatus(Policy.PI_FILE_SYSTEM, EFS.ERROR_DELETE, Messages.deleteProblem, null);
for (String element : directoryElements) {
if (monitor.isCanceled()) {
throw new OperationCanceledException();
}
Expand All @@ -246,36 +270,44 @@ private boolean internalDelete(File target, String pathToDelete, MultiStatus sta
childBuffer.append(File.separatorChar);
childBuffer.append(element);
String childName = childBuffer.toString();
// Try best effort on all children so put logical OR at end.
failedRecursive = !internalDelete(new java.io.File(childName), childName, status, monitor) || failedRecursive;

deleteChildrenStatus.add(internalDelete(new java.io.File(childName), childName, monitor));

monitor.worked(1);
}

// Abort if one of the children couldn't be deleted.
if (!deleteChildrenStatus.isOK())
return deleteChildrenStatus;

// Try to delete the root directory
try {
// Don't try to delete the root if one of the children failed.
if (!failedRecursive && Files.deleteIfExists(target.toPath()))
return true;
if (Files.deleteIfExists(target.toPath()))
return Status.OK_STATUS;
} catch (Exception e1) {
// We caught a runtime exception so log it.
String message = NLS.bind(Messages.couldnotDelete, target.getAbsolutePath());
status.add(new Status(IStatus.ERROR, Policy.PI_FILE_SYSTEM, EFS.ERROR_DELETE, message, e1));
return false;
return createErrorStatus(target, Messages.couldnotDelete, e1);
}

// If we got this far, we failed.
String message = null;
if (fetchInfo().getAttribute(EFS.ATTRIBUTE_READ_ONLY)) {
message = NLS.bind(Messages.couldnotDeleteReadOnly, target.getAbsolutePath());
} else {
message = NLS.bind(Messages.couldnotDelete, target.getAbsolutePath());
return createErrorStatus(target, Messages.couldnotDeleteReadOnly, null);
}
status.add(new Status(IStatus.ERROR, Policy.PI_FILE_SYSTEM, EFS.ERROR_DELETE, message, null));
return false;

// This is the worst-case scenario: something failed but we don't know what. The children were
// deleted successfully and the directory is NOT read-only... there's nothing else to report.
return createErrorStatus(target, Messages.couldnotDelete, null);

} catch (IOException e) {
String message = NLS.bind(Messages.couldnotDelete, target.getAbsolutePath());
status.add(new Status(IStatus.ERROR, Policy.PI_FILE_SYSTEM, EFS.ERROR_DELETE, message, e));
return false;
return createErrorStatus(target, Messages.couldnotDelete, e);
}
}

private IStatus createErrorStatus(File target, String msg, Exception e) {
String message = NLS.bind(msg, target.getAbsolutePath());
return new Status(IStatus.ERROR, Policy.PI_FILE_SYSTEM, EFS.ERROR_DELETE, message, e);
}

@Override
public boolean isParentOf(IFileStore other) {
if (!(other instanceof LocalFile))
Expand Down

0 comments on commit 6f76904

Please sign in to comment.