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