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 <[email protected]>
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.