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