Skip to content

Commit abba6c3

Browse files
author
Gary Gregory
committed
[IO-633] Add DeletingFileVisitor.
Also add a test to CountingFileVisitor.
1 parent 1d14807 commit abba6c3

4 files changed

Lines changed: 230 additions & 1 deletion

File tree

src/changes/changes.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,9 @@ The <action> type attribute can be add,update,fix,remove.
149149
<action issue="IO-632" dev="ggregory" type="add" due-to="Gary Gregory">
150150
Add PathUtils for operations on NIO Path.
151151
</action>
152+
<action issue="IO-633" dev="ggregory" type="add" due-to="Gary Gregory">
153+
Add DeletingFileVisitor.
154+
</action>
152155
</release>
153156

154157
<release version="2.6" date="2017-10-15" description="Java 7 required, Java 9 supported.">
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.commons.io.file;
19+
20+
import java.io.IOException;
21+
import java.nio.file.FileVisitResult;
22+
import java.nio.file.Files;
23+
import java.nio.file.Path;
24+
import java.nio.file.attribute.BasicFileAttributes;
25+
import java.util.Arrays;
26+
27+
import org.apache.commons.io.PathUtils;
28+
29+
/**
30+
* Deletes files and directories as a visit proceeds.
31+
*
32+
* @since 2.7
33+
*/
34+
public class DeletingFileVisitor extends CountingFileVisitor {
35+
36+
private static final String[] EMPTY_STRING_ARRAY = new String[0];
37+
private final String[] skip;
38+
39+
/**
40+
* Constructs a new visitor that deletes files except for the files and directories explicitly given.
41+
*
42+
* @param skip The files to skip deleting.
43+
*/
44+
public DeletingFileVisitor(final String... skip) {
45+
final String[] temp = skip != null ? skip.clone() : EMPTY_STRING_ARRAY;
46+
Arrays.sort(temp);
47+
this.skip = temp;
48+
}
49+
50+
private boolean accept(final Path path) {
51+
return Arrays.binarySearch(skip, path.getFileName().toString()) < 0;
52+
}
53+
54+
@Override
55+
public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) throws IOException {
56+
super.postVisitDirectory(dir, exc);
57+
if (PathUtils.isEmptyDirectory(dir)) {
58+
Files.deleteIfExists(dir);
59+
}
60+
return FileVisitResult.CONTINUE;
61+
}
62+
63+
@Override
64+
public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attrs) throws IOException {
65+
super.preVisitDirectory(dir, attrs);
66+
return accept(dir) ? FileVisitResult.CONTINUE : FileVisitResult.SKIP_SUBTREE;
67+
}
68+
69+
@Override
70+
public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
71+
super.visitFile(file, attrs);
72+
if (accept(file) && Files.exists(file)) {
73+
Files.deleteIfExists(file);
74+
}
75+
return FileVisitResult.CONTINUE;
76+
}
77+
}

src/test/java/org/apache/commons/io/file/CountingFileVisitorTest.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public void testEmptyFolder() throws IOException {
4343
Assertions.assertEquals(0, visitor.getFileCount());
4444
Assertions.assertEquals(0, visitor.getByteCount());
4545
} finally {
46-
Files.delete(tempDirectory);
46+
Files.deleteIfExists(tempDirectory);
4747
}
4848
}
4949

@@ -82,4 +82,9 @@ public void testFolders2FileSize2() throws IOException {
8282
Assertions.assertEquals(2, visitor.getFileCount());
8383
Assertions.assertEquals(2, visitor.getByteCount());
8484
}
85+
86+
@Test void testToString() {
87+
// Make sure it does not blow up
88+
new CountingFileVisitor().toString();
89+
}
8590
}
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.commons.io.file;
19+
20+
import java.io.IOException;
21+
import java.nio.file.Files;
22+
import java.nio.file.Path;
23+
import java.nio.file.Paths;
24+
25+
import org.apache.commons.io.FileUtils;
26+
import org.apache.commons.io.PathUtils;
27+
import org.junit.jupiter.api.AfterEach;
28+
import org.junit.jupiter.api.Assertions;
29+
import org.junit.jupiter.api.BeforeEach;
30+
import org.junit.jupiter.api.Test;
31+
32+
/**
33+
* Tests {@link DeletingFileVisitor}.
34+
*/
35+
public class DeletingFileVisitorTest {
36+
37+
private Path tempDirectory;
38+
39+
@AfterEach
40+
public void afterEach() throws IOException {
41+
// backstop
42+
if (Files.exists(tempDirectory) && PathUtils.isEmptyDirectory(tempDirectory)) {
43+
Files.deleteIfExists(tempDirectory);
44+
}
45+
}
46+
47+
@BeforeEach
48+
public void beforeEach() throws IOException {
49+
tempDirectory = Files.createTempDirectory(getClass().getCanonicalName());
50+
}
51+
52+
/**
53+
* Tests an empty folder.
54+
*/
55+
@Test
56+
public void testEmptyDirectory() throws IOException {
57+
testEmptyDirectory(new DeletingFileVisitor());
58+
// This will throw if not empty.
59+
Files.deleteIfExists(tempDirectory);
60+
}
61+
62+
private void testEmptyDirectory(final CountingFileVisitor visitor) throws IOException {
63+
Files.walkFileTree(tempDirectory, visitor);
64+
Assertions.assertEquals(1, visitor.getDirectoryCount());
65+
Assertions.assertEquals(0, visitor.getFileCount());
66+
Assertions.assertEquals(0, visitor.getByteCount());
67+
}
68+
69+
/**
70+
* Tests an empty folder.
71+
*/
72+
@Test
73+
public void testEmptyDirectoryNullCtorArg() throws IOException {
74+
testEmptyDirectory(new DeletingFileVisitor((String[]) null));
75+
// This will throw if not empty.
76+
Files.deleteIfExists(tempDirectory);
77+
}
78+
79+
/**
80+
* Tests a directory with one file of size 0.
81+
*/
82+
@Test
83+
public void testFolders1FileSize0() throws IOException {
84+
FileUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-0").toFile(),
85+
tempDirectory.toFile());
86+
final CountingFileVisitor visitor = new DeletingFileVisitor();
87+
Files.walkFileTree(tempDirectory, visitor);
88+
Assertions.assertEquals(1, visitor.getDirectoryCount());
89+
Assertions.assertEquals(1, visitor.getFileCount());
90+
Assertions.assertEquals(0, visitor.getByteCount());
91+
// This will throw if not empty.
92+
Files.deleteIfExists(tempDirectory);
93+
}
94+
95+
/**
96+
* Tests a directory with one file of size 1.
97+
*/
98+
@Test
99+
public void testFolders1FileSize1() throws IOException {
100+
FileUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1").toFile(),
101+
tempDirectory.toFile());
102+
final CountingFileVisitor visitor = new DeletingFileVisitor();
103+
Files.walkFileTree(tempDirectory, visitor);
104+
Assertions.assertEquals(1, visitor.getDirectoryCount());
105+
Assertions.assertEquals(1, visitor.getFileCount());
106+
Assertions.assertEquals(1, visitor.getByteCount());
107+
// This will throw if not empty.
108+
Files.deleteIfExists(tempDirectory);
109+
}
110+
111+
/**
112+
* Tests a directory with one file of size 1 but skip that file.
113+
*/
114+
@Test
115+
public void testFolders1FileSize1Skip() throws IOException {
116+
FileUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1").toFile(),
117+
tempDirectory.toFile());
118+
final String skipFileName = "file-size-1.bin";
119+
final CountingFileVisitor visitor = new DeletingFileVisitor(skipFileName);
120+
Files.walkFileTree(tempDirectory, visitor);
121+
Assertions.assertEquals(1, visitor.getDirectoryCount());
122+
Assertions.assertEquals(1, visitor.getFileCount());
123+
Assertions.assertEquals(1, visitor.getByteCount());
124+
final Path skippedFile = tempDirectory.resolve(skipFileName);
125+
Assertions.assertTrue(Files.exists(skippedFile));
126+
Files.delete(skippedFile);
127+
}
128+
129+
/**
130+
* Tests a directory with two subdirectorys, each containing one file of size 1.
131+
*/
132+
@Test
133+
public void testFolders2FileSize2() throws IOException {
134+
FileUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-2-file-size-2").toFile(),
135+
tempDirectory.toFile());
136+
final CountingFileVisitor visitor = new DeletingFileVisitor();
137+
Files.walkFileTree(tempDirectory, visitor);
138+
Assertions.assertEquals(3, visitor.getDirectoryCount());
139+
Assertions.assertEquals(2, visitor.getFileCount());
140+
Assertions.assertEquals(2, visitor.getByteCount());
141+
// This will throw if not empty.
142+
Files.deleteIfExists(tempDirectory);
143+
}
144+
}

0 commit comments

Comments
 (0)