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 e6a96aa  Refactor FileUtils to create new APIs surfacing CopyOption.
e6a96aa is described below

commit e6a96aa94b5d9607c65659e0137687ab3f99dcba
Author: Gary Gregory <gardgreg...@gmail.com>
AuthorDate: Wed Jul 29 18:39:58 2020 -0400

    Refactor FileUtils to create new APIs surfacing CopyOption.
    
    - copyDirectory(File, File, FileFilter, boolean, CopyOption...)
    - copyFile(File, File, boolean, CopyOption...)
    - Sort test methods.
---
 src/main/java/org/apache/commons/io/FileUtils.java | 117 ++++++++++++++++++---
 .../FileUtilsCopyDirectoryToDirectoryTestCase.java |  80 +++++++++-----
 2 files changed, 157 insertions(+), 40 deletions(-)

diff --git a/src/main/java/org/apache/commons/io/FileUtils.java 
b/src/main/java/org/apache/commons/io/FileUtils.java
index 492886c..1586ead 100644
--- a/src/main/java/org/apache/commons/io/FileUtils.java
+++ b/src/main/java/org/apache/commons/io/FileUtils.java
@@ -33,6 +33,7 @@ import java.net.URLConnection;
 import java.nio.ByteBuffer;
 import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
+import java.nio.file.CopyOption;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.StandardCopyOption;
@@ -652,8 +653,62 @@ public class FileUtils {
      * @throws IOException          if an IO error occurs during copying
      * @since 1.4
      */
-    public static void copyDirectory(final File srcDir, final File destDir,
-                                     final FileFilter filter, final boolean 
preserveFileDate) throws IOException {
+    public static void copyDirectory(final File srcDir, final File destDir, 
final FileFilter filter,
+        final boolean preserveFileDate) throws IOException {
+        copyDirectory(srcDir, destDir, filter, preserveFileDate, 
StandardCopyOption.REPLACE_EXISTING);
+    }
+
+    /**
+     * Copies a filtered directory to a new location.
+     * <p>
+     * This method copies the contents of the specified source directory
+     * to within the specified destination directory.
+     * </p>
+     * <p>
+     * The destination directory is created if it does not exist.
+     * If the destination directory did exist, then this method merges
+     * the source with the destination, with the source taking precedence.
+     * </p>
+     * <p>
+     * <strong>Note:</strong> Setting <code>preserveFileDate</code> to
+     * {@code true} tries to preserve the files' last modified
+     * date/times using {@link File#setLastModified(long)}, however it is
+     * not guaranteed that those operations will succeed.
+     * If the modification operation fails, no indication is provided.
+     * </p>
+     * <b>Example: Copy directories only</b>
+     * <pre>
+     *  // only copy the directory structure
+     *  FileUtils.copyDirectory(srcDir, destDir, 
DirectoryFileFilter.DIRECTORY, false);
+     *  </pre>
+     *
+     * <b>Example: Copy directories and txt files</b>
+     * <pre>
+     *  // Create a filter for ".txt" files
+     *  IOFileFilter txtSuffixFilter = 
FileFilterUtils.suffixFileFilter(".txt");
+     *  IOFileFilter txtFiles = 
FileFilterUtils.andFileFilter(FileFileFilter.FILE, txtSuffixFilter);
+     *
+     *  // Create a filter for either directories or ".txt" files
+     *  FileFilter filter = 
FileFilterUtils.orFileFilter(DirectoryFileFilter.DIRECTORY, txtFiles);
+     *
+     *  // Copy using the filter
+     *  FileUtils.copyDirectory(srcDir, destDir, filter, false);
+     *  </pre>
+     *
+     * @param srcDir           an existing directory to copy, must not be 
{@code null}
+     * @param destDir          the new directory, must not be {@code null}
+     * @param filter           the filter to apply, null means copy all 
directories and files
+     * @param preserveFileDate true if the file date of the copy
+     *                         should be the same as the original
+     * @param copyOptions      options specifying how the copy should be done, 
for example {@link StandardCopyOption}.
+     *
+     * @throws NullPointerException if source or destination is {@code null}
+     * @throws IOException          if source or destination is invalid
+     * @throws IOException          if an IO error occurs during copying
+     * @since 2.8.0
+     */
+    public static void copyDirectory(final File srcDir, final File destDir, 
final FileFilter filter,
+        final boolean preserveFileDate, final CopyOption... copyOptions) 
throws IOException {
         checkFileRequirements(srcDir, destDir);
         if (!srcDir.isDirectory()) {
             throw new IOException("Source '" + srcDir + "' exists but is not a 
directory");
@@ -674,7 +729,7 @@ public class FileUtils {
                 }
             }
         }
-        doCopyDirectory(srcDir, destDir, filter, preserveFileDate, 
exclusionList);
+        doCopyDirectory(srcDir, destDir, filter, preserveFileDate, 
exclusionList, copyOptions);
     }
 
     //-----------------------------------------------------------------------
@@ -774,10 +829,43 @@ public class FileUtils {
      * @throws IOException          if the output file length is not the same 
as the input file length after the copy
      * completes
      * @see #copyFileToDirectory(File, File, boolean)
-     * @see #doCopyFile(File, File, boolean)
      */
-    public static void copyFile(final File srcFile, final File destFile,
-                                final boolean preserveFileDate) throws 
IOException {
+    public static void copyFile(final File srcFile, final File destFile, final 
boolean preserveFileDate)
+        throws IOException {
+        copyFile(srcFile, destFile, preserveFileDate, 
StandardCopyOption.REPLACE_EXISTING);
+    }
+
+    /**
+     * Copies a file to a new location.
+     * <p>
+     * This method copies the contents of the specified source file
+     * to the specified destination file.
+     * The directory holding the destination file is created if it does not 
exist.
+     * If the destination file exists, then this method will overwrite it.
+     * </p>
+     * <p>
+     * <strong>Note:</strong> Setting <code>preserveFileDate</code> to
+     * {@code true} tries to preserve the file's last modified
+     * date/times using {@link File#setLastModified(long)}, however it is
+     * not guaranteed that the operation will succeed.
+     * If the modification operation fails, no indication is provided.
+     * </p>
+     *
+     * @param srcFile          an existing file to copy, must not be {@code 
null}
+     * @param destFile         the new file, must not be {@code null}
+     * @param preserveFileDate true if the file date of the copy
+     *                         should be the same as the original
+     * @param copyOptions      options specifying how the copy should be done, 
for example {@link StandardCopyOption}.
+     * @throws NullPointerException if source or destination is {@code null}
+     * @throws IOException          if source or destination is invalid
+     * @throws IOException          if an IO error occurs during copying
+     * @throws IOException          if the output file length is not the same 
as the input file length after the copy
+     * completes
+     * @see #copyFileToDirectory(File, File, boolean)
+     * @since 2.8.0
+     */
+    public static void copyFile(final File srcFile, final File destFile, final 
boolean preserveFileDate, final CopyOption... copyOptions)
+        throws IOException {
         checkFileRequirements(srcFile, destFile);
         if (srcFile.isDirectory()) {
             throw new IOException("Source '" + srcFile + "' exists but is a 
directory");
@@ -794,7 +882,7 @@ public class FileUtils {
         if (destFile.exists() && destFile.canWrite() == false) {
             throw new IOException("Destination '" + destFile + "' exists but 
is read-only");
         }
-        doCopyFile(srcFile, destFile, preserveFileDate);
+        doCopyFile(srcFile, destFile, preserveFileDate, copyOptions);
     }
 
     /**
@@ -1237,13 +1325,13 @@ public class FileUtils {
      * @param filter           the filter to apply, null means copy all 
directories and files
      * @param preserveFileDate whether to preserve the file date
      * @param exclusionList    List of files and directories to exclude from 
the copy, may be null
+     * @param copyOptions      options specifying how the copy should be done, 
for example {@link StandardCopyOption}.
      * @return whether the operation succeeded
      * @throws IOException if an error occurs
-     * @since 1.1
      */
     private static boolean doCopyDirectory(final File srcDir, final File 
destDir, final FileFilter filter,
-                                        final boolean preserveFileDate, final 
List<String> exclusionList)
-            throws IOException {
+        final boolean preserveFileDate, final List<String> exclusionList, 
final CopyOption... copyOptions)
+        throws IOException {
         // recurse
         final File[] srcFiles = filter == null ? srcDir.listFiles() : 
srcDir.listFiles(filter);
         if (srcFiles == null) {  // null if abstract pathname does not denote 
a directory, or if an I/O error occurs
@@ -1265,9 +1353,9 @@ public class FileUtils {
             final File dstFile = new File(destDir, srcFile.getName());
             if (exclusionList == null || 
!exclusionList.contains(srcFile.getCanonicalPath())) {
                 if (srcFile.isDirectory()) {
-                    doCopyDirectory(srcFile, dstFile, filter, 
preserveFileDate, exclusionList);
+                    doCopyDirectory(srcFile, dstFile, filter, 
preserveFileDate, exclusionList, copyOptions);
                 } else {
-                    doCopyFile(srcFile, dstFile, preserveFileDate);
+                    doCopyFile(srcFile, dstFile, preserveFileDate, 
copyOptions);
                 }
             }
         }
@@ -1290,6 +1378,7 @@ public class FileUtils {
      * @param srcFile          the validated source file, must not be {@code 
null}
      * @param destFile         the validated destination file, must not be 
{@code null}
      * @param preserveFileDate whether to preserve the file date
+     * @param copyOptions      options specifying how the copy should be done, 
for example {@link StandardCopyOption}.
      * @return whether the operation succeeded
      * @throws IOException              if an error occurs
      * @throws IOException              if the output file length is not the 
same as the input file length after the
@@ -1297,7 +1386,7 @@ public class FileUtils {
      * @throws IllegalArgumentException "Negative size" if the file is 
truncated so that the size is less than the
      * position
      */
-    private static boolean doCopyFile(final File srcFile, final File destFile, 
final boolean preserveFileDate)
+    private static boolean doCopyFile(final File srcFile, final File destFile, 
final boolean preserveFileDate, CopyOption... copyOptions)
             throws IOException {
         if (destFile.exists() && destFile.isDirectory()) {
             throw new IOException("Destination '" + destFile + "' exists but 
is a directory");
@@ -1306,7 +1395,7 @@ public class FileUtils {
         final Path srcPath = srcFile.toPath();
         final Path destPath = destFile.toPath();
         final long newLastModifed = preserveFileDate ? srcFile.lastModified() 
: destFile.lastModified();
-        Files.copy(srcPath, destPath, StandardCopyOption.REPLACE_EXISTING);
+        Files.copy(srcPath, destPath, copyOptions);
 
         // TODO IO-386: Do we still need this check?
         checkEqualSizes(srcFile, destFile, Files.size(srcPath), 
Files.size(destPath));
diff --git 
a/src/test/java/org/apache/commons/io/FileUtilsCopyDirectoryToDirectoryTestCase.java
 
b/src/test/java/org/apache/commons/io/FileUtilsCopyDirectoryToDirectoryTestCase.java
index 6d8031b..a59c0de 100644
--- 
a/src/test/java/org/apache/commons/io/FileUtilsCopyDirectoryToDirectoryTestCase.java
+++ 
b/src/test/java/org/apache/commons/io/FileUtilsCopyDirectoryToDirectoryTestCase.java
@@ -21,7 +21,11 @@ import static org.junit.jupiter.api.Assertions.fail;
 
 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 java.nio.file.attribute.AclFileAttributeView;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.io.TempDir;
 
@@ -33,17 +37,26 @@ import org.junit.jupiter.api.io.TempDir;
  */
 public class FileUtilsCopyDirectoryToDirectoryTestCase {
 
+    private static void assertExceptionTypeAndMessage(final File srcDir, final 
File destDir,
+        final Class<? extends Exception> expectedExceptionType, final String 
expectedMessage) {
+        try {
+            FileUtils.copyDirectoryToDirectory(srcDir, destDir);
+        } catch (final Exception e) {
+            final String msg = e.getMessage();
+            assertEquals(expectedExceptionType, e.getClass());
+            assertEquals(expectedMessage, msg);
+            return;
+        }
+        fail();
+    }
+
+    /** Temporary folder managed by JUnit. */
     @TempDir
     public File temporaryFolder;
 
-    @Test
-    public void 
copyDirectoryToDirectoryThrowsIllegalExceptionWithCorrectMessageWhenSrcDirIsNotDirectory()
-        throws IOException {
-        final File srcDir = File.createTempFile("notadireotry", null, 
temporaryFolder);
-        final File destDir = new File(temporaryFolder, "destinationDirectory");
-        destDir.mkdirs();
-        final String expectedMessage = String.format("Source '%s' is not a 
directory", srcDir);
-        assertExceptionTypeAndMessage(srcDir, destDir, 
IllegalArgumentException.class, expectedMessage);
+    private void assertAcl(final Path sourcePath, final Path destPath) throws 
IOException {
+        assertEquals(Files.getFileAttributeView(sourcePath, 
AclFileAttributeView.class).getAcl(),
+            Files.getFileAttributeView(destPath, 
AclFileAttributeView.class).getAcl());
     }
 
     @Test
@@ -53,16 +66,18 @@ public class FileUtilsCopyDirectoryToDirectoryTestCase {
         srcDir.mkdir();
         final File destDir = new File(temporaryFolder, "notadirectory");
         destDir.createNewFile();
-        String expectedMessage = String.format("Destination '%s' is not a 
directory", destDir);
+        final String expectedMessage = String.format("Destination '%s' is not 
a directory", destDir);
         assertExceptionTypeAndMessage(srcDir, destDir, 
IllegalArgumentException.class, expectedMessage);
     }
 
     @Test
-    public void 
copyDirectoryToDirectoryThrowsNullPointerExceptionWithCorrectMessageWhenSrcDirIsNull()
 {
-        final File srcDir = null;
-        final File destinationDirectory = new File(temporaryFolder, 
"destinationDirectory");
-        destinationDirectory.mkdir();
-        assertExceptionTypeAndMessage(srcDir, destinationDirectory, 
NullPointerException.class, "sourceDir");
+    public void 
copyDirectoryToDirectoryThrowsIllegalExceptionWithCorrectMessageWhenSrcDirIsNotDirectory()
+        throws IOException {
+        final File srcDir = File.createTempFile("notadireotry", null, 
temporaryFolder);
+        final File destDir = new File(temporaryFolder, "destinationDirectory");
+        destDir.mkdirs();
+        final String expectedMessage = String.format("Source '%s' is not a 
directory", srcDir);
+        assertExceptionTypeAndMessage(srcDir, destDir, 
IllegalArgumentException.class, expectedMessage);
     }
 
     @Test
@@ -73,16 +88,29 @@ public class FileUtilsCopyDirectoryToDirectoryTestCase {
         assertExceptionTypeAndMessage(srcDir, destDir, 
NullPointerException.class, "destinationDir");
     }
 
-    private static void assertExceptionTypeAndMessage(final File srcDir, final 
File destDir,
-        final Class<? extends Exception> expectedExceptionType, final String 
expectedMessage) {
-        try {
-            FileUtils.copyDirectoryToDirectory(srcDir, destDir);
-        } catch (final Exception e) {
-            final String msg = e.getMessage();
-            assertEquals(expectedExceptionType, e.getClass());
-            assertEquals(expectedMessage, msg);
-            return;
-        }
-        fail();
+    @Test
+    public void 
copyDirectoryToDirectoryThrowsNullPointerExceptionWithCorrectMessageWhenSrcDirIsNull()
 {
+        final File srcDir = null;
+        final File destinationDirectory = new File(temporaryFolder, 
"destinationDirectory");
+        destinationDirectory.mkdir();
+        assertExceptionTypeAndMessage(srcDir, destinationDirectory, 
NullPointerException.class, "sourceDir");
     }
+
+    @Test
+    public void copyFileAndCheckAcl() throws IOException {
+        final Path sourcePath = Files.createTempFile("TempOutput", ".bin");
+        final Path destPath = Paths.get(temporaryFolder.getAbsolutePath(), 
"SomeFile.bin");
+        // Test copy attributes without replace FIRST.
+        FileUtils.copyFile(sourcePath.toFile(), destPath.toFile(), true, 
StandardCopyOption.COPY_ATTRIBUTES);
+        assertAcl(sourcePath, destPath);
+        //
+        FileUtils.copyFile(sourcePath.toFile(), destPath.toFile());
+        assertAcl(sourcePath, destPath);
+        //
+        FileUtils.copyFile(sourcePath.toFile(), destPath.toFile(), true, 
StandardCopyOption.REPLACE_EXISTING);
+        assertAcl(sourcePath, destPath);
+        //
+        FileUtils.copyFile(sourcePath.toFile(), destPath.toFile(), true, 
StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);
+        assertAcl(sourcePath, destPath);
+}
 }

Reply via email to