Add PathUtils for operations on NIO Path.
Refactor to add PathUtils.copyDirectory(Path, Path, CopyOption...)
MODIFY
src/main/java/org/apache/commons/io/file/CleaningPathVisitor.java
src/main/java/org/apache/commons/io/file/CleaningPathVisitor.java
@@ -77,17 +77,17 @@ private boolean accept(final Path path) {
}
@Override
- public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attrs) throws IOException {
- super.preVisitDirectory(dir, attrs);
+ public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attributes) throws IOException {
+ super.preVisitDirectory(dir, attributes);
return accept(dir) ? FileVisitResult.CONTINUE : FileVisitResult.SKIP_SUBTREE;
}
@Override
- public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
+ public FileVisitResult visitFile(final Path file, final BasicFileAttributes attributes) throws IOException {
if (accept(file) && Files.exists(file)) {
Files.deleteIfExists(file);
}
- updateFileCounters(file, attrs);
+ updateFileCounters(file, attributes);
return FileVisitResult.CONTINUE;
}
}
\ No newline at end of file

ADD
src/main/java/org/apache/commons/io/file/CopyDirectoryVisitor.java
+/*
+ * 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.CopyOption;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.BasicFileAttributes;
+
+import org.apache.commons.io.file.Counters.PathCounters;
+
+/**
+ * Copies a source directory to a target directory.
+ *
+ * @since 2.7
+ */
+public class CopyDirectoryVisitor extends CountingPathVisitor {
+
+ private static final CopyOption[] EMPTY_COPY_OPTIONS = new CopyOption[0];
+
+ private final CopyOption[] copyOptions;
+ private final Path sourceDirectory;
+ private final Path targetDirectory;
+
+ /**
+ * Constructs a new visitor that deletes files except for the files and directories explicitly given.
+ *
+ * @param pathCounter How to count visits.
+ * @param sourceDirectory The source directory
+ * @param targetDirectory The target directory
+ * @param copyOptions Specifies how the copying should be done.
+ */
+ public CopyDirectoryVisitor(final PathCounters pathCounter, final Path sourceDirectory, final Path targetDirectory,
+ final CopyOption... copyOptions) {
+ super(pathCounter);
+ this.sourceDirectory = sourceDirectory;
+ this.targetDirectory = targetDirectory;
+ this.copyOptions = copyOptions == null ? EMPTY_COPY_OPTIONS : copyOptions.clone();
+ }
+
+ @Override
+ public FileVisitResult preVisitDirectory(final Path directory, final BasicFileAttributes attributes)
+ throws IOException {
+ final Path newTargetDir = targetDirectory.resolve(sourceDirectory.relativize(directory));
+ if (Files.notExists(newTargetDir)) {
+ Files.createDirectory(newTargetDir);
+ }
+ return super.preVisitDirectory(directory, attributes);
+ }
+
+ @Override
+ public FileVisitResult visitFile(final Path sourceFile, final BasicFileAttributes attributes) throws IOException {
+ final Path targetFile = targetDirectory.resolve(sourceDirectory.relativize(sourceFile));
+ Files.copy(sourceFile, targetFile, copyOptions);
+ return super.visitFile(targetFile, attributes);
+ }
+
+}

MODIFY
src/main/java/org/apache/commons/io/file/CountingPathVisitor.java
src/main/java/org/apache/commons/io/file/CountingPathVisitor.java
@@ -89,17 +89,17 @@ public String toString() {
* Updates the counters for visiting the given file.
*
* @param file the visited file.
- * @param attrs the visited file attributes.
+ * @param attributes the visited file attributes.
*/
- protected void updateFileCounters(final Path file, final BasicFileAttributes attrs) {
+ protected void updateFileCounters(final Path file, final BasicFileAttributes attributes) {
pathCounters.getFileCounter().increment();
- pathCounters.getByteCounter().add(attrs.size());
+ pathCounters.getByteCounter().add(attributes.size());
}
@Override
- public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
+ public FileVisitResult visitFile(final Path file, final BasicFileAttributes attributes) throws IOException {
if (Files.exists(file)) {
- updateFileCounters(file, attrs);
+ updateFileCounters(file, attributes);
}
return FileVisitResult.CONTINUE;
}

MODIFY
src/main/java/org/apache/commons/io/file/DeletingPathVisitor.java
src/main/java/org/apache/commons/io/file/DeletingPathVisitor.java
@@ -83,8 +83,7 @@ public FileVisitResult postVisitDirectory(final Path dir, final IOException exc)
if (PathUtils.isEmptyDirectory(dir)) {
Files.deleteIfExists(dir);
}
- super.postVisitDirectory(dir, exc);
- return FileVisitResult.CONTINUE;
+ return super.postVisitDirectory(dir, exc);
}
@Override

MODIFY
src/main/java/org/apache/commons/io/file/PathUtils.java
src/main/java/org/apache/commons/io/file/PathUtils.java
import java.io.IOException;
import java.net.URI;
+import java.nio.file.CopyOption;
import java.nio.file.DirectoryStream;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
* Cleans a directory including sub-directories without deleting directories.
*
* @param directory directory to clean.
- * @return The visitor used to clean the given directory.
+ * @return The visitation path counters.
* @throws IOException if an I/O error is thrown by a visitor method.
*/
public static PathCounters cleanDirectory(final Path directory) throws IOException {
return visitFileTree(CleaningPathVisitor.withLongCounters(), directory).getPathCounters();
}
+ /**
+ * Copies a source directory to a target directory.
+ *
+ * @param sourceDirectory The source directory
+ * @param targetDirectory The target directory
+ * @param copyOptions Specifies how the copying should be done.
+ * @return The visitation path counters.
+ * @throws IOException if an I/O error is thrown by a visitor method.
+ */
+ public static PathCounters copyDirectory(final Path sourceDirectory, final Path targetDirectory,
+ final CopyOption... copyOptions) throws IOException {
+ return visitFileTree(
+ new CopyDirectoryVisitor(Counters.longPathCounters(), sourceDirectory, targetDirectory, copyOptions),
+ sourceDirectory).getPathCounters();
+ }
+
/**
* Counts aspects of a directory including sub-directories.
*

MODIFY
src/test/java/org/apache/commons/io/file/CleaningPathVisitorTest.java
src/test/java/org/apache/commons/io/file/CleaningPathVisitorTest.java
@@ -84,8 +84,7 @@ public void testCleanEmptyDirectoryNullCtorArg(final PathCounters pathCounters)
@ParameterizedTest
@MethodSource("cleaningPathVisitors")
public void testCleanFolders1FileSize0(final CleaningPathVisitor visitor) throws IOException {
- FileUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-0").toFile(),
- tempDir.toFile());
+ PathUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-0"), tempDir);
assertCounts(1, 1, 0, PathUtils.visitFileTree(visitor, tempDir));
}
@@ -95,8 +94,7 @@ public void testCleanFolders1FileSize0(final CleaningPathVisitor visitor) throws
@ParameterizedTest
@MethodSource("cleaningPathVisitors")
public void testCleanFolders1FileSize1(final CleaningPathVisitor visitor) throws IOException {
- FileUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1").toFile(),
- tempDir.toFile());
+ PathUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1"), tempDir);
assertCounts(1, 1, 1, PathUtils.visitFileTree(visitor, tempDir));
}
@@ -106,8 +104,7 @@ public void testCleanFolders1FileSize1(final CleaningPathVisitor visitor) throws
@ParameterizedTest
@MethodSource("pathCounters")
public void testCleanFolders1FileSize1Skip(final PathCounters pathCounters) throws IOException {
- FileUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1").toFile(),
- tempDir.toFile());
+ PathUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1"), tempDir);
final String skipFileName = "file-size-1.bin";
final CountingPathVisitor visitor = new CleaningPathVisitor(pathCounters, skipFileName);
assertCounts(1, 1, 1, PathUtils.visitFileTree(visitor, tempDir));
@@ -122,8 +119,7 @@ public void testCleanFolders1FileSize1Skip(final PathCounters pathCounters) thro
@ParameterizedTest
@MethodSource("cleaningPathVisitors")
public void testCleanFolders2FileSize2(final CleaningPathVisitor visitor) throws IOException {
- FileUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-2-file-size-2").toFile(),
- tempDir.toFile());
+ PathUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-2-file-size-2"), tempDir);
assertCounts(3, 2, 2, PathUtils.visitFileTree(visitor, tempDir));
}
}

ADD
src/test/java/org/apache/commons/io/file/CopyDirectoryVisitorTest.java
+/*
+ * 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 static org.apache.commons.io.file.CounterAssertions.assertCounts;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.file.Counters.PathCounters;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+/**
+ * Tests {@link CountingPathVisitor}.
+ */
+public class CopyDirectoryVisitorTest extends TestArguments {
+
+ private Path targetDir;
+
+ @AfterEach
+ public void afterEach() throws IOException {
+ PathUtils.deleteDirectory(targetDir);
+ }
+
+ @BeforeEach
+ public void beforeEach() throws IOException {
+ targetDir = Files.createTempDirectory(getClass().getCanonicalName() + "-target");
+ }
+
+ /**
+ * Tests an empty folder.
+ */
+ @ParameterizedTest
+ @MethodSource("pathCounters")
+ public void testCopyDirectoryEmptyFolder(final PathCounters pathCounters) throws IOException {
+ final Path sourceDir = Files.createTempDirectory(getClass().getSimpleName());
+ try {
+ assertCounts(1, 0, 0,
+ PathUtils.visitFileTree(new CopyDirectoryVisitor(pathCounters, sourceDir, targetDir, StandardCopyOption.REPLACE_EXISTING), sourceDir));
+ } finally {
+ Files.deleteIfExists(sourceDir);
+ }
+ }
+
+ /**
+ * Tests a directory with one file of size 0.
+ */
+ @ParameterizedTest
+ @MethodSource("pathCounters")
+ public void testCopyDirectoryFolders1FileSize0(final PathCounters pathCounters) throws IOException {
+ final Path sourceDir = Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-0");
+ assertCounts(1, 1, 0, PathUtils.visitFileTree(
+ new CopyDirectoryVisitor(pathCounters, sourceDir, targetDir, StandardCopyOption.REPLACE_EXISTING),
+ sourceDir));
+ }
+
+ /**
+ * Tests a directory with one file of size 1.
+ */
+ @ParameterizedTest
+ @MethodSource("pathCounters")
+ public void testCopyDirectoryFolders1FileSize1(final PathCounters pathCounters) throws IOException {
+ final Path sourceDir = Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1");
+ assertCounts(1, 1, 1, PathUtils.visitFileTree(
+ new CopyDirectoryVisitor(pathCounters, sourceDir, targetDir, StandardCopyOption.REPLACE_EXISTING),
+ sourceDir));
+ }
+
+ /**
+ * Tests a directory with two subdirectorys, each containing one file of size 1.
+ */
+ @ParameterizedTest
+ @MethodSource("pathCounters")
+ public void testCopyDirectoryFolders2FileSize2(final PathCounters pathCounters) throws IOException {
+ final Path sourceDir = Paths.get("src/test/resources/org/apache/commons/io/dirs-2-file-size-2");
+ assertCounts(3, 2, 2, PathUtils.visitFileTree(
+ new CopyDirectoryVisitor(pathCounters, sourceDir, targetDir, StandardCopyOption.REPLACE_EXISTING),
+ sourceDir));
+ }
+
+}

MODIFY
src/test/java/org/apache/commons/io/file/CounterAssertions.java
src/test/java/org/apache/commons/io/file/CounterAssertions.java
class CounterAssertions {
- static void assertCounter(final long expected, final Counter actual) {
- assertEquals(expected, actual.get());
- assertEquals(Long.valueOf(expected), actual.getLong());
- assertEquals(BigInteger.valueOf(expected), actual.getBigInteger());
+ static void assertCounter(final long expected, final Counter actual, String message) {
+ assertEquals(expected, actual.get(), message);
+ assertEquals(Long.valueOf(expected), actual.getLong(), message);
+ assertEquals(BigInteger.valueOf(expected), actual.getBigInteger(), message);
}
- static void assertCounts(final long expectedDirCount, final long expectedFileCount, final long expectedByteCount, final CountingPathVisitor actualVisitor) {
+ static void assertCounts(final long expectedDirCount, final long expectedFileCount, final long expectedByteCount,
+ final CountingPathVisitor actualVisitor) {
assertCounts(expectedDirCount, expectedFileCount, expectedByteCount, actualVisitor.getPathCounters());
}
- static void assertCounts(final long expectedDirCount, final long expectedFileCount, final long expectedByteCount, final PathCounters actualPathCounters) {
- assertCounter(expectedDirCount, actualPathCounters.getDirectoryCounter());
- assertCounter(expectedFileCount, actualPathCounters.getFileCounter());
- assertCounter(expectedByteCount, actualPathCounters.getByteCounter());
+ static void assertCounts(final long expectedDirCount, final long expectedFileCount, final long expectedByteCount,
+ final PathCounters actualPathCounters) {
+ assertCounter(expectedDirCount, actualPathCounters.getDirectoryCounter(), "getDirectoryCounter");
+ assertCounter(expectedFileCount, actualPathCounters.getFileCounter(), "getFileCounter");
+ assertCounter(expectedByteCount, actualPathCounters.getByteCounter(), "getByteCounter");
}
}

MODIFY
src/test/java/org/apache/commons/io/file/CountersTest.java
src/test/java/org/apache/commons/io/file/CountersTest.java
@ParameterizedTest
@MethodSource("numberCounters")
public void testInitialValue(final Counter counter) {
- assertCounter(0, counter);
+ assertCounter(0, counter, "");
}
@ParameterizedTest

MODIFY
src/test/java/org/apache/commons/io/file/DeletingPathVisitorTest.java
src/test/java/org/apache/commons/io/file/DeletingPathVisitorTest.java
@@ -85,8 +85,7 @@ public void testDeleteEmptyDirectoryNullCtorArg(final PathCounters pathCounters)
@ParameterizedTest
@MethodSource("deletingPathVisitors")
public void testDeleteFolders1FileSize0(final DeletingPathVisitor visitor) throws IOException {
- FileUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-0").toFile(),
- tempDir.toFile());
+ PathUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-0"), tempDir);
assertCounts(1, 1, 0, PathUtils.visitFileTree(visitor, tempDir));
// This will throw if not empty.
Files.deleteIfExists(tempDir);
@@ -98,8 +97,7 @@ public void testDeleteFolders1FileSize0(final DeletingPathVisitor visitor) throw
@ParameterizedTest
@MethodSource("deletingPathVisitors")
public void testDeleteFolders1FileSize1(final DeletingPathVisitor visitor) throws IOException {
- FileUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1").toFile(),
- tempDir.toFile());
+ PathUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1"), tempDir);
assertCounts(1, 1, 1, PathUtils.visitFileTree(visitor, tempDir));
// This will throw if not empty.
Files.deleteIfExists(tempDir);
@@ -111,8 +109,7 @@ public void testDeleteFolders1FileSize1(final DeletingPathVisitor visitor) throw
@ParameterizedTest
@MethodSource("pathCounters")
public void testDeleteFolders1FileSize1Skip(final PathCounters pathCounters) throws IOException {
- FileUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1").toFile(),
- tempDir.toFile());
+ PathUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1"), tempDir);
final String skipFileName = "file-size-1.bin";
final CountingPathVisitor visitor = new DeletingPathVisitor(pathCounters, skipFileName);
assertCounts(1, 1, 1, PathUtils.visitFileTree(visitor, tempDir));
@@ -127,8 +124,7 @@ public void testDeleteFolders1FileSize1Skip(final PathCounters pathCounters) thr
@ParameterizedTest
@MethodSource("deletingPathVisitors")
public void testDeleteFolders2FileSize2(final DeletingPathVisitor visitor) throws IOException {
- FileUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-2-file-size-2").toFile(),
- tempDir.toFile());
+ PathUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-2-file-size-2"), tempDir);
assertCounts(3, 2, 2, PathUtils.visitFileTree(visitor, tempDir));
// This will throw if not empty.
Files.deleteIfExists(tempDir);

MODIFY
src/test/java/org/apache/commons/io/file/PathUtilsCleanDirectoryTest.java
src/test/java/org/apache/commons/io/file/PathUtilsCleanDirectoryTest.java
@@ -57,8 +57,7 @@ public void beforeEach() throws IOException {
*/
@Test
public void testCleanDirectory1FileSize0() throws IOException {
- FileUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-0").toFile(),
- tempDir.toFile());
+ PathUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-0"), tempDir);
assertCounts(1, 1, 0, PathUtils.cleanDirectory(tempDir));
}
@@ -67,8 +66,7 @@ public void testCleanDirectory1FileSize0() throws IOException {
*/
@Test
public void testCleanDirectory1FileSize1() throws IOException {
- FileUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1").toFile(),
- tempDir.toFile());
+ PathUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1"), tempDir);
assertCounts(1, 1, 1, PathUtils.cleanDirectory(tempDir));
}
@@ -77,8 +75,7 @@ public void testCleanDirectory1FileSize1() throws IOException {
*/
@Test
public void testCleanDirectory2FileSize2() throws IOException {
- FileUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-2-file-size-2").toFile(),
- tempDir.toFile());
+ PathUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-2-file-size-2"), tempDir);
assertCounts(3, 2, 2, PathUtils.cleanDirectory(tempDir));
}

MODIFY
src/test/java/org/apache/commons/io/file/PathUtilsDeleteDirectoryTest.java
src/test/java/org/apache/commons/io/file/PathUtilsDeleteDirectoryTest.java
@@ -54,8 +54,7 @@ public void beforeEach() throws IOException {
*/
@Test
public void testDeleteDirectory1FileSize0() throws IOException {
- FileUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-0").toFile(),
- tempDir.toFile());
+ PathUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-0"), tempDir);
assertCounts(1, 1, 0, PathUtils.deleteDirectory(tempDir));
// This will throw if not empty.
Files.deleteIfExists(tempDir);
@@ -66,8 +65,7 @@ public void testDeleteDirectory1FileSize0() throws IOException {
*/
@Test
public void testDeleteDirectory1FileSize1() throws IOException {
- FileUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1").toFile(),
- tempDir.toFile());
+ PathUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1"), tempDir);
assertCounts(1, 1, 1, PathUtils.deleteDirectory(tempDir));
// This will throw if not empty.
Files.deleteIfExists(tempDir);
@@ -78,8 +76,7 @@ public void testDeleteDirectory1FileSize1() throws IOException {
*/
@Test
public void testDeleteDirectory2FileSize2() throws IOException {
- FileUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-2-file-size-2").toFile(),
- tempDir.toFile());
+ PathUtils.copyDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-2-file-size-2"), tempDir);
assertCounts(3, 2, 2, PathUtils.deleteDirectory(tempDir));
// This will throw if not empty.
Files.deleteIfExists(tempDir);

[IO-632] Add PathUtils for operations on NIO Path.

Refactor to add PathUtils.copyDirectory(Path, Path, CopyOption...)