Skip to content

Commit fd7c818

Browse files
author
Gary Gregory
committed
Add PathUtils.touch(Path)
Refactor
1 parent cdc97d8 commit fd7c818

5 files changed

Lines changed: 73 additions & 17 deletions

File tree

src/changes/changes.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,9 @@ The <action> type attribute can be add,update,fix,remove.
368368
<action dev="ggregory" type="add" due-to="Gary Gregory">
369369
Reduce boilerplate through new UncheckedIO class and friends in org.apache.commons.io.function.
370370
</action>
371+
<action dev="ggregory" type="add" due-to="Gary Gregory">
372+
Add PathUtils.touch(Path).
373+
</action>
371374
<!-- UPDATE -->
372375
<action dev="kinow" type="update" due-to="Dependabot, Gary Gregory">
373376
Bump actions/cache from 2.1.6 to 3.0.4 #307, #337.

src/main/java/org/apache/commons/io/FileUtils.java

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@
7070
import org.apache.commons.io.file.PathFilter;
7171
import org.apache.commons.io.file.PathUtils;
7272
import org.apache.commons.io.file.StandardDeleteOption;
73-
import org.apache.commons.io.file.attribute.FileTimes;
7473
import org.apache.commons.io.filefilter.FileEqualsFileFilter;
7574
import org.apache.commons.io.filefilter.FileFileFilter;
7675
import org.apache.commons.io.filefilter.IOFileFilter;
@@ -3050,25 +3049,19 @@ private static String[] toSuffixes(final String... extensions) {
30503049
}
30513050

30523051
/**
3053-
* Implements the same behavior as the "touch" utility on Unix. It creates
3054-
* a new file with size 0 or, if the file exists already, it is opened and
3055-
* closed without modifying it, but updating the file date and time.
3052+
* Implements behavior similar to the Unix "touch" utility. Creates a new file with size 0, or, if the file exists, just
3053+
* updates the file's modified time.
30563054
* <p>
3057-
* NOTE: As from v1.3, this method throws an IOException if the last
3058-
* modified date of the file cannot be set. Also, as from v1.3 this method
3059-
* creates parent directories if they do not exist.
3055+
* NOTE: As from v1.3, this method throws an IOException if the last modified date of the file cannot be set. Also, as
3056+
* from v1.3 this method creates parent directories if they do not exist.
30603057
* </p>
30613058
*
30623059
* @param file the File to touch.
30633060
* @throws NullPointerException if the parameter is {@code null}.
3064-
* @throws IOException if setting the last-modified time failed or an I/O problem occurs.
3061+
* @throws IOException if setting the last-modified time failed or an I/O problem occurs.
30653062
*/
30663063
public static void touch(final File file) throws IOException {
3067-
Objects.requireNonNull(file, "file");
3068-
if (!file.exists()) {
3069-
newOutputStream(file, false).close();
3070-
}
3071-
FileTimes.setLastModifiedTime(file.toPath());
3064+
PathUtils.touch(Objects.requireNonNull(file, "file").toPath());
30723065
}
30733066

30743067
/**

src/main/java/org/apache/commons/io/file/PathUtils.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
import org.apache.commons.io.IOUtils;
7272
import org.apache.commons.io.UncheckedIO;
7373
import org.apache.commons.io.file.Counters.PathCounters;
74+
import org.apache.commons.io.file.attribute.FileTimes;
7475
import org.apache.commons.io.filefilter.IOFileFilter;
7576
import org.apache.commons.io.function.IOFunction;
7677

@@ -1533,6 +1534,26 @@ static Set<FileVisitOption> toFileVisitOptionSet(final FileVisitOption... fileVi
15331534
return fileVisitOptions == null ? EnumSet.noneOf(FileVisitOption.class) : Stream.of(fileVisitOptions).collect(Collectors.toSet());
15341535
}
15351536

1537+
/**
1538+
* Implements behavior similar to the Unix "touch" utility. Creates a new file with size 0, or, if the file exists, just
1539+
* updates the file's modified time.
1540+
*
1541+
* @param file the file to touch.
1542+
* @return The given file.
1543+
* @throws NullPointerException if the parameter is {@code null}.
1544+
* @throws IOException if setting the last-modified time failed or an I/O problem occurs.\
1545+
* @since 2.12.0
1546+
*/
1547+
public static Path touch(final Path file) throws IOException {
1548+
Objects.requireNonNull(file, "file");
1549+
if (!Files.exists(file)) {
1550+
Files.createFile(file);
1551+
} else {
1552+
FileTimes.setLastModifiedTime(file);
1553+
}
1554+
return file;
1555+
}
1556+
15361557
/**
15371558
* Performs {@link Files#walkFileTree(Path,FileVisitor)} and returns the given visitor.
15381559
*

src/test/java/org/apache/commons/io/FileUtilsTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2700,10 +2700,10 @@ public void testTouch() throws IOException {
27002700
assertFalse(file.exists(), "Bad test: test file still exists");
27012701
FileUtils.touch(file);
27022702
assertTrue(file.exists(), "FileUtils.touch() created file");
2703-
final OutputStream out = Files.newOutputStream(file.toPath());
2704-
assertEquals(0, file.length(), "Created empty file.");
2705-
out.write(0);
2706-
out.close();
2703+
try (final OutputStream out = Files.newOutputStream(file.toPath())) {
2704+
assertEquals(0, file.length(), "Created empty file.");
2705+
out.write(0);
2706+
}
27072707
assertEquals(1, file.length(), "Wrote one byte to file");
27082708
final long y2k = new GregorianCalendar(2000, 0, 1).getTime().getTime();
27092709
final boolean res = setLastModifiedMillis(file, y2k); // 0L fails on Win98

src/test/java/org/apache/commons/io/file/PathUtilsTest.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
2121
import static org.junit.jupiter.api.Assertions.assertEquals;
2222
import static org.junit.jupiter.api.Assertions.assertFalse;
23+
import static org.junit.jupiter.api.Assertions.assertNotEquals;
2324
import static org.junit.jupiter.api.Assertions.assertNull;
25+
import static org.junit.jupiter.api.Assertions.assertThrows;
2426
import static org.junit.jupiter.api.Assertions.assertThrowsExactly;
2527
import static org.junit.jupiter.api.Assertions.assertTrue;
2628
import static org.junit.jupiter.api.Assumptions.assumeFalse;
@@ -39,11 +41,14 @@
3941
import java.nio.file.Path;
4042
import java.nio.file.Paths;
4143
import java.nio.file.attribute.DosFileAttributeView;
44+
import java.nio.file.attribute.FileTime;
4245
import java.nio.file.attribute.PosixFileAttributes;
46+
import java.util.GregorianCalendar;
4347
import java.util.HashMap;
4448
import java.util.Iterator;
4549
import java.util.Map;
4650

51+
import org.apache.commons.io.FileUtils;
4752
import org.apache.commons.io.filefilter.NameFileFilter;
4853
import org.apache.commons.io.test.TestUtils;
4954
import org.apache.commons.lang3.ArrayUtils;
@@ -84,6 +89,10 @@ private Path createTempSymlinkedRelativeDir() throws IOException {
8489
return symlinkDir;
8590
}
8691

92+
private Long getLastModifiedMillis(final Path file) throws IOException {
93+
return Files.getLastModifiedTime(file).toMillis();
94+
}
95+
8796
private FileSystem openArchive(final Path p, final boolean createNew) throws IOException {
8897
if (createNew) {
8998
final Map<String, String> env = new HashMap<>();
@@ -95,6 +104,10 @@ private FileSystem openArchive(final Path p, final boolean createNew) throws IOE
95104
return FileSystems.newFileSystem(p, (ClassLoader) null);
96105
}
97106

107+
private void setLastModifiedMillis(final Path file, final long millis) throws IOException {
108+
Files.setLastModifiedTime(file, FileTime.fromMillis(millis));
109+
}
110+
98111
@Test
99112
public void testCopyDirectoryForDifferentFilesystemsWithAbsolutePath() throws IOException {
100113
final Path archivePath = Paths.get(TEST_JAR_PATH);
@@ -407,6 +420,32 @@ public void testSetReadOnlyFile() throws IOException {
407420
PathUtils.deleteFile(resolved);
408421
}
409422

423+
@Test
424+
public void testTouch() throws IOException {
425+
assertThrows(NullPointerException.class, () -> FileUtils.touch(null));
426+
427+
final Path file = managedTempDirPath.resolve("touch.txt");
428+
Files.deleteIfExists(file);
429+
assertFalse(Files.exists(file), "Bad test: test file still exists");
430+
PathUtils.touch(file);
431+
assertTrue(Files.exists(file), "touch() created file");
432+
try (final OutputStream out = Files.newOutputStream(file)) {
433+
assertEquals(0, Files.size(file), "Created empty file.");
434+
out.write(0);
435+
}
436+
assertEquals(1, Files.size(file), "Wrote one byte to file");
437+
final long y2k = new GregorianCalendar(2000, 0, 1).getTime().getTime();
438+
setLastModifiedMillis(file, y2k); // 0L fails on Win98
439+
assertEquals(y2k, getLastModifiedMillis(file), "Bad test: set lastModified set incorrect value");
440+
final long nowMillis = System.currentTimeMillis();
441+
PathUtils.touch(file);
442+
assertEquals(1, Files.size(file), "FileUtils.touch() didn't empty the file.");
443+
assertNotEquals(y2k, getLastModifiedMillis(file), "FileUtils.touch() changed lastModified");
444+
final int delta = 3000;
445+
assertTrue(getLastModifiedMillis(file) >= nowMillis - delta, "FileUtils.touch() changed lastModified to more than now-3s");
446+
assertTrue(getLastModifiedMillis(file) <= nowMillis + delta, "FileUtils.touch() changed lastModified to less than now+3s");
447+
}
448+
410449
@Test
411450
public void testWriteStringToFile1() throws Exception {
412451
final Path file = tempDirPath.resolve("write.txt");

0 commit comments

Comments
 (0)