You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2019/11/25 20:01:48 UTC

[commons-io] branch master updated: [IO-645] Add org.apache.commons.io.file.PathUtils.fileContentEquals(Path, Path, OpenOption...)

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 b0a9ab1  [IO-645] Add org.apache.commons.io.file.PathUtils.fileContentEquals(Path, Path, OpenOption...)
b0a9ab1 is described below

commit b0a9ab1b3cd4d295e082b59c2a2450b5b245fc3d
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Mon Nov 25 15:01:44 2019 -0500

    [IO-645] Add
    org.apache.commons.io.file.PathUtils.fileContentEquals(Path, Path,
    OpenOption...)
---
 src/changes/changes.xml                            | 11 ++-
 .../java/org/apache/commons/io/file/PathUtils.java | 95 +++++++++++++++++++++-
 .../io/file/PathUtilsContentEqualsTest.java        | 92 +++++++++++++++++++++
 3 files changed, 193 insertions(+), 5 deletions(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index c709b31..d612be5 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -165,18 +165,21 @@ The <action> type attribute can be add,update,fix,remove.
         Add and reuse org.apache.commons.io.IOUtils.closeQuitely(Closeable, Consumer&lt;IOException&gt;).
         Add and reuse org.apache.commons.io.IOUtils.close(Closeable, IOConsumer&lt;IOException&gt;).
       </action>
-      <action issue="IO-640" dev="ggregory" type="add" due-to="Gary Gregory">
+      <action issue="IO-640" dev="ggregory" type="fix" due-to="Gary Gregory">
         NPE in org.apache.commons.io.IOUtils.contentEquals(InputStream, InputStream) when only one input is null.
       </action>"src/changes/changes.xml"
-      <action issue="IO-641" dev="ggregory" type="add" due-to="Gary Gregory">
+      <action issue="IO-641" dev="ggregory" type="fix" due-to="Gary Gregory">
         NPE in org.apache.commons.io.IOUtils.contentEquals(Reader, Reader) when only one input is null.
       </action>
-      <action issue="IO-643" dev="ggregory" type="add" due-to="Gary Gregory">
+      <action issue="IO-643" dev="ggregory" type="fix" due-to="Gary Gregory">
         NPE in org.apache.commons.io.IOUtils.contentEqualsIgnoreEOL(Reader, Reader) when only one input is null.
       </action>
-      <action issue="IO-644" dev="ggregory" type="add" due-to="Gary Gregory">
+      <action issue="IO-644" dev="ggregory" type="fix" due-to="Gary Gregory">
         NPE in org.apache.commons.io.FileUtils.contentEqualsIgnoreEOL(File, File) when only one input is null.
       </action>
+      <action issue="IO-645" dev="ggregory" type="add" due-to="Gary Gregory">
+        Add org.apache.commons.io.file.PathUtils.fileContentEquals(Path, Path, OpenOption...).
+      </action>
     </release>
 
     <release version="2.6" date="2017-10-15" description="Java 7 required, Java 9 supported.">
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 a6af570..5f3d0e0 100644
--- a/src/main/java/org/apache/commons/io/file/PathUtils.java
+++ b/src/main/java/org/apache/commons/io/file/PathUtils.java
@@ -18,15 +18,19 @@
 package org.apache.commons.io.file;
 
 import java.io.IOException;
+import java.io.InputStream;
 import java.net.URI;
+import java.net.URL;
 import java.nio.file.CopyOption;
 import java.nio.file.DirectoryStream;
 import java.nio.file.FileVisitor;
 import java.nio.file.Files;
 import java.nio.file.NotDirectoryException;
+import java.nio.file.OpenOption;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 
+import org.apache.commons.io.IOUtils;
 import org.apache.commons.io.file.Counters.PathCounters;
 
 /**
@@ -48,6 +52,60 @@ public final class PathUtils {
     }
 
     /**
+     * Compares the contents of two Paths to determine if they are equal or not.
+     * <p>
+     * File content is accessed through {@link Files#newInputStream(Path,OpenOption...)}.
+     * </p>
+     *
+     * @param path1 the first stream.
+     * @param path2 the second stream.
+     * @param options options specifying how the files are opened.
+     * @return true if the content of the streams are equal or they both don't exist, false otherwise.
+     * @throws NullPointerException if either input is null.
+     * @throws IOException if an I/O error occurs.
+     * @see org.apache.commons.io.FileUtils#contentEquals(java.io.File, java.io.File)
+     */
+    public static boolean fileContentEquals(final Path path1, final Path path2, final OpenOption... options) throws IOException {
+        if (path1 == null && path2 == null) {
+            return true;
+        }
+        if (path1 == null ^ path2 == null) {
+            return false;
+        }
+        final Path nPath1 = path1.normalize();
+        final Path nPath2 = path2.normalize();
+        final boolean path1Exists = Files.exists(nPath1);
+        if (path1Exists != Files.exists(nPath2)) {
+            return false;
+        }
+        if (!path1Exists) {
+            // Two not existing files are equal?
+            // Same as FileUtils
+            return true;
+        }
+        if (Files.isDirectory(nPath1)) {
+            // don't compare directory contents.
+            throw new IOException("Can't compare directories, only files: " + nPath1);
+        }
+        if (Files.isDirectory(nPath2)) {
+            // don't compare directory contents.
+            throw new IOException("Can't compare directories, only files: " + nPath2);
+        }
+        if (Files.size(nPath1) != Files.size(nPath2)) {
+            // lengths differ, cannot be equal
+            return false;
+        }
+        if (path1.equals(path2)) {
+            // same file
+            return true;
+        }
+        try (final InputStream inputStream1 = Files.newInputStream(nPath1, options);
+                final InputStream inputStream2 = Files.newInputStream(nPath2, options)) {
+            return IOUtils.contentEquals(inputStream1, inputStream2);
+        }
+    }
+
+    /**
      * Copies a directory to another directory.
      *
      * @param sourceDirectory The source directory.
@@ -66,7 +124,7 @@ public final class PathUtils {
     /**
      * Copies a file to a directory.
      *
-     * @param sourceFile The source file
+     * @param sourceFile The source file.
      * @param targetDirectory The target directory.
      * @param copyOptions Specifies how the copying should be done.
      * @return The target file
@@ -76,7 +134,42 @@ public final class PathUtils {
     public static Path copyFileToDirectory(final Path sourceFile, final Path targetDirectory,
             final CopyOption... copyOptions) throws IOException {
         return Files.copy(sourceFile, targetDirectory.resolve(sourceFile.getFileName()), copyOptions);
+    }
 
+    /**
+     * Copies a URL to a directory.
+     *
+     * @param sourceFile The source URL.
+     * @param targetDirectory The target directory.
+     * @param copyOptions Specifies how the copying should be done.
+     * @return The target file
+     * @throws IOException if an I/O error occurs
+     * @see Files#copy(InputStream, Path, CopyOption...)
+     */
+    public static Path copyFileToDirectory(final URL sourceFile, final Path targetDirectory,
+            final CopyOption... copyOptions) throws IOException {
+        try (final InputStream inputStream = sourceFile.openStream()) {
+            Files.copy(inputStream, targetDirectory.resolve(sourceFile.getFile()), copyOptions);
+            return targetDirectory;
+        }
+    }
+
+    /**
+     * Copies a URL to a directory.
+     *
+     * @param sourceFile The source URL.
+     * @param targetFile The target file.
+     * @param copyOptions Specifies how the copying should be done.
+     * @return The target file
+     * @throws IOException if an I/O error occurs
+     * @see Files#copy(InputStream, Path, CopyOption...)
+     */
+    public static Path copyFile(final URL sourceFile, final Path targetFile,
+            final CopyOption... copyOptions) throws IOException {
+        try (final InputStream inputStream = sourceFile.openStream()) {
+            Files.copy(inputStream, targetFile, copyOptions);
+            return targetFile;
+        }
     }
 
     /**
diff --git a/src/test/java/org/apache/commons/io/file/PathUtilsContentEqualsTest.java b/src/test/java/org/apache/commons/io/file/PathUtilsContentEqualsTest.java
new file mode 100644
index 0000000..7bcba2e
--- /dev/null
+++ b/src/test/java/org/apache/commons/io/file/PathUtilsContentEqualsTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+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 org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+public class PathUtilsContentEqualsTest {
+
+    @TempDir
+    public File temporaryFolder;
+
+    private String getName() {
+        return this.getClass().getSimpleName();
+    }
+
+    @Test
+    public void testFileContentEquals() throws Exception {
+        // Non-existent files
+        final Path path1 = new File(temporaryFolder, getName()).toPath();
+        final Path path2 = new File(temporaryFolder, getName() + "2").toPath();
+        assertTrue(PathUtils.fileContentEquals(null, null));
+        assertFalse(PathUtils.fileContentEquals(null, path1));
+        assertFalse(PathUtils.fileContentEquals(path1, null));
+        // both don't exist
+        assertTrue(PathUtils.fileContentEquals(path1, path1));
+        assertTrue(PathUtils.fileContentEquals(path1, path2));
+        assertTrue(PathUtils.fileContentEquals(path2, path2));
+        assertTrue(PathUtils.fileContentEquals(path2, path1));
+
+        // Directories
+        try {
+            PathUtils.fileContentEquals(temporaryFolder.toPath(), temporaryFolder.toPath());
+            fail("Comparing directories should fail with an IOException");
+        } catch (final IOException ioe) {
+            // expected
+        }
+
+        // Different files
+        final Path objFile1 = Paths.get(temporaryFolder.getAbsolutePath(), getName() + ".object");
+        objFile1.toFile().deleteOnExit();
+        PathUtils.copyFile(getClass().getResource("/java/lang/Object.class"), objFile1);
+
+        final Path objFile1b = Paths.get(temporaryFolder.getAbsolutePath(), getName() + ".object2");
+        objFile1b.toFile().deleteOnExit();
+        PathUtils.copyFile(getClass().getResource("/java/lang/Object.class"), objFile1b);
+
+        final Path objFile2 = Paths.get(temporaryFolder.getAbsolutePath(), getName() + ".collection");
+        objFile2.toFile().deleteOnExit();
+        PathUtils.copyFile(getClass().getResource("/java/util/Collection.class"), objFile2);
+
+        assertFalse(PathUtils.fileContentEquals(objFile1, objFile2));
+        assertFalse(PathUtils.fileContentEquals(objFile1b, objFile2));
+        assertTrue(PathUtils.fileContentEquals(objFile1, objFile1b));
+
+        assertTrue(PathUtils.fileContentEquals(objFile1, objFile1));
+        assertTrue(PathUtils.fileContentEquals(objFile1b, objFile1b));
+        assertTrue(PathUtils.fileContentEquals(objFile2, objFile2));
+
+        // Equal files
+        Files.createFile(path1);
+        Files.createFile(path2);
+        assertTrue(PathUtils.fileContentEquals(path1, path1));
+        assertTrue(PathUtils.fileContentEquals(path1, path2));
+    }
+
+}