Add PathUtils.touch(Path)
Refactor
MODIFY
src/changes/changes.xml
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.

MODIFY
src/main/java/org/apache/commons/io/FileUtils.java
src/main/java/org/apache/commons/io/FileUtils.java
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,13 +3049,11 @@ private static int toMaxDepth(final boolean recursive) {
}
/**
- * 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.
@@ -3064,11 +3061,7 @@ private static int toMaxDepth(final boolean recursive) {
* @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());
}
/**

MODIFY
src/main/java/org/apache/commons/io/file/PathUtils.java
src/main/java/org/apache/commons/io/file/PathUtils.java
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 static BigInteger sizeOfDirectoryAsBigInteger(final Path directory) throw
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.
*

MODIFY
src/test/java/org/apache/commons/io/FileUtilsTest.java
src/test/java/org/apache/commons/io/FileUtilsTest.java
@@ -2700,10 +2700,10 @@ public void testTouch() throws IOException {
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());
+ try (final OutputStream out = Files.newOutputStream(file.toPath())) {
assertEquals(0, file.length(), "Created empty file.");
out.write(0);
- out.close();
+ }
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

MODIFY
src/test/java/org/apache/commons/io/file/PathUtilsTest.java
src/test/java/org/apache/commons/io/file/PathUtilsTest.java
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;
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 @@ private Path createTempSymlinkedRelativeDir() throws IOException {
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 @@ private FileSystem openArchive(final Path p, final boolean createNew) throws IOE
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 void testSetReadOnlyFile() throws IOException {
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");

Add PathUtils.touch(Path)

Refactor