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 2022/06/14 21:34:14 UTC

[commons-io] 02/02: Add PathUtils.touch(Path)

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

commit fd7c8182d2117d01f43ccc9fe939105f834ba672
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Tue Jun 14 17:34:05 2022 -0400

    Add PathUtils.touch(Path)
    
    Refactor
---
 src/changes/changes.xml                            |  3 ++
 src/main/java/org/apache/commons/io/FileUtils.java | 19 ++++-------
 .../java/org/apache/commons/io/file/PathUtils.java | 21 ++++++++++++
 .../java/org/apache/commons/io/FileUtilsTest.java  |  8 ++---
 .../org/apache/commons/io/file/PathUtilsTest.java  | 39 ++++++++++++++++++++++
 5 files changed, 73 insertions(+), 17 deletions(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index c9db4116..31122869 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -368,6 +368,9 @@ The <action> type attribute can be add,update,fix,remove.
       <action dev="ggregory" type="add" due-to="Gary Gregory">
         Reduce boilerplate through new UncheckedIO class and friends in org.apache.commons.io.function.
       </action>
+      <action dev="ggregory" type="add" due-to="Gary Gregory">
+        Add PathUtils.touch(Path).
+      </action>
       <!-- UPDATE -->
       <action dev="kinow" type="update" due-to="Dependabot, Gary Gregory">
         Bump actions/cache from 2.1.6 to 3.0.4 #307, #337.
diff --git a/src/main/java/org/apache/commons/io/FileUtils.java b/src/main/java/org/apache/commons/io/FileUtils.java
index 6bda85a4..c2d8945b 100644
--- a/src/main/java/org/apache/commons/io/FileUtils.java
+++ b/src/main/java/org/apache/commons/io/FileUtils.java
@@ -70,7 +70,6 @@ import org.apache.commons.io.file.Counters;
 import org.apache.commons.io.file.PathFilter;
 import org.apache.commons.io.file.PathUtils;
 import org.apache.commons.io.file.StandardDeleteOption;
-import org.apache.commons.io.file.attribute.FileTimes;
 import org.apache.commons.io.filefilter.FileEqualsFileFilter;
 import org.apache.commons.io.filefilter.FileFileFilter;
 import org.apache.commons.io.filefilter.IOFileFilter;
@@ -3050,25 +3049,19 @@ public class FileUtils {
     }
 
     /**
-     * Implements the same behavior as the "touch" utility on Unix. It creates
-     * a new file with size 0 or, if the file exists already, it is opened and
-     * closed without modifying it, but updating the file date and time.
+     * 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.
      * <p>
-     * NOTE: As from v1.3, this method throws an IOException if the last
-     * modified date of the file cannot be set. Also, as from v1.3 this method
-     * creates parent directories if they do not exist.
+     * NOTE: As from v1.3, this method throws an IOException if the last modified date of the file cannot be set. Also, as
+     * from v1.3 this method creates parent directories if they do not exist.
      * </p>
      *
      * @param file the File to touch.
      * @throws NullPointerException if the parameter is {@code null}.
-     * @throws IOException          if setting the last-modified time failed or an I/O problem occurs.
+     * @throws IOException if setting the last-modified time failed or an I/O problem occurs.
      */
     public static void touch(final File file) throws IOException {
-        Objects.requireNonNull(file, "file");
-        if (!file.exists()) {
-            newOutputStream(file, false).close();
-        }
-        FileTimes.setLastModifiedTime(file.toPath());
+        PathUtils.touch(Objects.requireNonNull(file, "file").toPath());
     }
 
     /**
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 2c56895a..be7e216b 100644
--- a/src/main/java/org/apache/commons/io/file/PathUtils.java
+++ b/src/main/java/org/apache/commons/io/file/PathUtils.java
@@ -71,6 +71,7 @@ import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.io.UncheckedIO;
 import org.apache.commons.io.file.Counters.PathCounters;
+import org.apache.commons.io.file.attribute.FileTimes;
 import org.apache.commons.io.filefilter.IOFileFilter;
 import org.apache.commons.io.function.IOFunction;
 
@@ -1533,6 +1534,26 @@ public final class PathUtils {
         return fileVisitOptions == null ? EnumSet.noneOf(FileVisitOption.class) : Stream.of(fileVisitOptions).collect(Collectors.toSet());
     }
 
+    /**
+     * 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.
+     *
+     * @param file the file to touch.
+     * @return The given file.
+     * @throws NullPointerException if the parameter is {@code null}.
+     * @throws IOException if setting the last-modified time failed or an I/O problem occurs.\
+     * @since 2.12.0
+     */
+    public static Path touch(final Path file) throws IOException {
+        Objects.requireNonNull(file, "file");
+        if (!Files.exists(file)) {
+            Files.createFile(file);
+        } else {
+            FileTimes.setLastModifiedTime(file);
+        }
+        return file;
+    }
+
     /**
      * Performs {@link Files#walkFileTree(Path,FileVisitor)} and returns the given visitor.
      *
diff --git a/src/test/java/org/apache/commons/io/FileUtilsTest.java b/src/test/java/org/apache/commons/io/FileUtilsTest.java
index 2108582b..1d431c14 100644
--- a/src/test/java/org/apache/commons/io/FileUtilsTest.java
+++ b/src/test/java/org/apache/commons/io/FileUtilsTest.java
@@ -2700,10 +2700,10 @@ public class FileUtilsTest extends AbstractTempDirTest {
         assertFalse(file.exists(), "Bad test: test file still exists");
         FileUtils.touch(file);
         assertTrue(file.exists(), "FileUtils.touch() created file");
-        final OutputStream out = Files.newOutputStream(file.toPath());
-        assertEquals(0, file.length(), "Created empty file.");
-        out.write(0);
-        out.close();
+        try (final OutputStream out = Files.newOutputStream(file.toPath())) {
+            assertEquals(0, file.length(), "Created empty file.");
+            out.write(0);
+        }
         assertEquals(1, file.length(), "Wrote one byte to file");
         final long y2k = new GregorianCalendar(2000, 0, 1).getTime().getTime();
         final boolean res = setLastModifiedMillis(file, y2k);  // 0L fails on Win98
diff --git a/src/test/java/org/apache/commons/io/file/PathUtilsTest.java b/src/test/java/org/apache/commons/io/file/PathUtilsTest.java
index 6f53093f..bc76f7fd 100644
--- a/src/test/java/org/apache/commons/io/file/PathUtilsTest.java
+++ b/src/test/java/org/apache/commons/io/file/PathUtilsTest.java
@@ -20,7 +20,9 @@ package org.apache.commons.io.file;
 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
 import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertThrowsExactly;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.jupiter.api.Assumptions.assumeFalse;
@@ -39,11 +41,14 @@ import java.nio.file.LinkOption;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.nio.file.attribute.DosFileAttributeView;
+import java.nio.file.attribute.FileTime;
 import java.nio.file.attribute.PosixFileAttributes;
+import java.util.GregorianCalendar;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 
+import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.filefilter.NameFileFilter;
 import org.apache.commons.io.test.TestUtils;
 import org.apache.commons.lang3.ArrayUtils;
@@ -84,6 +89,10 @@ public class PathUtilsTest extends AbstractTempDirTest {
         return symlinkDir;
     }
 
+    private Long getLastModifiedMillis(final Path file) throws IOException {
+        return Files.getLastModifiedTime(file).toMillis();
+    }
+
     private FileSystem openArchive(final Path p, final boolean createNew) throws IOException {
         if (createNew) {
             final Map<String, String> env = new HashMap<>();
@@ -95,6 +104,10 @@ public class PathUtilsTest extends AbstractTempDirTest {
         return FileSystems.newFileSystem(p, (ClassLoader) null);
     }
 
+    private void setLastModifiedMillis(final Path file, final long millis) throws IOException {
+        Files.setLastModifiedTime(file, FileTime.fromMillis(millis));
+    }
+
     @Test
     public void testCopyDirectoryForDifferentFilesystemsWithAbsolutePath() throws IOException {
         final Path archivePath = Paths.get(TEST_JAR_PATH);
@@ -407,6 +420,32 @@ public class PathUtilsTest extends AbstractTempDirTest {
         PathUtils.deleteFile(resolved);
     }
 
+    @Test
+    public void testTouch() throws IOException {
+        assertThrows(NullPointerException.class, () -> FileUtils.touch(null));
+
+        final Path file = managedTempDirPath.resolve("touch.txt");
+        Files.deleteIfExists(file);
+        assertFalse(Files.exists(file), "Bad test: test file still exists");
+        PathUtils.touch(file);
+        assertTrue(Files.exists(file), "touch() created file");
+        try (final OutputStream out = Files.newOutputStream(file)) {
+            assertEquals(0, Files.size(file), "Created empty file.");
+            out.write(0);
+        }
+        assertEquals(1, Files.size(file), "Wrote one byte to file");
+        final long y2k = new GregorianCalendar(2000, 0, 1).getTime().getTime();
+        setLastModifiedMillis(file, y2k);  // 0L fails on Win98
+        assertEquals(y2k, getLastModifiedMillis(file), "Bad test: set lastModified set incorrect value");
+        final long nowMillis = System.currentTimeMillis();
+        PathUtils.touch(file);
+        assertEquals(1, Files.size(file), "FileUtils.touch() didn't empty the file.");
+        assertNotEquals(y2k, getLastModifiedMillis(file), "FileUtils.touch() changed lastModified");
+        final int delta = 3000;
+        assertTrue(getLastModifiedMillis(file) >= nowMillis - delta, "FileUtils.touch() changed lastModified to more than now-3s");
+        assertTrue(getLastModifiedMillis(file) <= nowMillis + delta, "FileUtils.touch() changed lastModified to less than now+3s");
+    }
+
     @Test
     public void testWriteStringToFile1() throws Exception {
         final Path file = tempDirPath.resolve("write.txt");