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 95cb90dab PathUtils.copyFileToDirectory() creates incorrect file names 
when copying between different file systems that use different file system 
separators ("/" versus "\")
95cb90dab is described below

commit 95cb90dab5a3220274fa9dfd4ad4219f9ddba200
Author: Gary Gregory <garydgreg...@gmail.com>
AuthorDate: Sat Apr 5 15:24:07 2025 -0400

    PathUtils.copyFileToDirectory() creates incorrect file names when
    copying between
    different file systems that use different file system separators ("/"
    versus "\")
---
 .../commons/io/file/CopyDirectoryVisitor.java      |  15 +--
 .../java/org/apache/commons/io/file/PathUtils.java | 115 +++++++++++----------
 2 files changed, 62 insertions(+), 68 deletions(-)

diff --git a/src/main/java/org/apache/commons/io/file/CopyDirectoryVisitor.java 
b/src/main/java/org/apache/commons/io/file/CopyDirectoryVisitor.java
index 0fa09f200..2716357b8 100644
--- a/src/main/java/org/apache/commons/io/file/CopyDirectoryVisitor.java
+++ b/src/main/java/org/apache/commons/io/file/CopyDirectoryVisitor.java
@@ -19,7 +19,6 @@
 
 import java.io.IOException;
 import java.nio.file.CopyOption;
-import java.nio.file.FileSystem;
 import java.nio.file.FileVisitResult;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -155,18 +154,6 @@ public FileVisitResult preVisitDirectory(final Path 
directory, final BasicFileAt
         return super.preVisitDirectory(directory, attributes);
     }
 
-    private Path resolve(final Path otherPath) {
-        final FileSystem fileSystemTarget = targetDirectory.getFileSystem();
-        final FileSystem fileSystemSource = sourceDirectory.getFileSystem();
-        if (fileSystemTarget == fileSystemSource) {
-            return targetDirectory.resolve(otherPath);
-        }
-        final String separatorSource = fileSystemSource.getSeparator();
-        final String separatorTarget = fileSystemTarget.getSeparator();
-        final String otherString = otherPath.toString();
-        return targetDirectory.resolve(Objects.equals(separatorSource, 
separatorTarget) ? otherString : otherString.replace(separatorSource, 
separatorTarget));
-    }
-
     /**
      * Relativizes against {@code sourceDirectory}, then resolves against 
{@code targetDirectory}.
      * <p>
@@ -178,7 +165,7 @@ private Path resolve(final Path otherPath) {
      * @return a new path, relativized against sourceDirectory, then resolved 
against targetDirectory.
      */
     private Path resolveRelativeAsString(final Path directory) {
-        return resolve(sourceDirectory.relativize(directory));
+        return PathUtils.resolve(targetDirectory, 
sourceDirectory.relativize(directory));
     }
 
     @Override
diff --git a/src/main/java/org/apache/commons/io/file/PathUtils.java 
b/src/main/java/org/apache/commons/io/file/PathUtils.java
index 3ad5db134..4ea36dd71 100644
--- a/src/main/java/org/apache/commons/io/file/PathUtils.java
+++ b/src/main/java/org/apache/commons/io/file/PathUtils.java
@@ -303,6 +303,44 @@ private static int compareLastModifiedTimeTo(final Path 
file, final FileTime fil
         return getLastModifiedTime(file, options).compareTo(fileTime);
     }
 
+    /**
+     * Compares the files of two FileSystems to determine if they are equal or 
not while considering file contents. The comparison includes all files in all
+     * subdirectories.
+     * <p>
+     * For example, to compare two ZIP files:
+     * </p>
+     *
+     * <pre>
+     * final Path zipPath1 = Paths.get("file1.zip");
+     * final Path zipPath2 = Paths.get("file2.zip");
+     * try (FileSystem fileSystem1 = FileSystems.newFileSystem(zipPath1, 
null); FileSystem fileSystem2 = FileSystems.newFileSystem(zipPath2, null)) {
+     *     assertTrue(PathUtils.directoryAndFileContentEquals(dir1, dir2));
+     * }
+     * </pre>
+     *
+     * @param fileSystem1 The first FileSystem.
+     * @param fileSystem2 The second FileSystem.
+     * @return Whether the two FileSystem contain the same files while 
considering file contents.
+     * @throws IOException if an I/O error is thrown by a visitor method.
+     * @since 2.19.0
+     */
+    public static boolean contentEquals(final FileSystem fileSystem1, final 
FileSystem fileSystem2) throws IOException {
+        if (Objects.equals(fileSystem1, fileSystem2)) {
+            return true;
+        }
+        final List<Path> sortedList1 = 
toSortedList(fileSystem1.getRootDirectories());
+        final List<Path> sortedList2 = 
toSortedList(fileSystem2.getRootDirectories());
+        if (sortedList1.size() != sortedList2.size()) {
+            return false;
+        }
+        for (int i = 0; i < sortedList1.size(); i++) {
+            if (!directoryAndFileContentEquals(sortedList1.get(i), 
sortedList2.get(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     /**
      * Copies the InputStream from the supplier with {@link 
Files#copy(InputStream, Path, CopyOption...)}.
      *
@@ -362,12 +400,7 @@ public static Path copyFile(final URL sourceFile, final 
Path targetFile, final C
     public static Path copyFileToDirectory(final Path sourceFile, final Path 
targetDirectory, final CopyOption... copyOptions) throws IOException {
         // Path.resolve() naturally won't work across FileSystem unless we 
convert to a String
         final Path sourceFileName = 
Objects.requireNonNull(sourceFile.getFileName(), "source file name");
-        final Path targetFile;
-        if (isSameFileSystem(sourceFileName, targetDirectory)) {
-            targetFile = targetDirectory.resolve(sourceFileName);
-        } else {
-            targetFile = targetDirectory.resolve(sourceFileName.toString());
-        }
+        final Path targetFile = resolve(targetDirectory, sourceFileName);
         return Files.copy(sourceFile, targetFile, copyOptions);
     }
 
@@ -667,54 +700,6 @@ public static boolean directoryAndFileContentEquals(final 
Path path1, final Path
         return directoryAndFileContentEquals(path1, path2, 
EMPTY_LINK_OPTION_ARRAY, EMPTY_OPEN_OPTION_ARRAY, 
EMPTY_FILE_VISIT_OPTION_ARRAY);
     }
 
-    /**
-     * Compares the files of two FileSystems to determine if they are equal or 
not while considering file contents. The comparison includes all files in all
-     * subdirectories.
-     * <p>
-     * For example, to compare two ZIP files:
-     * </p>
-     *
-     * <pre>
-     * final Path zipPath1 = Paths.get("file1.zip");
-     * final Path zipPath2 = Paths.get("file2.zip");
-     * try (FileSystem fileSystem1 = FileSystems.newFileSystem(zipPath1, 
null); FileSystem fileSystem2 = FileSystems.newFileSystem(zipPath2, null)) {
-     *     assertTrue(PathUtils.directoryAndFileContentEquals(dir1, dir2));
-     * }
-     * </pre>
-     *
-     * @param fileSystem1 The first FileSystem.
-     * @param fileSystem2 The second FileSystem.
-     * @return Whether the two FileSystem contain the same files while 
considering file contents.
-     * @throws IOException if an I/O error is thrown by a visitor method.
-     * @since 2.19.0
-     */
-    public static boolean contentEquals(final FileSystem fileSystem1, final 
FileSystem fileSystem2) throws IOException {
-        if (Objects.equals(fileSystem1, fileSystem2)) {
-            return true;
-        }
-        final List<Path> sortedList1 = 
toSortedList(fileSystem1.getRootDirectories());
-        final List<Path> sortedList2 = 
toSortedList(fileSystem2.getRootDirectories());
-        if (sortedList1.size() != sortedList2.size()) {
-            return false;
-        }
-        for (int i = 0; i < sortedList1.size(); i++) {
-            if (!directoryAndFileContentEquals(sortedList1.get(i), 
sortedList2.get(i))) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    private static List<Path> toSortedList(final Iterable<Path> 
rootDirectories) {
-        final List<Path> list = toList(rootDirectories);
-        list.sort(Comparator.comparing(Function.identity()));
-        return list;
-    }
-
-    private static <T> List<T> toList(final Iterable<T> iterable) {
-        return StreamSupport.stream(iterable.spliterator(), 
false).collect(Collectors.toList());
-    }
-
     /**
      * Compares the file sets of two Paths to determine if they are equal or 
not while considering file contents. The comparison includes all files in all
      * subdirectories.
@@ -1572,6 +1557,18 @@ private static Path requireExists(final Path file, final 
String fileParamName, f
         return file;
     }
 
+    static Path resolve(final Path targetDirectory, final Path otherPath) {
+        final FileSystem fileSystemTarget = targetDirectory.getFileSystem();
+        final FileSystem fileSystemSource = otherPath.getFileSystem();
+        if (fileSystemTarget == fileSystemSource) {
+            return targetDirectory.resolve(otherPath);
+        }
+        final String separatorSource = fileSystemSource.getSeparator();
+        final String separatorTarget = fileSystemTarget.getSeparator();
+        final String otherString = otherPath.toString();
+        return targetDirectory.resolve(Objects.equals(separatorSource, 
separatorTarget) ? otherString : otherString.replace(separatorSource, 
separatorTarget));
+    }
+
     private static boolean setDosReadOnly(final Path path, final boolean 
readOnly, final LinkOption... linkOptions) throws IOException {
         final DosFileAttributeView dosFileAttributeView = 
getDosFileAttributeView(path, linkOptions);
         if (dosFileAttributeView != null) {
@@ -1800,6 +1797,16 @@ static Set<FileVisitOption> toFileVisitOptionSet(final 
FileVisitOption... fileVi
         return fileVisitOptions == null ? 
EnumSet.noneOf(FileVisitOption.class) : 
Stream.of(fileVisitOptions).collect(Collectors.toSet());
     }
 
+    private static <T> List<T> toList(final Iterable<T> iterable) {
+        return StreamSupport.stream(iterable.spliterator(), 
false).collect(Collectors.toList());
+    }
+
+    private static List<Path> toSortedList(final Iterable<Path> 
rootDirectories) {
+        final List<Path> list = toList(rootDirectories);
+        Collections.sort(list);
+        return list;
+    }
+
     /**
      * Implements behavior similar to the Unix "touch" utility. Creates a new 
file with size 0, or, if the file exists, just updates the file's modified time.
      * this method creates parent directories if they do not exist.

Reply via email to