[util] Fixing the behavior of the cleanFolder function in Files class.

The previous behavior does not remove the parent folder when its has no
file inside and the "deleteParentFolder" argument is true.
The behavior of the function is updated to delete the parent folder as
expected.
Additionally, the recurive algorithm is replaced by an iterative
algorithm in order to make it more robust.

Signed-off-by: Stéphane Galland <galland@arakhne.org>
This commit is contained in:
Stéphane Galland 2019-10-02 14:57:02 +08:00
parent a3e52c5f37
commit dddfe343ec
2 changed files with 111 additions and 26 deletions

View file

@ -0,0 +1,71 @@
/*******************************************************************************
* Copyright (c) 2019 Universite de Technologie de Belfort-Montbeliard (http://www.utbm.fr) 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
*******************************************************************************/
package org.eclipse.xtext.util;
import static org.junit.Assert.*;
import java.io.File;
import java.io.IOException;
import org.junit.Test;
/**
* @author Stephane Galland - Initial contribution and API
*/
public class FilesTest {
private static File makeFile(File root, String... elements) {
File res = root;
for (final String component : elements) {
if (res == null) {
res = new File(component);
} else {
res = new File(res, component);
}
}
return res;
}
private static void touch(File folder, String filename) throws IOException {
folder.mkdirs();
final File file = new File(folder, filename);
file.createNewFile();
}
private File createTestFolder() throws IOException {
File rootPath = makeFile(null, "build", "tmp", "FilesTest" + (int) (Math.random() * 100000000.));
while (rootPath.exists()) {
rootPath = makeFile(null, "build", "tmp", "FilesTest" + (int) (Math.random() * 100000000.));
}
final File folder1 = makeFile(rootPath, "src", "main", "java");
final File folder2 = makeFile(rootPath, "src", "main", "java", "mypack");
final File folder3 = makeFile(rootPath, "src", "generated-sources", "xtend", "mypack");
final File folder4 = makeFile(rootPath, "target", "classes", "mypack");
touch(folder1, "MyFile.java");
touch(folder2, "MyType.xtend");
touch(folder3, "MyType.java");
touch(folder4, "MyType.class");
touch(rootPath, ".hiddenfile");
return rootPath;
}
@Test
public void cleanFolder_deleteParentTrue() throws Exception {
final File root = createTestFolder();
Files.cleanFolder(root, null, true, true);
assertFalse(root.exists());
}
@Test
public void cleanFolder_deleteParentFalse() throws Exception {
final File root = createTestFolder();
Files.cleanFolder(root, null, true, false);
assertTrue(root.exists());
assertEquals(0, root.listFiles().length);
}
}

View file

@ -15,6 +15,9 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import org.apache.log4j.Logger;
@ -25,6 +28,7 @@ import com.google.common.io.ByteStreams;
/**
* @author Jan Köhnlein - Initial contribution and API
* @author Stephane Galland - fixing cleanFolder behavior
*/
public class Files {
private static Logger log = Logger.getLogger(Files.class);
@ -74,39 +78,54 @@ public class Files {
}
}
/** Clean the content of the the given folder.
*
* @param parentFolder the folder to be cleaned. It must not be {@code null}.
* @param filter a filter for selecting the files to be removed. If it is {@code null}, all the files are removed.
* @param continueOnError indicates if the cleaning should continue after an error occurs.
* @param deleteParentFolder indicates if {@code parentFolder} should be also deleted if it becomes empty.
* @return {@code true} if the cleaning process goes through all the folders and files. {@code false} if the process
* has been stopped before its termination. The value {@code false} could be replied only if the value of
* {@code continueOnError} is {@code false}.
* @throws FileNotFoundException if the given {@code parentFolder} does not exists.
*/
public static boolean cleanFolder(final File parentFolder, final FileFilter filter, boolean continueOnError,
boolean deleteParentFolder) throws FileNotFoundException {
if (!parentFolder.exists()) {
throw new FileNotFoundException(parentFolder.getAbsolutePath());
}
FileFilter myFilter = filter;
if (myFilter == null)
myFilter = new FileFilter() {
@Override
public boolean accept(File pathname) {
return true;
}
};
final FileFilter myFilter = filter == null ? it -> true : filter;
log.debug("Cleaning folder " + parentFolder.toString());
final File[] contents = parentFolder.listFiles(myFilter);
if (contents == null) {
return true;
}
for (int j = 0; j < contents.length; j++) {
final File file = contents[j];
if (file.isDirectory()) {
if (!cleanFolder(file, myFilter, continueOnError, true) && !continueOnError)
return false;
} else {
if (!file.delete()) {
if (contents != null) {
final Deque<File> filesToRemove = new LinkedList<>(Arrays.asList(contents));
while (!filesToRemove.isEmpty()) {
final File file = filesToRemove.pop();
if (file.isDirectory()) {
final File[] children = file.listFiles(myFilter);
if (children != null && children.length > 0) {
// Push back the folder in order to be removed after all its children.
filesToRemove.push(file);
// Push the children in order to be removed before the parent folder.
for (int i = 0; i < children.length; ++i) {
filesToRemove.push(children[i]);
}
} else if (!file.delete()) {
log.error("Couldn't delete " + file.getAbsolutePath());
if (!continueOnError) {
return false;
}
}
} else if (!file.delete()) {
log.error("Couldn't delete " + file.getAbsolutePath());
if (!continueOnError)
if (!continueOnError) {
return false;
}
}
}
}
if (deleteParentFolder) {
String[] children = parentFolder.list();
final String[] children = parentFolder.list();
if (children != null && children.length == 0 && !parentFolder.delete()) {
log.error("Couldn't delete " + parentFolder.getAbsolutePath());
return false;
@ -124,12 +143,7 @@ public class Files {
* @throws FileNotFoundException if folder does not exists
*/
public static boolean sweepFolder(File folder) throws FileNotFoundException {
return Files.cleanFolder(folder, new FileFilter() {
@Override
public boolean accept(File pathname) {
return true;
}
}, true, false);
return cleanFolder(folder, null, true, false);
}
/**