This is an automated email from the ASF dual-hosted git repository. ggregory pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-io.git
The following commit(s) were added to refs/heads/master by this push: new abba6c3 [IO-633] Add DeletingFileVisitor. abba6c3 is described below commit abba6c3735d0c8c7930011063739ca1467990317 Author: Gary Gregory <gardgreg...@gmail.com> AuthorDate: Thu Oct 10 15:01:32 2019 -0400 [IO-633] Add DeletingFileVisitor. Also add a test to CountingFileVisitor. --- src/changes/changes.xml | 3 + .../commons/io/file/DeletingFileVisitor.java | 77 +++++++++++ .../commons/io/file/CountingFileVisitorTest.java | 7 +- .../commons/io/file/DeletingFileVisitorTest.java | 144 +++++++++++++++++++++ 4 files changed, 230 insertions(+), 1 deletion(-) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index cdb38e6..87403bd 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -149,6 +149,9 @@ The <action> type attribute can be add,update,fix,remove. <action issue="IO-632" dev="ggregory" type="add" due-to="Gary Gregory"> Add PathUtils for operations on NIO Path. </action> + <action issue="IO-633" dev="ggregory" type="add" due-to="Gary Gregory"> + Add DeletingFileVisitor. + </action> </release> <release version="2.6" date="2017-10-15" description="Java 7 required, Java 9 supported."> diff --git a/src/main/java/org/apache/commons/io/file/DeletingFileVisitor.java b/src/main/java/org/apache/commons/io/file/DeletingFileVisitor.java new file mode 100644 index 0000000..ed5d5e1 --- /dev/null +++ b/src/main/java/org/apache/commons/io/file/DeletingFileVisitor.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.io.file; + +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.Arrays; + +import org.apache.commons.io.PathUtils; + +/** + * Deletes files and directories as a visit proceeds. + * + * @since 2.7 + */ +public class DeletingFileVisitor extends CountingFileVisitor { + + private static final String[] EMPTY_STRING_ARRAY = new String[0]; + private final String[] skip; + + /** + * Constructs a new visitor that deletes files except for the files and directories explicitly given. + * + * @param skip The files to skip deleting. + */ + public DeletingFileVisitor(final String... skip) { + final String[] temp = skip != null ? skip.clone() : EMPTY_STRING_ARRAY; + Arrays.sort(temp); + this.skip = temp; + } + + private boolean accept(final Path path) { + return Arrays.binarySearch(skip, path.getFileName().toString()) < 0; + } + + @Override + public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) throws IOException { + super.postVisitDirectory(dir, exc); + if (PathUtils.isEmptyDirectory(dir)) { + Files.deleteIfExists(dir); + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attrs) throws IOException { + super.preVisitDirectory(dir, attrs); + return accept(dir) ? FileVisitResult.CONTINUE : FileVisitResult.SKIP_SUBTREE; + } + + @Override + public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException { + super.visitFile(file, attrs); + if (accept(file) && Files.exists(file)) { + Files.deleteIfExists(file); + } + return FileVisitResult.CONTINUE; + } +} \ No newline at end of file diff --git a/src/test/java/org/apache/commons/io/file/CountingFileVisitorTest.java b/src/test/java/org/apache/commons/io/file/CountingFileVisitorTest.java index b65e783..cd2c828 100644 --- a/src/test/java/org/apache/commons/io/file/CountingFileVisitorTest.java +++ b/src/test/java/org/apache/commons/io/file/CountingFileVisitorTest.java @@ -43,7 +43,7 @@ public class CountingFileVisitorTest { Assertions.assertEquals(0, visitor.getFileCount()); Assertions.assertEquals(0, visitor.getByteCount()); } finally { - Files.delete(tempDirectory); + Files.deleteIfExists(tempDirectory); } } @@ -82,4 +82,9 @@ public class CountingFileVisitorTest { Assertions.assertEquals(2, visitor.getFileCount()); Assertions.assertEquals(2, visitor.getByteCount()); } + + @Test void testToString() { + // Make sure it does not blow up + new CountingFileVisitor().toString(); + } } diff --git a/src/test/java/org/apache/commons/io/file/DeletingFileVisitorTest.java b/src/test/java/org/apache/commons/io/file/DeletingFileVisitorTest.java new file mode 100644 index 0000000..f9cff34 --- /dev/null +++ b/src/test/java/org/apache/commons/io/file/DeletingFileVisitorTest.java @@ -0,0 +1,144 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.io.file; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.PathUtils; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Tests {@link DeletingFileVisitor}. + */ +public class DeletingFileVisitorTest { + + private Path tempDirectory; + + @AfterEach + public void afterEach() throws IOException { + // backstop + if (Files.exists(tempDirectory) && PathUtils.isEmptyDirectory(tempDirectory)) { + Files.deleteIfExists(tempDirectory); + } + } + + @BeforeEach + public void beforeEach() throws IOException { + tempDirectory = Files.createTempDirectory(getClass().getCanonicalName()); + } + + /** + * Tests an empty folder. + */ + @Test + public void testEmptyDirectory() throws IOException { + testEmptyDirectory(new DeletingFileVisitor()); + // This will throw if not empty. + Files.deleteIfExists(tempDirectory); + } + + private void testEmptyDirectory(final CountingFileVisitor visitor) throws IOException { + Files.walkFileTree(tempDirectory, visitor); + Assertions.assertEquals(1, visitor.getDirectoryCount()); + Assertions.assertEquals(0, visitor.getFileCount()); + Assertions.assertEquals(0, visitor.getByteCount()); + } + + /** + * Tests an empty folder. + */ + @Test + public void testEmptyDirectoryNullCtorArg() throws IOException { + testEmptyDirectory(new DeletingFileVisitor((String[]) null)); + // This will throw if not empty. + Files.deleteIfExists(tempDirectory); + } + + /** + * Tests a directory with one file of size 0. + */ + @Test + public void testFolders1FileSize0() throws IOException { + FileUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-0").toFile(), + tempDirectory.toFile()); + final CountingFileVisitor visitor = new DeletingFileVisitor(); + Files.walkFileTree(tempDirectory, visitor); + Assertions.assertEquals(1, visitor.getDirectoryCount()); + Assertions.assertEquals(1, visitor.getFileCount()); + Assertions.assertEquals(0, visitor.getByteCount()); + // This will throw if not empty. + Files.deleteIfExists(tempDirectory); + } + + /** + * Tests a directory with one file of size 1. + */ + @Test + public void testFolders1FileSize1() throws IOException { + FileUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1").toFile(), + tempDirectory.toFile()); + final CountingFileVisitor visitor = new DeletingFileVisitor(); + Files.walkFileTree(tempDirectory, visitor); + Assertions.assertEquals(1, visitor.getDirectoryCount()); + Assertions.assertEquals(1, visitor.getFileCount()); + Assertions.assertEquals(1, visitor.getByteCount()); + // This will throw if not empty. + Files.deleteIfExists(tempDirectory); + } + + /** + * Tests a directory with one file of size 1 but skip that file. + */ + @Test + public void testFolders1FileSize1Skip() throws IOException { + FileUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1").toFile(), + tempDirectory.toFile()); + final String skipFileName = "file-size-1.bin"; + final CountingFileVisitor visitor = new DeletingFileVisitor(skipFileName); + Files.walkFileTree(tempDirectory, visitor); + Assertions.assertEquals(1, visitor.getDirectoryCount()); + Assertions.assertEquals(1, visitor.getFileCount()); + Assertions.assertEquals(1, visitor.getByteCount()); + final Path skippedFile = tempDirectory.resolve(skipFileName); + Assertions.assertTrue(Files.exists(skippedFile)); + Files.delete(skippedFile); + } + + /** + * Tests a directory with two subdirectorys, each containing one file of size 1. + */ + @Test + public void testFolders2FileSize2() throws IOException { + FileUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-2-file-size-2").toFile(), + tempDirectory.toFile()); + final CountingFileVisitor visitor = new DeletingFileVisitor(); + Files.walkFileTree(tempDirectory, visitor); + Assertions.assertEquals(3, visitor.getDirectoryCount()); + Assertions.assertEquals(2, visitor.getFileCount()); + Assertions.assertEquals(2, visitor.getByteCount()); + // This will throw if not empty. + Files.deleteIfExists(tempDirectory); + } +}