• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 package org.apache.commons.io.file;
19 
20 import java.io.File;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.OutputStream;
24 import java.io.UncheckedIOException;
25 import java.math.BigInteger;
26 import java.net.URI;
27 import java.net.URISyntaxException;
28 import java.net.URL;
29 import java.nio.charset.Charset;
30 import java.nio.file.AccessDeniedException;
31 import java.nio.file.CopyOption;
32 import java.nio.file.DirectoryStream;
33 import java.nio.file.FileVisitOption;
34 import java.nio.file.FileVisitResult;
35 import java.nio.file.FileVisitor;
36 import java.nio.file.Files;
37 import java.nio.file.LinkOption;
38 import java.nio.file.NoSuchFileException;
39 import java.nio.file.NotDirectoryException;
40 import java.nio.file.OpenOption;
41 import java.nio.file.Path;
42 import java.nio.file.Paths;
43 import java.nio.file.StandardOpenOption;
44 import java.nio.file.attribute.AclEntry;
45 import java.nio.file.attribute.AclFileAttributeView;
46 import java.nio.file.attribute.BasicFileAttributes;
47 import java.nio.file.attribute.DosFileAttributeView;
48 import java.nio.file.attribute.DosFileAttributes;
49 import java.nio.file.attribute.FileAttribute;
50 import java.nio.file.attribute.FileTime;
51 import java.nio.file.attribute.PosixFileAttributeView;
52 import java.nio.file.attribute.PosixFileAttributes;
53 import java.nio.file.attribute.PosixFilePermission;
54 import java.time.Duration;
55 import java.time.Instant;
56 import java.time.chrono.ChronoZonedDateTime;
57 import java.util.ArrayList;
58 import java.util.Arrays;
59 import java.util.Collection;
60 import java.util.Collections;
61 import java.util.Comparator;
62 import java.util.EnumSet;
63 import java.util.List;
64 import java.util.Objects;
65 import java.util.Set;
66 import java.util.stream.Collector;
67 import java.util.stream.Collectors;
68 import java.util.stream.Stream;
69 
70 import org.apache.commons.io.Charsets;
71 import org.apache.commons.io.FileUtils;
72 import org.apache.commons.io.FilenameUtils;
73 import org.apache.commons.io.IOUtils;
74 import org.apache.commons.io.ThreadUtils;
75 import org.apache.commons.io.file.Counters.PathCounters;
76 import org.apache.commons.io.file.attribute.FileTimes;
77 import org.apache.commons.io.filefilter.IOFileFilter;
78 import org.apache.commons.io.function.IOFunction;
79 import org.apache.commons.io.function.Uncheck;
80 
81 /**
82  * NIO Path utilities.
83  *
84  * @since 2.7
85  */
86 public final class PathUtils {
87 
88     /**
89      * Private worker/holder that computes and tracks relative path names and their equality. We reuse the sorted relative
90      * lists when comparing directories.
91      */
92     private static class RelativeSortedPaths {
93 
94         final boolean equals;
95         // final List<Path> relativeDirList1; // might need later?
96         // final List<Path> relativeDirList2; // might need later?
97         final List<Path> relativeFileList1;
98         final List<Path> relativeFileList2;
99 
100         /**
101          * Constructs and initializes a new instance by accumulating directory and file info.
102          *
103          * @param dir1 First directory to compare.
104          * @param dir2 Seconds directory to compare.
105          * @param maxDepth See {@link Files#walkFileTree(Path,Set,int,FileVisitor)}.
106          * @param linkOptions Options indicating how symbolic links are handled.
107          * @param fileVisitOptions See {@link Files#walkFileTree(Path,Set,int,FileVisitor)}.
108          * @throws IOException if an I/O error is thrown by a visitor method.
109          */
RelativeSortedPaths(final Path dir1, final Path dir2, final int maxDepth, final LinkOption[] linkOptions, final FileVisitOption[] fileVisitOptions)110         private RelativeSortedPaths(final Path dir1, final Path dir2, final int maxDepth, final LinkOption[] linkOptions,
111             final FileVisitOption[] fileVisitOptions) throws IOException {
112             final List<Path> tmpRelativeDirList1;
113             final List<Path> tmpRelativeDirList2;
114             List<Path> tmpRelativeFileList1 = null;
115             List<Path> tmpRelativeFileList2 = null;
116             if (dir1 == null && dir2 == null) {
117                 equals = true;
118             } else if (dir1 == null ^ dir2 == null) {
119                 equals = false;
120             } else {
121                 final boolean parentDirNotExists1 = Files.notExists(dir1, linkOptions);
122                 final boolean parentDirNotExists2 = Files.notExists(dir2, linkOptions);
123                 if (parentDirNotExists1 || parentDirNotExists2) {
124                     equals = parentDirNotExists1 && parentDirNotExists2;
125                 } else {
126                     final AccumulatorPathVisitor visitor1 = accumulate(dir1, maxDepth, fileVisitOptions);
127                     final AccumulatorPathVisitor visitor2 = accumulate(dir2, maxDepth, fileVisitOptions);
128                     if (visitor1.getDirList().size() != visitor2.getDirList().size() || visitor1.getFileList().size() != visitor2.getFileList().size()) {
129                         equals = false;
130                     } else {
131                         tmpRelativeDirList1 = visitor1.relativizeDirectories(dir1, true, null);
132                         tmpRelativeDirList2 = visitor2.relativizeDirectories(dir2, true, null);
133                         if (!tmpRelativeDirList1.equals(tmpRelativeDirList2)) {
134                             equals = false;
135                         } else {
136                             tmpRelativeFileList1 = visitor1.relativizeFiles(dir1, true, null);
137                             tmpRelativeFileList2 = visitor2.relativizeFiles(dir2, true, null);
138                             equals = tmpRelativeFileList1.equals(tmpRelativeFileList2);
139                         }
140                     }
141                 }
142             }
143             // relativeDirList1 = tmpRelativeDirList1;
144             // relativeDirList2 = tmpRelativeDirList2;
145             relativeFileList1 = tmpRelativeFileList1;
146             relativeFileList2 = tmpRelativeFileList2;
147         }
148     }
149 
150     private static final OpenOption[] OPEN_OPTIONS_TRUNCATE = {StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING};
151 
152     private static final OpenOption[] OPEN_OPTIONS_APPEND = {StandardOpenOption.CREATE, StandardOpenOption.APPEND};
153 
154     /**
155      * Empty {@link CopyOption} array.
156      *
157      * @since 2.8.0
158      */
159     public static final CopyOption[] EMPTY_COPY_OPTIONS = {};
160 
161     /**
162      * Empty {@link DeleteOption} array.
163      *
164      * @since 2.8.0
165      */
166     public static final DeleteOption[] EMPTY_DELETE_OPTION_ARRAY = {};
167 
168     /**
169      * Empty {@link FileVisitOption} array.
170      */
171     public static final FileVisitOption[] EMPTY_FILE_VISIT_OPTION_ARRAY = {};
172 
173     /**
174      * Empty {@link LinkOption} array.
175      */
176     public static final LinkOption[] EMPTY_LINK_OPTION_ARRAY = {};
177 
178     /**
179      * {@link LinkOption} array for {@link LinkOption#NOFOLLOW_LINKS}.
180      *
181      * @since 2.9.0
182      * @deprecated Use {@link #noFollowLinkOptionArray()}.
183      */
184     @Deprecated
185     public static final LinkOption[] NOFOLLOW_LINK_OPTION_ARRAY = {LinkOption.NOFOLLOW_LINKS};
186 
187     /**
188      * A LinkOption used to follow link in this class, the inverse of {@link LinkOption#NOFOLLOW_LINKS}.
189      *
190      * @since 2.12.0
191      */
192     static final LinkOption NULL_LINK_OPTION = null;
193 
194     /**
195      * Empty {@link OpenOption} array.
196      */
197     public static final OpenOption[] EMPTY_OPEN_OPTION_ARRAY = {};
198 
199     /**
200      * Empty {@link Path} array.
201      *
202      * @since 2.9.0
203      */
204     public static final Path[] EMPTY_PATH_ARRAY = {};
205 
206     /**
207      * Accumulates file tree information in a {@link AccumulatorPathVisitor}.
208      *
209      * @param directory The directory to accumulate information.
210      * @param maxDepth See {@link Files#walkFileTree(Path,Set,int,FileVisitor)}.
211      * @param fileVisitOptions See {@link Files#walkFileTree(Path,Set,int,FileVisitor)}.
212      * @throws IOException if an I/O error is thrown by a visitor method.
213      * @return file tree information.
214      */
accumulate(final Path directory, final int maxDepth, final FileVisitOption[] fileVisitOptions)215     private static AccumulatorPathVisitor accumulate(final Path directory, final int maxDepth, final FileVisitOption[] fileVisitOptions) throws IOException {
216         return visitFileTree(AccumulatorPathVisitor.withLongCounters(), directory, toFileVisitOptionSet(fileVisitOptions), maxDepth);
217     }
218 
219     /**
220      * Cleans a directory including subdirectories without deleting directories.
221      *
222      * @param directory directory to clean.
223      * @return The visitation path counters.
224      * @throws IOException if an I/O error is thrown by a visitor method.
225      */
cleanDirectory(final Path directory)226     public static PathCounters cleanDirectory(final Path directory) throws IOException {
227         return cleanDirectory(directory, EMPTY_DELETE_OPTION_ARRAY);
228     }
229 
230     /**
231      * Cleans a directory including subdirectories without deleting directories.
232      *
233      * @param directory directory to clean.
234      * @param deleteOptions How to handle deletion.
235      * @return The visitation path counters.
236      * @throws IOException if an I/O error is thrown by a visitor method.
237      * @since 2.8.0
238      */
cleanDirectory(final Path directory, final DeleteOption... deleteOptions)239     public static PathCounters cleanDirectory(final Path directory, final DeleteOption... deleteOptions) throws IOException {
240         return visitFileTree(new CleaningPathVisitor(Counters.longPathCounters(), deleteOptions), directory).getPathCounters();
241     }
242 
243     /**
244      * Compares the given {@link Path}'s last modified time to the given file time.
245      *
246      * @param file the {@link Path} to test.
247      * @param fileTime the time reference.
248      * @param options options indicating how to handle symbolic links.
249      * @return See {@link FileTime#compareTo(FileTime)}
250      * @throws IOException if an I/O error occurs.
251      * @throws NullPointerException if the file is {@code null}.
252      */
compareLastModifiedTimeTo(final Path file, final FileTime fileTime, final LinkOption... options)253     private static int compareLastModifiedTimeTo(final Path file, final FileTime fileTime, final LinkOption... options) throws IOException {
254         return getLastModifiedTime(file, options).compareTo(fileTime);
255     }
256 
257     /**
258      * Copies a directory to another directory.
259      *
260      * @param sourceDirectory The source directory.
261      * @param targetDirectory The target directory.
262      * @param copyOptions Specifies how the copying should be done.
263      * @return The visitation path counters.
264      * @throws IOException if an I/O error is thrown by a visitor method.
265      */
copyDirectory(final Path sourceDirectory, final Path targetDirectory, final CopyOption... copyOptions)266     public static PathCounters copyDirectory(final Path sourceDirectory, final Path targetDirectory, final CopyOption... copyOptions) throws IOException {
267         final Path absoluteSource = sourceDirectory.toAbsolutePath();
268         return visitFileTree(new CopyDirectoryVisitor(Counters.longPathCounters(), absoluteSource, targetDirectory, copyOptions), absoluteSource)
269             .getPathCounters();
270     }
271 
272     /**
273      * Copies a URL to a directory.
274      *
275      * @param sourceFile The source URL.
276      * @param targetFile The target file.
277      * @param copyOptions Specifies how the copying should be done.
278      * @return The target file
279      * @throws IOException if an I/O error occurs.
280      * @see Files#copy(InputStream, Path, CopyOption...)
281      */
copyFile(final URL sourceFile, final Path targetFile, final CopyOption... copyOptions)282     public static Path copyFile(final URL sourceFile, final Path targetFile, final CopyOption... copyOptions) throws IOException {
283         try (InputStream inputStream = sourceFile.openStream()) {
284             Files.copy(inputStream, targetFile, copyOptions);
285             return targetFile;
286         }
287     }
288 
289     /**
290      * Copies a file to a directory.
291      *
292      * @param sourceFile The source file.
293      * @param targetDirectory The target directory.
294      * @param copyOptions Specifies how the copying should be done.
295      * @return The target file
296      * @throws IOException if an I/O error occurs.
297      * @see Files#copy(Path, Path, CopyOption...)
298      */
copyFileToDirectory(final Path sourceFile, final Path targetDirectory, final CopyOption... copyOptions)299     public static Path copyFileToDirectory(final Path sourceFile, final Path targetDirectory, final CopyOption... copyOptions) throws IOException {
300         return Files.copy(sourceFile, targetDirectory.resolve(sourceFile.getFileName()), copyOptions);
301     }
302 
303     /**
304      * Copies a URL to a directory.
305      *
306      * @param sourceFile The source URL.
307      * @param targetDirectory The target directory.
308      * @param copyOptions Specifies how the copying should be done.
309      * @return The target file
310      * @throws IOException if an I/O error occurs.
311      * @see Files#copy(InputStream, Path, CopyOption...)
312      */
copyFileToDirectory(final URL sourceFile, final Path targetDirectory, final CopyOption... copyOptions)313     public static Path copyFileToDirectory(final URL sourceFile, final Path targetDirectory, final CopyOption... copyOptions) throws IOException {
314         try (InputStream inputStream = sourceFile.openStream()) {
315             final Path resolve = targetDirectory.resolve(FilenameUtils.getName(sourceFile.getFile()));
316             Files.copy(inputStream, resolve, copyOptions);
317             return resolve;
318         }
319     }
320 
321     /**
322      * Counts aspects of a directory including subdirectories.
323      *
324      * @param directory directory to delete.
325      * @return The visitor used to count the given directory.
326      * @throws IOException if an I/O error is thrown by a visitor method.
327      */
countDirectory(final Path directory)328     public static PathCounters countDirectory(final Path directory) throws IOException {
329         return visitFileTree(CountingPathVisitor.withLongCounters(), directory).getPathCounters();
330     }
331 
332     /**
333      * Counts aspects of a directory including subdirectories.
334      *
335      * @param directory directory to count.
336      * @return The visitor used to count the given directory.
337      * @throws IOException if an I/O error occurs.
338      * @since 2.12.0
339      */
countDirectoryAsBigInteger(final Path directory)340     public static PathCounters countDirectoryAsBigInteger(final Path directory) throws IOException {
341         return visitFileTree(CountingPathVisitor.withBigIntegerCounters(), directory).getPathCounters();
342     }
343 
344     /**
345      * Creates the parent directories for the given {@code path}.
346      *
347      * @param path The path to a file (or directory).
348      * @param attrs An optional list of file attributes to set atomically when creating the directories.
349      * @return The Path for the {@code path}'s parent directory or null if the given path has no parent.
350      * @throws IOException if an I/O error occurs.
351      * @since 2.9.0
352      */
createParentDirectories(final Path path, final FileAttribute<?>... attrs)353     public static Path createParentDirectories(final Path path, final FileAttribute<?>... attrs) throws IOException {
354         return createParentDirectories(path, LinkOption.NOFOLLOW_LINKS, attrs);
355     }
356 
357     /**
358      * Creates the parent directories for the given {@code path}.
359      *
360      * @param path The path to a file (or directory).
361      * @param linkOption A {@link LinkOption} or null.
362      * @param attrs An optional list of file attributes to set atomically when creating the directories.
363      * @return The Path for the {@code path}'s parent directory or null if the given path has no parent.
364      * @throws IOException if an I/O error occurs.
365      * @since 2.12.0
366      */
createParentDirectories(final Path path, final LinkOption linkOption, final FileAttribute<?>... attrs)367     public static Path createParentDirectories(final Path path, final LinkOption linkOption, final FileAttribute<?>... attrs) throws IOException {
368         Path parent = getParent(path);
369         parent = linkOption == LinkOption.NOFOLLOW_LINKS ? parent : readIfSymbolicLink(parent);
370         return parent == null ? null : Files.createDirectories(parent, attrs);
371     }
372 
373     /**
374      * Gets the current directory.
375      *
376      * @return the current directory.
377      *
378      * @since 2.9.0
379      */
current()380     public static Path current() {
381         return Paths.get(".");
382     }
383 
384     /**
385      * Deletes a file or directory. If the path is a directory, delete it and all subdirectories.
386      * <p>
387      * The difference between File.delete() and this method are:
388      * </p>
389      * <ul>
390      * <li>A directory to delete does not have to be empty.</li>
391      * <li>You get exceptions when a file or directory cannot be deleted; {@link java.io.File#delete()} returns a boolean.
392      * </ul>
393      *
394      * @param path file or directory to delete, must not be {@code null}
395      * @return The visitor used to delete the given directory.
396      * @throws NullPointerException if the directory is {@code null}
397      * @throws IOException if an I/O error is thrown by a visitor method or if an I/O error occurs.
398      */
delete(final Path path)399     public static PathCounters delete(final Path path) throws IOException {
400         return delete(path, EMPTY_DELETE_OPTION_ARRAY);
401     }
402 
403     /**
404      * Deletes a file or directory. If the path is a directory, delete it and all subdirectories.
405      * <p>
406      * The difference between File.delete() and this method are:
407      * </p>
408      * <ul>
409      * <li>A directory to delete does not have to be empty.</li>
410      * <li>You get exceptions when a file or directory cannot be deleted; {@link java.io.File#delete()} returns a boolean.
411      * </ul>
412      *
413      * @param path file or directory to delete, must not be {@code null}
414      * @param deleteOptions How to handle deletion.
415      * @return The visitor used to delete the given directory.
416      * @throws NullPointerException if the directory is {@code null}
417      * @throws IOException if an I/O error is thrown by a visitor method or if an I/O error occurs.
418      * @since 2.8.0
419      */
delete(final Path path, final DeleteOption... deleteOptions)420     public static PathCounters delete(final Path path, final DeleteOption... deleteOptions) throws IOException {
421         // File deletion through Files deletes links, not targets, so use LinkOption.NOFOLLOW_LINKS.
422         return Files.isDirectory(path, LinkOption.NOFOLLOW_LINKS) ? deleteDirectory(path, deleteOptions) : deleteFile(path, deleteOptions);
423     }
424 
425     /**
426      * Deletes a file or directory. If the path is a directory, delete it and all subdirectories.
427      * <p>
428      * The difference between File.delete() and this method are:
429      * </p>
430      * <ul>
431      * <li>A directory to delete does not have to be empty.</li>
432      * <li>You get exceptions when a file or directory cannot be deleted; {@link java.io.File#delete()} returns a boolean.
433      * </ul>
434      *
435      * @param path file or directory to delete, must not be {@code null}
436      * @param linkOptions How to handle symbolic links.
437      * @param deleteOptions How to handle deletion.
438      * @return The visitor used to delete the given directory.
439      * @throws NullPointerException if the directory is {@code null}
440      * @throws IOException if an I/O error is thrown by a visitor method or if an I/O error occurs.
441      * @since 2.9.0
442      */
delete(final Path path, final LinkOption[] linkOptions, final DeleteOption... deleteOptions)443     public static PathCounters delete(final Path path, final LinkOption[] linkOptions, final DeleteOption... deleteOptions) throws IOException {
444         // File deletion through Files deletes links, not targets, so use LinkOption.NOFOLLOW_LINKS.
445         return Files.isDirectory(path, linkOptions) ? deleteDirectory(path, linkOptions, deleteOptions) : deleteFile(path, linkOptions, deleteOptions);
446     }
447 
448     /**
449      * Deletes a directory including subdirectories.
450      *
451      * @param directory directory to delete.
452      * @return The visitor used to delete the given directory.
453      * @throws IOException if an I/O error is thrown by a visitor method.
454      */
deleteDirectory(final Path directory)455     public static PathCounters deleteDirectory(final Path directory) throws IOException {
456         return deleteDirectory(directory, EMPTY_DELETE_OPTION_ARRAY);
457     }
458 
459     /**
460      * Deletes a directory including subdirectories.
461      *
462      * @param directory directory to delete.
463      * @param deleteOptions How to handle deletion.
464      * @return The visitor used to delete the given directory.
465      * @throws IOException if an I/O error is thrown by a visitor method.
466      * @since 2.8.0
467      */
deleteDirectory(final Path directory, final DeleteOption... deleteOptions)468     public static PathCounters deleteDirectory(final Path directory, final DeleteOption... deleteOptions) throws IOException {
469         final LinkOption[] linkOptions = PathUtils.noFollowLinkOptionArray();
470         // POSIX ops will noop on non-POSIX.
471         return withPosixFileAttributes(getParent(directory), linkOptions, overrideReadOnly(deleteOptions),
472             pfa -> visitFileTree(new DeletingPathVisitor(Counters.longPathCounters(), linkOptions, deleteOptions), directory).getPathCounters());
473     }
474 
475     /**
476      * Deletes a directory including subdirectories.
477      *
478      * @param directory directory to delete.
479      * @param linkOptions How to handle symbolic links.
480      * @param deleteOptions How to handle deletion.
481      * @return The visitor used to delete the given directory.
482      * @throws IOException if an I/O error is thrown by a visitor method.
483      * @since 2.9.0
484      */
deleteDirectory(final Path directory, final LinkOption[] linkOptions, final DeleteOption... deleteOptions)485     public static PathCounters deleteDirectory(final Path directory, final LinkOption[] linkOptions, final DeleteOption... deleteOptions) throws IOException {
486         return visitFileTree(new DeletingPathVisitor(Counters.longPathCounters(), linkOptions, deleteOptions), directory).getPathCounters();
487     }
488 
489     /**
490      * Deletes the given file.
491      *
492      * @param file The file to delete.
493      * @return A visitor with path counts set to 1 file, 0 directories, and the size of the deleted file.
494      * @throws IOException if an I/O error occurs.
495      * @throws NoSuchFileException if the file is a directory.
496      */
deleteFile(final Path file)497     public static PathCounters deleteFile(final Path file) throws IOException {
498         return deleteFile(file, EMPTY_DELETE_OPTION_ARRAY);
499     }
500 
501     /**
502      * Deletes the given file.
503      *
504      * @param file The file to delete.
505      * @param deleteOptions How to handle deletion.
506      * @return A visitor with path counts set to 1 file, 0 directories, and the size of the deleted file.
507      * @throws IOException if an I/O error occurs.
508      * @throws NoSuchFileException if the file is a directory.
509      * @since 2.8.0
510      */
deleteFile(final Path file, final DeleteOption... deleteOptions)511     public static PathCounters deleteFile(final Path file, final DeleteOption... deleteOptions) throws IOException {
512         // Files.deleteIfExists() never follows links, so use LinkOption.NOFOLLOW_LINKS in other calls to Files.
513         return deleteFile(file, noFollowLinkOptionArray(), deleteOptions);
514     }
515 
516     /**
517      * Deletes the given file.
518      *
519      * @param file The file to delete.
520      * @param linkOptions How to handle symbolic links.
521      * @param deleteOptions How to handle deletion.
522      * @return A visitor with path counts set to 1 file, 0 directories, and the size of the deleted file.
523      * @throws IOException if an I/O error occurs.
524      * @throws NoSuchFileException if the file is a directory.
525      * @since 2.9.0
526      */
deleteFile(final Path file, final LinkOption[] linkOptions, final DeleteOption... deleteOptions)527     public static PathCounters deleteFile(final Path file, final LinkOption[] linkOptions, final DeleteOption... deleteOptions)
528         throws NoSuchFileException, IOException {
529         //
530         // TODO Needs clean up
531         //
532         if (Files.isDirectory(file, linkOptions)) {
533             throw new NoSuchFileException(file.toString());
534         }
535         final PathCounters pathCounts = Counters.longPathCounters();
536         boolean exists = exists(file, linkOptions);
537         long size = exists && !Files.isSymbolicLink(file) ? Files.size(file) : 0;
538         try {
539             if (Files.deleteIfExists(file)) {
540                 pathCounts.getFileCounter().increment();
541                 pathCounts.getByteCounter().add(size);
542                 return pathCounts;
543             }
544         } catch (final AccessDeniedException ignored) {
545             // Ignore and try again below.
546         }
547         final Path parent = getParent(file);
548         PosixFileAttributes posixFileAttributes = null;
549         try {
550             if (overrideReadOnly(deleteOptions)) {
551                 posixFileAttributes = readPosixFileAttributes(parent, linkOptions);
552                 setReadOnly(file, false, linkOptions);
553             }
554             // Read size _after_ having read/execute access on POSIX.
555             exists = exists(file, linkOptions);
556             size = exists && !Files.isSymbolicLink(file) ? Files.size(file) : 0;
557             if (Files.deleteIfExists(file)) {
558                 pathCounts.getFileCounter().increment();
559                 pathCounts.getByteCounter().add(size);
560             }
561         } finally {
562             if (posixFileAttributes != null) {
563                 Files.setPosixFilePermissions(parent, posixFileAttributes.permissions());
564             }
565         }
566         return pathCounts;
567     }
568 
569     /**
570      * Delegates to {@link File#deleteOnExit()}.
571      *
572      * @param path the path to delete.
573      * @since 3.13.0
574      */
deleteOnExit(Path path)575     public static void deleteOnExit(Path path) {
576         Objects.requireNonNull(path.toFile()).deleteOnExit();
577     }
578 
579     /**
580      * Compares the file sets of two Paths to determine if they are equal or not while considering file contents. The
581      * comparison includes all files in all subdirectories.
582      *
583      * @param path1 The first directory.
584      * @param path2 The second directory.
585      * @return Whether the two directories contain the same files while considering file contents.
586      * @throws IOException if an I/O error is thrown by a visitor method.
587      */
directoryAndFileContentEquals(final Path path1, final Path path2)588     public static boolean directoryAndFileContentEquals(final Path path1, final Path path2) throws IOException {
589         return directoryAndFileContentEquals(path1, path2, EMPTY_LINK_OPTION_ARRAY, EMPTY_OPEN_OPTION_ARRAY, EMPTY_FILE_VISIT_OPTION_ARRAY);
590     }
591 
592     /**
593      * Compares the file sets of two Paths to determine if they are equal or not while considering file contents. The
594      * comparison includes all files in all subdirectories.
595      *
596      * @param path1 The first directory.
597      * @param path2 The second directory.
598      * @param linkOptions options to follow links.
599      * @param openOptions options to open files.
600      * @param fileVisitOption options to configure traversal.
601      * @return Whether the two directories contain the same files while considering file contents.
602      * @throws IOException if an I/O error is thrown by a visitor method.
603      */
directoryAndFileContentEquals(final Path path1, final Path path2, final LinkOption[] linkOptions, final OpenOption[] openOptions, final FileVisitOption[] fileVisitOption)604     public static boolean directoryAndFileContentEquals(final Path path1, final Path path2, final LinkOption[] linkOptions, final OpenOption[] openOptions,
605         final FileVisitOption[] fileVisitOption) throws IOException {
606         // First walk both file trees and gather normalized paths.
607         if (path1 == null && path2 == null) {
608             return true;
609         }
610         if (path1 == null || path2 == null) {
611             return false;
612         }
613         if (notExists(path1) && notExists(path2)) {
614             return true;
615         }
616         final RelativeSortedPaths relativeSortedPaths = new RelativeSortedPaths(path1, path2, Integer.MAX_VALUE, linkOptions, fileVisitOption);
617         // If the normalized path names and counts are not the same, no need to compare contents.
618         if (!relativeSortedPaths.equals) {
619             return false;
620         }
621         // Both visitors contain the same normalized paths, we can compare file contents.
622         final List<Path> fileList1 = relativeSortedPaths.relativeFileList1;
623         final List<Path> fileList2 = relativeSortedPaths.relativeFileList2;
624         for (final Path path : fileList1) {
625             final int binarySearch = Collections.binarySearch(fileList2, path);
626             if (binarySearch <= -1) {
627                 throw new IllegalStateException("Unexpected mismatch.");
628             }
629             if (!fileContentEquals(path1.resolve(path), path2.resolve(path), linkOptions, openOptions)) {
630                 return false;
631             }
632         }
633         return true;
634     }
635 
636     /**
637      * Compares the file sets of two Paths to determine if they are equal or not without considering file contents. The
638      * comparison includes all files in all subdirectories.
639      *
640      * @param path1 The first directory.
641      * @param path2 The second directory.
642      * @return Whether the two directories contain the same files without considering file contents.
643      * @throws IOException if an I/O error is thrown by a visitor method.
644      */
directoryContentEquals(final Path path1, final Path path2)645     public static boolean directoryContentEquals(final Path path1, final Path path2) throws IOException {
646         return directoryContentEquals(path1, path2, Integer.MAX_VALUE, EMPTY_LINK_OPTION_ARRAY, EMPTY_FILE_VISIT_OPTION_ARRAY);
647     }
648 
649     /**
650      * Compares the file sets of two Paths to determine if they are equal or not without considering file contents. The
651      * comparison includes all files in all subdirectories.
652      *
653      * @param path1 The first directory.
654      * @param path2 The second directory.
655      * @param maxDepth See {@link Files#walkFileTree(Path,Set,int,FileVisitor)}.
656      * @param linkOptions options to follow links.
657      * @param fileVisitOptions options to configure the traversal
658      * @return Whether the two directories contain the same files without considering file contents.
659      * @throws IOException if an I/O error is thrown by a visitor method.
660      */
directoryContentEquals(final Path path1, final Path path2, final int maxDepth, final LinkOption[] linkOptions, final FileVisitOption[] fileVisitOptions)661     public static boolean directoryContentEquals(final Path path1, final Path path2, final int maxDepth, final LinkOption[] linkOptions,
662         final FileVisitOption[] fileVisitOptions) throws IOException {
663         return new RelativeSortedPaths(path1, path2, maxDepth, linkOptions, fileVisitOptions).equals;
664     }
665 
exists(final Path path, final LinkOption... options)666     private static boolean exists(final Path path, final LinkOption... options) {
667         Objects.requireNonNull(path, "path");
668         return options != null ? Files.exists(path, options) : Files.exists(path);
669     }
670 
671     /**
672      * Compares the file contents of two Paths to determine if they are equal or not.
673      * <p>
674      * File content is accessed through {@link Files#newInputStream(Path,OpenOption...)}.
675      * </p>
676      *
677      * @param path1 the first stream.
678      * @param path2 the second stream.
679      * @return true if the content of the streams are equal or they both don't exist, false otherwise.
680      * @throws NullPointerException if either input is null.
681      * @throws IOException if an I/O error occurs.
682      * @see org.apache.commons.io.FileUtils#contentEquals(java.io.File, java.io.File)
683      */
fileContentEquals(final Path path1, final Path path2)684     public static boolean fileContentEquals(final Path path1, final Path path2) throws IOException {
685         return fileContentEquals(path1, path2, EMPTY_LINK_OPTION_ARRAY, EMPTY_OPEN_OPTION_ARRAY);
686     }
687 
688     /**
689      * Compares the file contents of two Paths to determine if they are equal or not.
690      * <p>
691      * File content is accessed through {@link Files#newInputStream(Path,OpenOption...)}.
692      * </p>
693      *
694      * @param path1 the first stream.
695      * @param path2 the second stream.
696      * @param linkOptions options specifying how files are followed.
697      * @param openOptions options specifying how files are opened.
698      * @return true if the content of the streams are equal or they both don't exist, false otherwise.
699      * @throws NullPointerException if either input is null.
700      * @throws IOException if an I/O error occurs.
701      * @see org.apache.commons.io.FileUtils#contentEquals(java.io.File, java.io.File)
702      */
fileContentEquals(final Path path1, final Path path2, final LinkOption[] linkOptions, final OpenOption[] openOptions)703     public static boolean fileContentEquals(final Path path1, final Path path2, final LinkOption[] linkOptions, final OpenOption[] openOptions)
704         throws IOException {
705         if (path1 == null && path2 == null) {
706             return true;
707         }
708         if (path1 == null || path2 == null) {
709             return false;
710         }
711         final Path nPath1 = path1.normalize();
712         final Path nPath2 = path2.normalize();
713         final boolean path1Exists = exists(nPath1, linkOptions);
714         if (path1Exists != exists(nPath2, linkOptions)) {
715             return false;
716         }
717         if (!path1Exists) {
718             // Two not existing files are equal?
719             // Same as FileUtils
720             return true;
721         }
722         if (Files.isDirectory(nPath1, linkOptions)) {
723             // don't compare directory contents.
724             throw new IOException("Can't compare directories, only files: " + nPath1);
725         }
726         if (Files.isDirectory(nPath2, linkOptions)) {
727             // don't compare directory contents.
728             throw new IOException("Can't compare directories, only files: " + nPath2);
729         }
730         if (Files.size(nPath1) != Files.size(nPath2)) {
731             // lengths differ, cannot be equal
732             return false;
733         }
734         if (path1.equals(path2)) {
735             // same file
736             return true;
737         }
738         try (InputStream inputStream1 = Files.newInputStream(nPath1, openOptions);
739             InputStream inputStream2 = Files.newInputStream(nPath2, openOptions)) {
740             return IOUtils.contentEquals(inputStream1, inputStream2);
741         }
742     }
743 
744     /**
745      * <p>
746      * Applies an {@link IOFileFilter} to the provided {@link File} objects. The resulting array is a subset of the original
747      * file list that matches the provided filter.
748      * </p>
749      *
750      * <p>
751      * The {@link Set} returned by this method is not guaranteed to be thread safe.
752      * </p>
753      *
754      * <pre>
755      * Set&lt;File&gt; allFiles = ...
756      * Set&lt;File&gt; javaFiles = FileFilterUtils.filterSet(allFiles,
757      *     FileFilterUtils.suffixFileFilter(".java"));
758      * </pre>
759      *
760      * @param filter the filter to apply to the set of files.
761      * @param paths the array of files to apply the filter to.
762      *
763      * @return a subset of {@code files} that is accepted by the file filter.
764      * @throws NullPointerException if the filter is {@code null}
765      * @throws IllegalArgumentException if {@code files} contains a {@code null} value.
766      *
767      * @since 2.9.0
768      */
filter(final PathFilter filter, final Path... paths)769     public static Path[] filter(final PathFilter filter, final Path... paths) {
770         Objects.requireNonNull(filter, "filter");
771         if (paths == null) {
772             return EMPTY_PATH_ARRAY;
773         }
774         return filterPaths(filter, Stream.of(paths), Collectors.toList()).toArray(EMPTY_PATH_ARRAY);
775     }
776 
filterPaths(final PathFilter filter, final Stream<Path> stream, final Collector<? super Path, A, R> collector)777     private static <R, A> R filterPaths(final PathFilter filter, final Stream<Path> stream, final Collector<? super Path, A, R> collector) {
778         Objects.requireNonNull(filter, "filter");
779         Objects.requireNonNull(collector, "collector");
780         if (stream == null) {
781             return Stream.<Path>empty().collect(collector);
782         }
783         return stream.filter(p -> {
784             try {
785                 return p != null && filter.accept(p, readBasicFileAttributes(p)) == FileVisitResult.CONTINUE;
786             } catch (final IOException e) {
787                 return false;
788             }
789         }).collect(collector);
790     }
791 
792     /**
793      * Reads the access control list from a file attribute view.
794      *
795      * @param sourcePath the path to the file.
796      * @return a file attribute view of the given type, or null if the attribute view type is not available.
797      * @throws IOException if an I/O error occurs.
798      * @since 2.8.0
799      */
800     public static List<AclEntry> getAclEntryList(final Path sourcePath) throws IOException {
801         final AclFileAttributeView fileAttributeView = getAclFileAttributeView(sourcePath);
802         return fileAttributeView == null ? null : fileAttributeView.getAcl();
803     }
804 
805     /**
806      * Shorthand for {@code Files.getFileAttributeView(path, AclFileAttributeView.class)}.
807      *
808      * @param path the path to the file.
809      * @param options how to handle symbolic links.
810      * @return a AclFileAttributeView, or {@code null} if the attribute view type is not available.
811      * @since 2.12.0
812      */
813     public static AclFileAttributeView getAclFileAttributeView(final Path path, final LinkOption... options) {
814         return Files.getFileAttributeView(path, AclFileAttributeView.class, options);
815     }
816 
817     /**
818      * Shorthand for {@code Files.getFileAttributeView(path, DosFileAttributeView.class)}.
819      *
820      * @param path the path to the file.
821      * @param options how to handle symbolic links.
822      * @return a DosFileAttributeView, or {@code null} if the attribute view type is not available.
823      * @since 2.12.0
824      */
825     public static DosFileAttributeView getDosFileAttributeView(final Path path, final LinkOption... options) {
826         return Files.getFileAttributeView(path, DosFileAttributeView.class, options);
827     }
828 
829     /**
830      * Gets the file's last modified time or null if the file does not exist.
831      * <p>
832      * The method provides a workaround for bug <a href="https://bugs.openjdk.java.net/browse/JDK-8177809">JDK-8177809</a>
833      * where {@link File#lastModified()} looses milliseconds and always ends in 000. This bug is in OpenJDK 8 and 9, and
834      * fixed in 11.
835      * </p>
836      *
837      * @param file the file to query.
838      * @return the file's last modified time.
839      * @throws IOException Thrown if an I/O error occurs.
840      * @since 2.12.0
841      */
842     public static FileTime getLastModifiedFileTime(final File file) throws IOException {
843         return getLastModifiedFileTime(file.toPath(), null, EMPTY_LINK_OPTION_ARRAY);
844     }
845 
846     /**
847      * Gets the file's last modified time or null if the file does not exist.
848      *
849      * @param path the file to query.
850      * @param defaultIfAbsent Returns this file time of the file does not exist, may be null.
851      * @param options options indicating how symbolic links are handled.
852      * @return the file's last modified time.
853      * @throws IOException Thrown if an I/O error occurs.
854      * @since 2.12.0
855      */
856     public static FileTime getLastModifiedFileTime(final Path path, final FileTime defaultIfAbsent, final LinkOption... options) throws IOException {
857         return Files.exists(path) ? getLastModifiedTime(path, options) : defaultIfAbsent;
858     }
859 
860     /**
861      * Gets the file's last modified time or null if the file does not exist.
862      *
863      * @param path the file to query.
864      * @param options options indicating how symbolic links are handled.
865      * @return the file's last modified time.
866      * @throws IOException Thrown if an I/O error occurs.
867      * @since 2.12.0
868      */
869     public static FileTime getLastModifiedFileTime(final Path path, final LinkOption... options) throws IOException {
870         return getLastModifiedFileTime(path, null, options);
871     }
872 
873     /**
874      * Gets the file's last modified time or null if the file does not exist.
875      *
876      * @param uri the file to query.
877      * @return the file's last modified time.
878      * @throws IOException Thrown if an I/O error occurs.
879      * @since 2.12.0
880      */
881     public static FileTime getLastModifiedFileTime(final URI uri) throws IOException {
882         return getLastModifiedFileTime(Paths.get(uri), null, EMPTY_LINK_OPTION_ARRAY);
883     }
884 
885     /**
886      * Gets the file's last modified time or null if the file does not exist.
887      *
888      * @param url the file to query.
889      * @return the file's last modified time.
890      * @throws IOException Thrown if an I/O error occurs.
891      * @throws URISyntaxException if the URL is not formatted strictly according to RFC2396 and cannot be converted to a
892      *         URI.
893      * @since 2.12.0
894      */
895     public static FileTime getLastModifiedFileTime(final URL url) throws IOException, URISyntaxException {
896         return getLastModifiedFileTime(url.toURI());
897     }
898 
899     private static FileTime getLastModifiedTime(final Path path, final LinkOption... options) throws IOException {
900         return Files.getLastModifiedTime(Objects.requireNonNull(path, "path"), options);
901     }
902 
903     private static Path getParent(final Path path) {
904         return path == null ? null : path.getParent();
905     }
906 
907     /**
908      * Shorthand for {@code Files.getFileAttributeView(path, PosixFileAttributeView.class)}.
909      *
910      * @param path the path to the file.
911      * @param options how to handle symbolic links.
912      * @return a PosixFileAttributeView, or {@code null} if the attribute view type is not available.
913      * @since 2.12.0
914      */
915     public static PosixFileAttributeView getPosixFileAttributeView(final Path path, final LinkOption... options) {
916         return Files.getFileAttributeView(path, PosixFileAttributeView.class, options);
917     }
918 
919     /**
920      * Gets a {@link Path} representing the system temporary directory.
921      *
922      * @return the system temporary directory.
923      * @since 2.12.0
924      */
925     public static Path getTempDirectory() {
926         return Paths.get(FileUtils.getTempDirectoryPath());
927     }
928 
929     /**
930      * Tests whether the given {@link Path} is a directory or not. Implemented as a null-safe delegate to
931      * {@code Files.isDirectory(Path path, LinkOption... options)}.
932      *
933      * @param path the path to the file.
934      * @param options options indicating how to handle symbolic links
935      * @return {@code true} if the file is a directory; {@code false} if the path is null, the file does not exist, is not a
936      *         directory, or it cannot be determined if the file is a directory or not.
937      * @throws SecurityException In the case of the default provider, and a security manager is installed, the
938      *         {@link SecurityManager#checkRead(String) checkRead} method is invoked to check read access to the directory.
939      * @since 2.9.0
940      */
941     public static boolean isDirectory(final Path path, final LinkOption... options) {
942         return path != null && Files.isDirectory(path, options);
943     }
944 
945     /**
946      * Tests whether the given file or directory is empty.
947      *
948      * @param path the file or directory to query.
949      * @return whether the file or directory is empty.
950      * @throws IOException if an I/O error occurs.
951      */
952     public static boolean isEmpty(final Path path) throws IOException {
953         return Files.isDirectory(path) ? isEmptyDirectory(path) : isEmptyFile(path);
954     }
955 
956     /**
957      * Tests whether the directory is empty.
958      *
959      * @param directory the directory to query.
960      * @return whether the directory is empty.
961      * @throws NotDirectoryException if the file could not otherwise be opened because it is not a directory <i>(optional
962      *         specific exception)</i>.
963      * @throws IOException if an I/O error occurs.
964      * @throws SecurityException In the case of the default provider, and a security manager is installed, the
965      *         {@link SecurityManager#checkRead(String) checkRead} method is invoked to check read access to the directory.
966      */
967     public static boolean isEmptyDirectory(final Path directory) throws IOException {
968         try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(directory)) {
969             return !directoryStream.iterator().hasNext();
970         }
971     }
972 
973     /**
974      * Tests whether the given file is empty.
975      *
976      * @param file the file to query.
977      * @return whether the file is empty.
978      * @throws IOException if an I/O error occurs.
979      * @throws SecurityException In the case of the default provider, and a security manager is installed, its
980      *         {@link SecurityManager#checkRead(String) checkRead} method denies read access to the file.
981      */
982     public static boolean isEmptyFile(final Path file) throws IOException {
983         return Files.size(file) <= 0;
984     }
985 
986     /**
987      * Tests if the given {@link Path} is newer than the given time reference.
988      *
989      * @param file the {@link Path} to test.
990      * @param czdt the time reference.
991      * @param options options indicating how to handle symbolic links.
992      * @return true if the {@link Path} exists and has been modified after the given time reference.
993      * @throws IOException if an I/O error occurs.
994      * @throws NullPointerException if the file is {@code null}.
995      * @since 2.12.0
996      */
997     public static boolean isNewer(final Path file, final ChronoZonedDateTime<?> czdt, final LinkOption... options) throws IOException {
998         Objects.requireNonNull(czdt, "czdt");
999         return isNewer(file, czdt.toInstant(), options);
1000     }
1001 
1002     /**
1003      * Tests if the given {@link Path} is newer than the given time reference.
1004      *
1005      * @param file the {@link Path} to test.
1006      * @param fileTime the time reference.
1007      * @param options options indicating how to handle symbolic links.
1008      * @return true if the {@link Path} exists and has been modified after the given time reference.
1009      * @throws IOException if an I/O error occurs.
1010      * @throws NullPointerException if the file is {@code null}.
1011      * @since 2.12.0
1012      */
1013     public static boolean isNewer(final Path file, final FileTime fileTime, final LinkOption... options) throws IOException {
1014         if (notExists(file)) {
1015             return false;
1016         }
1017         return compareLastModifiedTimeTo(file, fileTime, options) > 0;
1018     }
1019 
1020     /**
1021      * Tests if the given {@link Path} is newer than the given time reference.
1022      *
1023      * @param file the {@link Path} to test.
1024      * @param instant the time reference.
1025      * @param options options indicating how to handle symbolic links.
1026      * @return true if the {@link Path} exists and has been modified after the given time reference.
1027      * @throws IOException if an I/O error occurs.
1028      * @throws NullPointerException if the file is {@code null}.
1029      * @since 2.12.0
1030      */
1031     public static boolean isNewer(final Path file, final Instant instant, final LinkOption... options) throws IOException {
1032         return isNewer(file, FileTime.from(instant), options);
1033     }
1034 
1035     /**
1036      * Tests if the given {@link Path} is newer than the given time reference.
1037      *
1038      * @param file the {@link Path} to test.
1039      * @param timeMillis the time reference measured in milliseconds since the epoch (00:00:00 GMT, January 1, 1970)
1040      * @param options options indicating how to handle symbolic links.
1041      * @return true if the {@link Path} exists and has been modified after the given time reference.
1042      * @throws IOException if an I/O error occurs.
1043      * @throws NullPointerException if the file is {@code null}.
1044      * @since 2.9.0
1045      */
1046     public static boolean isNewer(final Path file, final long timeMillis, final LinkOption... options) throws IOException {
1047         return isNewer(file, FileTime.fromMillis(timeMillis), options);
1048     }
1049 
1050     /**
1051      * Tests if the given {@link Path} is newer than the reference {@link Path}.
1052      *
1053      * @param file the {@link File} to test.
1054      * @param reference the {@link File} of which the modification date is used.
1055      * @return true if the {@link File} exists and has been modified more recently than the reference {@link File}.
1056      * @throws IOException if an I/O error occurs.
1057      * @since 2.12.0
1058      */
1059     public static boolean isNewer(final Path file, final Path reference) throws IOException {
1060         return isNewer(file, getLastModifiedTime(reference));
1061     }
1062 
1063     /**
1064      * Tests if the given {@link Path} is older than the given time reference.
1065      *
1066      * @param file the {@link Path} to test.
1067      * @param fileTime the time reference.
1068      * @param options options indicating how to handle symbolic links.
1069      * @return true if the {@link Path} exists and has been modified before the given time reference.
1070      * @throws IOException if an I/O error occurs.
1071      * @throws NullPointerException if the file is {@code null}.
1072      * @since 2.12.0
1073      */
1074     public static boolean isOlder(final Path file, final FileTime fileTime, final LinkOption... options) throws IOException {
1075         if (notExists(file)) {
1076             return false;
1077         }
1078         return compareLastModifiedTimeTo(file, fileTime, options) < 0;
1079     }
1080 
1081     /**
1082      * Tests if the given {@link Path} is older than the given time reference.
1083      *
1084      * @param file the {@link Path} to test.
1085      * @param instant the time reference.
1086      * @param options options indicating how to handle symbolic links.
1087      * @return true if the {@link Path} exists and has been modified before the given time reference.
1088      * @throws IOException if an I/O error occurs.
1089      * @throws NullPointerException if the file is {@code null}.
1090      * @since 2.12.0
1091      */
1092     public static boolean isOlder(final Path file, final Instant instant, final LinkOption... options) throws IOException {
1093         return isOlder(file, FileTime.from(instant), options);
1094     }
1095 
1096     /**
1097      * Tests if the given {@link Path} is older than the given time reference.
1098      *
1099      * @param file the {@link Path} to test.
1100      * @param timeMillis the time reference measured in milliseconds since the epoch (00:00:00 GMT, January 1, 1970)
1101      * @param options options indicating how to handle symbolic links.
1102      * @return true if the {@link Path} exists and has been modified before the given time reference.
1103      * @throws IOException if an I/O error occurs.
1104      * @throws NullPointerException if the file is {@code null}.
1105      * @since 2.12.0
1106      */
1107     public static boolean isOlder(final Path file, final long timeMillis, final LinkOption... options) throws IOException {
1108         return isOlder(file, FileTime.fromMillis(timeMillis), options);
1109     }
1110 
1111     /**
1112      * Tests if the given {@link Path} is older than the reference {@link Path}.
1113      *
1114      * @param file the {@link File} to test.
1115      * @param reference the {@link File} of which the modification date is used.
1116      * @return true if the {@link File} exists and has been modified before than the reference {@link File}.
1117      * @throws IOException if an I/O error occurs.
1118      * @since 2.12.0
1119      */
1120     public static boolean isOlder(final Path file, final Path reference) throws IOException {
1121         return isOlder(file, getLastModifiedTime(reference));
1122     }
1123 
1124     /**
1125      * Tests whether the given path is on a POSIX file system.
1126      *
1127      * @param test The Path to test.
1128      * @param options options indicating how to handle symbolic links.
1129      * @return true if test is on a POSIX file system.
1130      * @since 2.12.0
1131      */
1132     public static boolean isPosix(final Path test, final LinkOption... options) {
1133         return exists(test, options) && readPosixFileAttributes(test, options) != null;
1134     }
1135 
1136     /**
1137      * Tests whether the given {@link Path} is a regular file or not. Implemented as a null-safe delegate to
1138      * {@code Files.isRegularFile(Path path, LinkOption... options)}.
1139      *
1140      * @param path the path to the file.
1141      * @param options options indicating how to handle symbolic links.
1142      * @return {@code true} if the file is a regular file; {@code false} if the path is null, the file does not exist, is
1143      *         not a directory, or it cannot be determined if the file is a regular file or not.
1144      * @throws SecurityException In the case of the default provider, and a security manager is installed, the
1145      *         {@link SecurityManager#checkRead(String) checkRead} method is invoked to check read access to the directory.
1146      * @since 2.9.0
1147      */
1148     public static boolean isRegularFile(final Path path, final LinkOption... options) {
1149         return path != null && Files.isRegularFile(path, options);
1150     }
1151 
1152     /**
1153      * Creates a new DirectoryStream for Paths rooted at the given directory.
1154      *
1155      * @param dir the path to the directory to stream.
1156      * @param pathFilter the directory stream filter.
1157      * @return a new instance.
1158      * @throws IOException if an I/O error occurs.
1159      */
1160     public static DirectoryStream<Path> newDirectoryStream(final Path dir, final PathFilter pathFilter) throws IOException {
1161         return Files.newDirectoryStream(dir, new DirectoryStreamFilter(pathFilter));
1162     }
1163 
1164     /**
1165      * Creates a new OutputStream by opening or creating a file, returning an output stream that may be used to write bytes
1166      * to the file.
1167      *
1168      * @param path the Path.
1169      * @param append Whether or not to append.
1170      * @return a new OutputStream.
1171      * @throws IOException if an I/O error occurs.
1172      * @see Files#newOutputStream(Path, OpenOption...)
1173      * @since 2.12.0
1174      */
1175     public static OutputStream newOutputStream(final Path path, final boolean append) throws IOException {
1176         return newOutputStream(path, EMPTY_LINK_OPTION_ARRAY, append ? OPEN_OPTIONS_APPEND : OPEN_OPTIONS_TRUNCATE);
1177     }
1178 
1179     static OutputStream newOutputStream(final Path path, final LinkOption[] linkOptions, final OpenOption... openOptions) throws IOException {
1180         if (!exists(path, linkOptions)) {
1181             createParentDirectories(path, linkOptions != null && linkOptions.length > 0 ? linkOptions[0] : NULL_LINK_OPTION);
1182         }
1183         final List<OpenOption> list = new ArrayList<>(Arrays.asList(openOptions != null ? openOptions : EMPTY_OPEN_OPTION_ARRAY));
1184         list.addAll(Arrays.asList(linkOptions != null ? linkOptions : EMPTY_LINK_OPTION_ARRAY));
1185         return Files.newOutputStream(path, list.toArray(EMPTY_OPEN_OPTION_ARRAY));
1186     }
1187 
1188     /**
1189      * Copy of the {@link LinkOption} array for {@link LinkOption#NOFOLLOW_LINKS}.
1190      *
1191      * @return Copy of the {@link LinkOption} array for {@link LinkOption#NOFOLLOW_LINKS}.
1192      */
1193     public static LinkOption[] noFollowLinkOptionArray() {
1194         return NOFOLLOW_LINK_OPTION_ARRAY.clone();
1195     }
1196 
1197     private static boolean notExists(final Path path, final LinkOption... options) {
1198         return Files.notExists(Objects.requireNonNull(path, "path"), options);
1199     }
1200 
1201     /**
1202      * Returns true if the given options contain {@link StandardDeleteOption#OVERRIDE_READ_ONLY}.
1203      *
1204      * @param deleteOptions the array to test
1205      * @return true if the given options contain {@link StandardDeleteOption#OVERRIDE_READ_ONLY}.
1206      */
1207     private static boolean overrideReadOnly(final DeleteOption... deleteOptions) {
1208         if (deleteOptions == null) {
1209             return false;
1210         }
1211         return Stream.of(deleteOptions).anyMatch(e -> e == StandardDeleteOption.OVERRIDE_READ_ONLY);
1212     }
1213 
1214     /**
1215      * Reads the BasicFileAttributes from the given path. Returns null instead of throwing
1216      * {@link UnsupportedOperationException}. Throws {@link Uncheck} instead of {@link IOException}.
1217      *
1218      * @param <A> The {@link BasicFileAttributes} type
1219      * @param path The Path to test.
1220      * @param type the {@link Class} of the file attributes required to read.
1221      * @param options options indicating how to handle symbolic links.
1222      * @return the file attributes.
1223      * @see Files#readAttributes(Path, Class, LinkOption...)
1224      * @since 2.12.0
1225      */
1226     public static <A extends BasicFileAttributes> A readAttributes(final Path path, final Class<A> type, final LinkOption... options) {
1227         try {
1228             return path == null ? null : Uncheck.apply(Files::readAttributes, path, type, options);
1229         } catch (final UnsupportedOperationException e) {
1230             // For example, on Windows.
1231             return null;
1232         }
1233     }
1234 
1235     /**
1236      * Reads the BasicFileAttributes from the given path.
1237      *
1238      * @param path the path to read.
1239      * @return the path attributes.
1240      * @throws IOException if an I/O error occurs.
1241      * @since 2.9.0
1242      * @deprecated Will be removed in 3.0.0 in favor of {@link #readBasicFileAttributes(Path, LinkOption...)}.
1243      */
1244     @Deprecated
1245     public static BasicFileAttributes readBasicFileAttributes(final Path path) throws IOException {
1246         return Files.readAttributes(path, BasicFileAttributes.class);
1247     }
1248 
1249     /**
1250      * Reads the BasicFileAttributes from the given path. Returns null instead of throwing
1251      * {@link UnsupportedOperationException}.
1252      *
1253      * @param path the path to read.
1254      * @param options options indicating how to handle symbolic links.
1255      * @return the path attributes.
1256      * @since 2.12.0
1257      */
1258     public static BasicFileAttributes readBasicFileAttributes(final Path path, final LinkOption... options) {
1259         return readAttributes(path, BasicFileAttributes.class, options);
1260     }
1261 
1262     /**
1263      * Reads the BasicFileAttributes from the given path. Returns null instead of throwing
1264      * {@link UnsupportedOperationException}.
1265      *
1266      * @param path the path to read.
1267      * @return the path attributes.
1268      * @throws UncheckedIOException if an I/O error occurs
1269      * @since 2.9.0
1270      * @deprecated Use {@link #readBasicFileAttributes(Path, LinkOption...)}.
1271      */
1272     @Deprecated
1273     public static BasicFileAttributes readBasicFileAttributesUnchecked(final Path path) {
1274         return readBasicFileAttributes(path, EMPTY_LINK_OPTION_ARRAY);
1275     }
1276 
1277     /**
1278      * Reads the DosFileAttributes from the given path. Returns null instead of throwing
1279      * {@link UnsupportedOperationException}.
1280      *
1281      * @param path the path to read.
1282      * @param options options indicating how to handle symbolic links.
1283      * @return the path attributes.
1284      * @since 2.12.0
1285      */
1286     public static DosFileAttributes readDosFileAttributes(final Path path, final LinkOption... options) {
1287         return readAttributes(path, DosFileAttributes.class, options);
1288     }
1289 
1290     private static Path readIfSymbolicLink(final Path path) throws IOException {
1291         return path != null ? Files.isSymbolicLink(path) ? Files.readSymbolicLink(path) : path : null;
1292     }
1293 
1294     /**
1295      * Reads the PosixFileAttributes or DosFileAttributes from the given path. Returns null instead of throwing
1296      * {@link UnsupportedOperationException}.
1297      *
1298      * @param path The Path to read.
1299      * @param options options indicating how to handle symbolic links.
1300      * @return the file attributes.
1301      * @since 2.12.0
1302      */
1303     public static BasicFileAttributes readOsFileAttributes(final Path path, final LinkOption... options) {
1304         final PosixFileAttributes fileAttributes = readPosixFileAttributes(path, options);
1305         return fileAttributes != null ? fileAttributes : readDosFileAttributes(path, options);
1306     }
1307 
1308     /**
1309      * Reads the PosixFileAttributes from the given path. Returns null instead of throwing
1310      * {@link UnsupportedOperationException}.
1311      *
1312      * @param path The Path to read.
1313      * @param options options indicating how to handle symbolic links.
1314      * @return the file attributes.
1315      * @since 2.12.0
1316      */
1317     public static PosixFileAttributes readPosixFileAttributes(final Path path, final LinkOption... options) {
1318         return readAttributes(path, PosixFileAttributes.class, options);
1319     }
1320 
1321     /**
1322      * Reads the given path as a String.
1323      *
1324      * @param path The source path.
1325      * @param charset How to convert bytes to a String, null uses the default Charset.
1326      * @return a new String.
1327      * @throws IOException if an I/O error occurs reading from the stream.
1328      * @see Files#readAllBytes(Path)
1329      * @since 2.12.0
1330      */
1331     public static String readString(final Path path, final Charset charset) throws IOException {
1332         return new String(Files.readAllBytes(path), Charsets.toCharset(charset));
1333     }
1334 
1335     /**
1336      * Relativizes all files in the given {@code collection} against a {@code parent}.
1337      *
1338      * @param collection The collection of paths to relativize.
1339      * @param parent relativizes against this parent path.
1340      * @param sort Whether to sort the result.
1341      * @param comparator How to sort.
1342      * @return A collection of relativized paths, optionally sorted.
1343      */
1344     static List<Path> relativize(final Collection<Path> collection, final Path parent, final boolean sort, final Comparator<? super Path> comparator) {
1345         Stream<Path> stream = collection.stream().map(parent::relativize);
1346         if (sort) {
1347             stream = comparator == null ? stream.sorted() : stream.sorted(comparator);
1348         }
1349         return stream.collect(Collectors.toList());
1350     }
1351 
1352     /**
1353      * Requires that the given {@link File} exists and throws an {@link IllegalArgumentException} if it doesn't.
1354      *
1355      * @param file The {@link File} to check.
1356      * @param fileParamName The parameter name to use in the exception message in case of {@code null} input.
1357      * @param options options indicating how to handle symbolic links.
1358      * @return the given file.
1359      * @throws NullPointerException if the given {@link File} is {@code null}.
1360      * @throws IllegalArgumentException if the given {@link File} does not exist.
1361      */
1362     private static Path requireExists(final Path file, final String fileParamName, final LinkOption... options) {
1363         Objects.requireNonNull(file, fileParamName);
1364         if (!exists(file, options)) {
1365             throw new IllegalArgumentException("File system element for parameter '" + fileParamName + "' does not exist: '" + file + "'");
1366         }
1367         return file;
1368     }
1369 
1370     private static boolean setDosReadOnly(final Path path, final boolean readOnly, final LinkOption... linkOptions) throws IOException {
1371         final DosFileAttributeView dosFileAttributeView = getDosFileAttributeView(path, linkOptions);
1372         if (dosFileAttributeView != null) {
1373             dosFileAttributeView.setReadOnly(readOnly);
1374             return true;
1375         }
1376         return false;
1377     }
1378 
1379     /**
1380      * Sets the given {@code targetFile}'s last modified time to the value from {@code sourceFile}.
1381      *
1382      * @param sourceFile The source path to query.
1383      * @param targetFile The target path to set.
1384      * @throws NullPointerException if sourceFile is {@code null}.
1385      * @throws NullPointerException if targetFile is {@code null}.
1386      * @throws IOException if setting the last-modified time failed.
1387      * @since 2.12.0
1388      */
1389     public static void setLastModifiedTime(final Path sourceFile, final Path targetFile) throws IOException {
1390         Objects.requireNonNull(sourceFile, "sourceFile");
1391         Files.setLastModifiedTime(targetFile, getLastModifiedTime(sourceFile));
1392     }
1393 
1394     /**
1395      * To delete a file in POSIX, you need Write and Execute permissions on its parent directory.
1396      *
1397      * @param parent The parent path for a file element to delete which needs RW permissions.
1398      * @param enableDeleteChildren true to set permissions to delete.
1399      * @param linkOptions options indicating how handle symbolic links.
1400      * @return true if the operation was attempted and succeeded, false if parent is null.
1401      * @throws IOException if an I/O error occurs.
1402      */
1403     private static boolean setPosixDeletePermissions(final Path parent, final boolean enableDeleteChildren, final LinkOption... linkOptions)
1404         throws IOException {
1405         // To delete a file in POSIX, you need write and execute permissions on its parent directory.
1406         // @formatter:off
1407         return setPosixPermissions(parent, enableDeleteChildren, Arrays.asList(
1408             PosixFilePermission.OWNER_WRITE,
1409             //PosixFilePermission.GROUP_WRITE,
1410             //PosixFilePermission.OTHERS_WRITE,
1411             PosixFilePermission.OWNER_EXECUTE
1412             //PosixFilePermission.GROUP_EXECUTE,
1413             //PosixFilePermission.OTHERS_EXECUTE
1414             ), linkOptions);
1415         // @formatter:on
1416     }
1417 
1418     /**
1419      * Low-level POSIX permission operation to set permissions.
1420      *
1421      * @param path Set this path's permissions.
1422      * @param addPermissions true to add, false to remove.
1423      * @param updatePermissions the List of PosixFilePermission to add or remove.
1424      * @param linkOptions options indicating how handle symbolic links.
1425      * @return true if the operation was attempted and succeeded, false if parent is null.
1426      * @throws IOException if an I/O error occurs.
1427      */
1428     private static boolean setPosixPermissions(final Path path, final boolean addPermissions, final List<PosixFilePermission> updatePermissions,
1429         final LinkOption... linkOptions) throws IOException {
1430         if (path != null) {
1431             final Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(path, linkOptions);
1432             if (addPermissions) {
1433                 permissions.addAll(updatePermissions);
1434             } else {
1435                 permissions.removeAll(updatePermissions);
1436             }
1437             Files.setPosixFilePermissions(path, permissions);
1438             return true;
1439         }
1440         return false;
1441     }
1442 
1443     private static void setPosixReadOnlyFile(final Path path, final boolean readOnly, final LinkOption... linkOptions) throws IOException {
1444         // Not Windows 10
1445         final Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(path, linkOptions);
1446         // @formatter:off
1447         final List<PosixFilePermission> readPermissions = Arrays.asList(
1448                 PosixFilePermission.OWNER_READ
1449                 //PosixFilePermission.GROUP_READ,
1450                 //PosixFilePermission.OTHERS_READ
1451             );
1452         final List<PosixFilePermission> writePermissions = Arrays.asList(
1453                 PosixFilePermission.OWNER_WRITE
1454                 //PosixFilePermission.GROUP_WRITE,
1455                 //PosixFilePermission.OTHERS_WRITE
1456             );
1457         // @formatter:on
1458         if (readOnly) {
1459             // RO: We can read, we cannot write.
1460             permissions.addAll(readPermissions);
1461             permissions.removeAll(writePermissions);
1462         } else {
1463             // Not RO: We can read, we can write.
1464             permissions.addAll(readPermissions);
1465             permissions.addAll(writePermissions);
1466         }
1467         Files.setPosixFilePermissions(path, permissions);
1468     }
1469 
1470     /**
1471      * Sets the given Path to the {@code readOnly} value.
1472      * <p>
1473      * This behavior is OS dependent.
1474      * </p>
1475      *
1476      * @param path The path to set.
1477      * @param readOnly true for read-only, false for not read-only.
1478      * @param linkOptions options indicating how to handle symbolic links.
1479      * @return The given path.
1480      * @throws IOException if an I/O error occurs.
1481      * @since 2.8.0
1482      */
1483     public static Path setReadOnly(final Path path, final boolean readOnly, final LinkOption... linkOptions) throws IOException {
1484         try {
1485             // Windows is simplest
1486             if (setDosReadOnly(path, readOnly, linkOptions)) {
1487                 return path;
1488             }
1489         } catch (final IOException ignored) {
1490             // Retry with POSIX below.
1491         }
1492         final Path parent = getParent(path);
1493         if (!isPosix(parent, linkOptions)) { // Test parent because we may not the permissions to test the file.
1494             throw new IOException(String.format("DOS or POSIX file operations not available for '%s' %s", path, Arrays.toString(linkOptions)));
1495         }
1496         // POSIX
1497         if (readOnly) {
1498             // RO
1499             // File, then parent dir (if any).
1500             setPosixReadOnlyFile(path, readOnly, linkOptions);
1501             setPosixDeletePermissions(parent, false, linkOptions);
1502         } else {
1503             // RE
1504             // Parent dir (if any), then file.
1505             setPosixDeletePermissions(parent, true, linkOptions);
1506         }
1507         return path;
1508     }
1509 
1510     /**
1511      * Returns the size of the given file or directory. If the provided {@link Path} is a regular file, then the file's size
1512      * is returned. If the argument is a directory, then the size of the directory is calculated recursively.
1513      * <p>
1514      * Note that overflow is not detected, and the return value may be negative if overflow occurs. See
1515      * {@link #sizeOfAsBigInteger(Path)} for an alternative method that does not overflow.
1516      * </p>
1517      *
1518      * @param path the regular file or directory to return the size of, must not be {@code null}.
1519      * @return the length of the file, or recursive size of the directory, in bytes.
1520      * @throws NullPointerException if the file is {@code null}.
1521      * @throws IllegalArgumentException if the file does not exist.
1522      * @throws IOException if an I/O error occurs.
1523      * @since 2.12.0
1524      */
1525     public static long sizeOf(final Path path) throws IOException {
1526         requireExists(path, "path");
1527         return Files.isDirectory(path) ? sizeOfDirectory(path) : Files.size(path);
1528     }
1529 
1530     /**
1531      * Returns the size of the given file or directory. If the provided {@link Path} is a regular file, then the file's size
1532      * is returned. If the argument is a directory, then the size of the directory is calculated recursively.
1533      *
1534      * @param path the regular file or directory to return the size of (must not be {@code null}).
1535      * @return the length of the file, or recursive size of the directory, provided (in bytes).
1536      * @throws NullPointerException if the file is {@code null}.
1537      * @throws IllegalArgumentException if the file does not exist.
1538      * @throws IOException if an I/O error occurs.
1539      * @since 2.12.0
1540      */
1541     public static BigInteger sizeOfAsBigInteger(final Path path) throws IOException {
1542         requireExists(path, "path");
1543         return Files.isDirectory(path) ? sizeOfDirectoryAsBigInteger(path) : BigInteger.valueOf(Files.size(path));
1544     }
1545 
1546     /**
1547      * Counts the size of a directory recursively (sum of the size of all files).
1548      * <p>
1549      * Note that overflow is not detected, and the return value may be negative if overflow occurs. See
1550      * {@link #sizeOfDirectoryAsBigInteger(Path)} for an alternative method that does not overflow.
1551      * </p>
1552      *
1553      * @param directory directory to inspect, must not be {@code null}.
1554      * @return size of directory in bytes, 0 if directory is security restricted, a negative number when the real total is
1555      *         greater than {@link Long#MAX_VALUE}.
1556      * @throws NullPointerException if the directory is {@code null}.
1557      * @throws IOException if an I/O error occurs.
1558      * @since 2.12.0
1559      */
1560     public static long sizeOfDirectory(final Path directory) throws IOException {
1561         return countDirectory(directory).getByteCounter().getLong();
1562     }
1563 
1564     /**
1565      * Counts the size of a directory recursively (sum of the size of all files).
1566      *
1567      * @param directory directory to inspect, must not be {@code null}.
1568      * @return size of directory in bytes, 0 if directory is security restricted.
1569      * @throws NullPointerException if the directory is {@code null}.
1570      * @throws IOException if an I/O error occurs.
1571      * @since 2.12.0
1572      */
1573     public static BigInteger sizeOfDirectoryAsBigInteger(final Path directory) throws IOException {
1574         return countDirectoryAsBigInteger(directory).getByteCounter().getBigInteger();
1575     }
1576 
1577     /**
1578      * Converts an array of {@link FileVisitOption} to a {@link Set}.
1579      *
1580      * @param fileVisitOptions input array.
1581      * @return a new Set.
1582      */
1583     static Set<FileVisitOption> toFileVisitOptionSet(final FileVisitOption... fileVisitOptions) {
1584         return fileVisitOptions == null ? EnumSet.noneOf(FileVisitOption.class) : Stream.of(fileVisitOptions).collect(Collectors.toSet());
1585     }
1586 
1587     /**
1588      * Implements behavior similar to the Unix "touch" utility. Creates a new file with size 0, or, if the file exists, just
1589      * updates the file's modified time.
1590      *
1591      * @param file the file to touch.
1592      * @return The given file.
1593      * @throws NullPointerException if the parameter is {@code null}.
1594      * @throws IOException if setting the last-modified time failed or an I/O problem occurs.\
1595      * @since 2.12.0
1596      */
1597     public static Path touch(final Path file) throws IOException {
1598         Objects.requireNonNull(file, "file");
1599         if (!Files.exists(file)) {
1600             Files.createFile(file);
1601         } else {
1602             FileTimes.setLastModifiedTime(file);
1603         }
1604         return file;
1605     }
1606 
1607     /**
1608      * Performs {@link Files#walkFileTree(Path,FileVisitor)} and returns the given visitor.
1609      *
1610      * Note that {@link Files#walkFileTree(Path,FileVisitor)} returns the given path.
1611      *
1612      * @param visitor See {@link Files#walkFileTree(Path,FileVisitor)}.
1613      * @param directory See {@link Files#walkFileTree(Path,FileVisitor)}.
1614      * @param <T> See {@link Files#walkFileTree(Path,FileVisitor)}.
1615      * @return the given visitor.
1616      *
1617      * @throws IOException if an I/O error is thrown by a visitor method.
1618      * @throws NullPointerException if the directory is {@code null}.
1619      */
1620     public static <T extends FileVisitor<? super Path>> T visitFileTree(final T visitor, final Path directory) throws IOException {
1621         requireExists(directory, "directory");
1622         Files.walkFileTree(directory, visitor);
1623         return visitor;
1624     }
1625 
1626     /**
1627      * Performs {@link Files#walkFileTree(Path,FileVisitor)} and returns the given visitor.
1628      *
1629      * Note that {@link Files#walkFileTree(Path,FileVisitor)} returns the given path.
1630      *
1631      * @param start See {@link Files#walkFileTree(Path,Set,int,FileVisitor)}.
1632      * @param options See {@link Files#walkFileTree(Path,Set,int,FileVisitor)}.
1633      * @param maxDepth See {@link Files#walkFileTree(Path,Set,int,FileVisitor)}.
1634      * @param visitor See {@link Files#walkFileTree(Path,Set,int,FileVisitor)}.
1635      * @param <T> See {@link Files#walkFileTree(Path,Set,int,FileVisitor)}.
1636      * @return the given visitor.
1637      *
1638      * @throws IOException if an I/O error is thrown by a visitor method.
1639      */
1640     public static <T extends FileVisitor<? super Path>> T visitFileTree(final T visitor, final Path start, final Set<FileVisitOption> options,
1641         final int maxDepth) throws IOException {
1642         Files.walkFileTree(start, options, maxDepth, visitor);
1643         return visitor;
1644     }
1645 
1646     /**
1647      * Performs {@link Files#walkFileTree(Path,FileVisitor)} and returns the given visitor.
1648      *
1649      * Note that {@link Files#walkFileTree(Path,FileVisitor)} returns the given path.
1650      *
1651      * @param visitor See {@link Files#walkFileTree(Path,FileVisitor)}.
1652      * @param first See {@link Paths#get(String,String[])}.
1653      * @param more See {@link Paths#get(String,String[])}.
1654      * @param <T> See {@link Files#walkFileTree(Path,FileVisitor)}.
1655      * @return the given visitor.
1656      *
1657      * @throws IOException if an I/O error is thrown by a visitor method.
1658      */
1659     public static <T extends FileVisitor<? super Path>> T visitFileTree(final T visitor, final String first, final String... more) throws IOException {
1660         return visitFileTree(visitor, Paths.get(first, more));
1661     }
1662 
1663     /**
1664      * Performs {@link Files#walkFileTree(Path,FileVisitor)} and returns the given visitor.
1665      *
1666      * Note that {@link Files#walkFileTree(Path,FileVisitor)} returns the given path.
1667      *
1668      * @param visitor See {@link Files#walkFileTree(Path,FileVisitor)}.
1669      * @param uri See {@link Paths#get(URI)}.
1670      * @param <T> See {@link Files#walkFileTree(Path,FileVisitor)}.
1671      * @return the given visitor.
1672      *
1673      * @throws IOException if an I/O error is thrown by a visitor method.
1674      */
1675     public static <T extends FileVisitor<? super Path>> T visitFileTree(final T visitor, final URI uri) throws IOException {
1676         return visitFileTree(visitor, Paths.get(uri));
1677     }
1678 
1679     /**
1680      * Waits for the file system to propagate a file creation, with a timeout.
1681      * <p>
1682      * This method repeatedly tests {@link Files#exists(Path,LinkOption...)} until it returns true up to the maximum time
1683      * given.
1684      * </p>
1685      *
1686      * @param file the file to check, must not be {@code null}.
1687      * @param timeout the maximum time to wait.
1688      * @param options options indicating how to handle symbolic links.
1689      * @return true if file exists.
1690      * @throws NullPointerException if the file is {@code null}.
1691      * @since 2.12.0
1692      */
1693     public static boolean waitFor(final Path file, final Duration timeout, final LinkOption... options) {
1694         Objects.requireNonNull(file, "file");
1695         final Instant finishInstant = Instant.now().plus(timeout);
1696         boolean interrupted = false;
1697         final long minSleepMillis = 100;
1698         try {
1699             while (!exists(file, options)) {
1700                 final Instant now = Instant.now();
1701                 if (now.isAfter(finishInstant)) {
1702                     return false;
1703                 }
1704                 try {
1705                     ThreadUtils.sleep(Duration.ofMillis(Math.min(minSleepMillis, finishInstant.minusMillis(now.toEpochMilli()).toEpochMilli())));
1706                 } catch (final InterruptedException ignore) {
1707                     interrupted = true;
1708                 } catch (final Exception ex) {
1709                     break;
1710                 }
1711             }
1712         } finally {
1713             if (interrupted) {
1714                 Thread.currentThread().interrupt();
1715             }
1716         }
1717         return exists(file, options);
1718     }
1719 
1720     /**
1721      * Returns a stream of filtered paths.
1722      *
1723      * @param start the start path
1724      * @param pathFilter the path filter
1725      * @param maxDepth the maximum depth of directories to walk.
1726      * @param readAttributes whether to call the filters with file attributes (false passes null).
1727      * @param options the options to configure the walk.
1728      * @return a filtered stream of paths.
1729      * @throws IOException if an I/O error is thrown when accessing the starting file.
1730      * @since 2.9.0
1731      */
1732     public static Stream<Path> walk(final Path start, final PathFilter pathFilter, final int maxDepth, final boolean readAttributes,
1733         final FileVisitOption... options) throws IOException {
1734         return Files.walk(start, maxDepth, options)
1735             .filter(path -> pathFilter.accept(path, readAttributes ? readBasicFileAttributesUnchecked(path) : null) == FileVisitResult.CONTINUE);
1736     }
1737 
1738     private static <R> R withPosixFileAttributes(final Path path, final LinkOption[] linkOptions, final boolean overrideReadOnly,
1739         final IOFunction<PosixFileAttributes, R> function) throws IOException {
1740         final PosixFileAttributes posixFileAttributes = overrideReadOnly ? readPosixFileAttributes(path, linkOptions) : null;
1741         try {
1742             return function.apply(posixFileAttributes);
1743         } finally {
1744             if (posixFileAttributes != null && path != null && Files.exists(path, linkOptions)) {
1745                 Files.setPosixFilePermissions(path, posixFileAttributes.permissions());
1746             }
1747         }
1748     }
1749 
1750     /**
1751      * Writes the given character sequence to a file at the given path.
1752      *
1753      * @param path The target file.
1754      * @param charSequence The character sequence text.
1755      * @param charset The Charset to encode the text.
1756      * @param openOptions options How to open the file.
1757      * @return The given path.
1758      * @throws IOException if an I/O error occurs writing to or creating the file.
1759      * @throws NullPointerException if either {@code path} or {@code charSequence} is {@code null}.
1760      * @since 2.12.0
1761      */
1762     public static Path writeString(final Path path, final CharSequence charSequence, final Charset charset, final OpenOption... openOptions)
1763         throws IOException {
1764         // Check the text is not null before opening file.
1765         Objects.requireNonNull(path, "path");
1766         Objects.requireNonNull(charSequence, "charSequence");
1767         Files.write(path, String.valueOf(charSequence).getBytes(Charsets.toCharset(charset)), openOptions);
1768         return path;
1769     }
1770 
1771     /**
1772      * Does allow to instantiate.
1773      */
1774     private PathUtils() {
1775         // do not instantiate.
1776     }
1777 
1778 }
1779