    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 *      https://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 */
    017package org.apache.commons.io;
    018
    019import java.io.BufferedInputStream;
    020import java.io.BufferedOutputStream;
    021import java.io.File;
    022import java.io.FileFilter;
    023import java.io.FileInputStream;
    024import java.io.FileNotFoundException;
    025import java.io.FileOutputStream;
    026import java.io.FilenameFilter;
    027import java.io.IOException;
    028import java.io.InputStream;
    029import java.io.InputStreamReader;
    030import java.io.OutputStream;
    031import java.io.Reader;
    032import java.io.UncheckedIOException;
    033import java.math.BigInteger;
    034import java.net.URL;
    035import java.nio.ByteBuffer;
    036import java.nio.charset.Charset;
    037import java.nio.charset.StandardCharsets;
    038import java.nio.charset.UnsupportedCharsetException;
    039import java.nio.file.CopyOption;
    040import java.nio.file.DirectoryStream;
    041import java.nio.file.FileVisitOption;
    042import java.nio.file.FileVisitResult;
    043import java.nio.file.Files;
    044import java.nio.file.LinkOption;
    045import java.nio.file.NoSuchFileException;
    046import java.nio.file.NotDirectoryException;
    047import java.nio.file.Path;
    048import java.nio.file.StandardCopyOption;
    049import java.nio.file.attribute.BasicFileAttributeView;
    050import java.nio.file.attribute.BasicFileAttributes;
    051import java.nio.file.attribute.FileTime;
    052import java.time.Duration;
    053import java.time.Instant;
    054import java.time.LocalTime;
    055import java.time.OffsetDateTime;
    056import java.time.OffsetTime;
    057import java.time.ZoneId;
    058import java.time.chrono.ChronoLocalDate;
    059import java.time.chrono.ChronoLocalDateTime;
    060import java.time.chrono.ChronoZonedDateTime;
    061import java.util.ArrayList;
    062import java.util.Arrays;
    063import java.util.Collection;
    064import java.util.Collections;
    065import java.util.Date;
    066import java.util.HashSet;
    067import java.util.Iterator;
    068import java.util.List;
    069import java.util.Objects;
    070import java.util.Set;
    071import java.util.stream.Collectors;
    072import java.util.stream.Stream;
    073import java.util.zip.CRC32;
    074import java.util.zip.CheckedInputStream;
    075import java.util.zip.Checksum;
    076
    077import org.apache.commons.io.file.AccumulatorPathVisitor;
    078import org.apache.commons.io.file.Counters;
    079import org.apache.commons.io.file.PathFilter;
    080import org.apache.commons.io.file.PathUtils;
    081import org.apache.commons.io.file.StandardDeleteOption;
    082import org.apache.commons.io.filefilter.FileEqualsFileFilter;
    083import org.apache.commons.io.filefilter.FileFileFilter;
    084import org.apache.commons.io.filefilter.IOFileFilter;
    085import org.apache.commons.io.filefilter.SuffixFileFilter;
    086import org.apache.commons.io.filefilter.TrueFileFilter;
    087import org.apache.commons.io.function.IOConsumer;
    088import org.apache.commons.io.function.Uncheck;
    089
    090/**
    091 * General file manipulation utilities.
    092 * <p>
    093 * Facilities are provided in the following areas:
    094 * </p>
    095 * <ul>
    096 * <li>writing to a file</li>
    097 * <li>reading from a file</li>
    098 * <li>make a directory including parent directories</li>
    099 * <li>copying files and directories</li>
    100 * <li>deleting files and directories</li>
    101 * <li>converting to and from a URL</li>
    102 * <li>listing files and directories by filter and extension</li>
    103 * <li>comparing file content</li>
    104 * <li>file last changed date</li>
    105 * <li>calculating a checksum</li>
    106 * </ul>
    107 * <p>
    108 * Note that a specific charset should be specified whenever possible. Relying on the platform default means that the
    109 * code is Locale-dependent. Only use the default if the files are known to always use the platform default.
    110 * </p>
    111 * <p>
    112 * {@link SecurityException} are not documented in the Javadoc.
    113 * </p>
    114 * <p>
    115 * Provenance: Excalibur, Alexandria, Commons-Utils.
    116 * </p>
    117 */
    118public class FileUtils {
    119
    120    private static final String PROTOCOL_FILE = "file";
    121
    122    /**
    123     * The number of bytes in a kilobyte.
    124     */
    125    public static final long ONE_KB = 1024;
    126
    127    /**
    128     * The number of bytes in a kilobyte.
    129     *
    130     * @since 2.4
    131     */
    132    public static final BigInteger ONE_KB_BI = BigInteger.valueOf(ONE_KB);
    133
    134    /**
    135     * The number of bytes in a megabyte.
    136     */
    137    public static final long ONE_MB = ONE_KB * ONE_KB;
    138
    139    /**
    140     * The number of bytes in a megabyte.
    141     *
    142     * @since 2.4
    143     */
    144    public static final BigInteger ONE_MB_BI = ONE_KB_BI.multiply(ONE_KB_BI);
    145
    146    /**
    147     * The number of bytes in a gigabyte.
    148     */
    149    public static final long ONE_GB = ONE_KB * ONE_MB;
    150
    151    /**
    152     * The number of bytes in a gigabyte.
    153     *
    154     * @since 2.4
    155     */
    156    public static final BigInteger ONE_GB_BI = ONE_KB_BI.multiply(ONE_MB_BI);
    157
    158    /**
    159     * The number of bytes in a terabyte.
    160     */
    161    public static final long ONE_TB = ONE_KB * ONE_GB;
    162
    163    /**
    164     * The number of bytes in a terabyte.
    165     *
    166     * @since 2.4
    167     */
    168    public static final BigInteger ONE_TB_BI = ONE_KB_BI.multiply(ONE_GB_BI);
    169
    170    /**
    171     * The number of bytes in a petabyte.
    172     */
    173    public static final long ONE_PB = ONE_KB * ONE_TB;
    174
    175    /**
    176     * The number of bytes in a petabyte.
    177     *
    178     * @since 2.4
    179     */
    180    public static final BigInteger ONE_PB_BI = ONE_KB_BI.multiply(ONE_TB_BI);
    181
    182    /**
    183     * The number of bytes in an exabyte.
    184     */
    185    public static final long ONE_EB = ONE_KB * ONE_PB;
    186
    187    /**
    188     * The number of bytes in an exabyte.
    189     *
    190     * @since 2.4
    191     */
    192    public static final BigInteger ONE_EB_BI = ONE_KB_BI.multiply(ONE_PB_BI);
    193
    194    /**
    195     * The number of bytes in a zettabyte.
    196     */
    197    public static final BigInteger ONE_ZB = ONE_KB_BI.multiply(ONE_EB_BI);
    198
    199    /**
    200     * The number of bytes in a yottabyte.
    201     */
    202    public static final BigInteger ONE_YB = ONE_KB_BI.multiply(ONE_ZB);
    203
    204    /**
    205     * The number of bytes in a ronnabyte.
    206     *
    207     * @since 2.21.0
    208     */
    209    public static final BigInteger ONE_RB = ONE_KB_BI.multiply(ONE_YB);
    210
    211    /**
    212     * The number of bytes in a quettabyte.
    213     *
    214     * @since 2.21.0
    215     */
    216    public static final BigInteger ONE_QB = ONE_KB_BI.multiply(ONE_RB);
    217
    218    /**
    219     * An empty array of type {@link File}.
    220     */
    221    public static final File[] EMPTY_FILE_ARRAY = {};
    222
    223    /**
    224     * Returns a human-readable version of the file size, where the input represents a specific number of bytes.
    225     * <p>
    226     * If the size is over 1GB, the size is returned as the number of whole GB, the size is rounded down to the
    227     * nearest GB boundary.
    228     * </p>
    229     * <p>
    230     * Similarly for the 1MB and 1KB boundaries.
    231     * </p>
    232     *
    233     * @param size the number of bytes.
    234     * @return a human-readable display value (includes units - QB, RB, YB, ZB, EB, PB, TB, GB, MB, KB or bytes).
    235     * @throws NullPointerException if the given {@link BigInteger} is {@code null}.
    236     * @see <a href="https://issues.apache.org/jira/browse/IO-226">IO-226 - should the rounding be changed?</a>
    237     * @since 2.4
    238     */
    239    // See https://issues.apache.org/jira/browse/IO-226 - should the rounding be changed?
    240    public static String byteCountToDisplaySize(final BigInteger size) {
    241        Objects.requireNonNull(size, "size");
    242        final String displaySize;
    243        if (size.divide(ONE_QB).compareTo(BigInteger.ZERO) > 0) {
    244            displaySize = size.divide(ONE_QB) + " QB";
    245        } else if (size.divide(ONE_RB).compareTo(BigInteger.ZERO) > 0) {
    246            displaySize = size.divide(ONE_RB) + " RB";
    247        } else if (size.divide(ONE_YB).compareTo(BigInteger.ZERO) > 0) {
    248            displaySize = size.divide(ONE_YB) + " YB";
    249        } else if (size.divide(ONE_ZB).compareTo(BigInteger.ZERO) > 0) {
    250            displaySize = size.divide(ONE_ZB) + " ZB";
    251        } else if (size.divide(ONE_EB_BI).compareTo(BigInteger.ZERO) > 0) {
    252            displaySize = size.divide(ONE_EB_BI) + " EB";
    253        } else if (size.divide(ONE_PB_BI).compareTo(BigInteger.ZERO) > 0) {
    254            displaySize = size.divide(ONE_PB_BI) + " PB";
    255        } else if (size.divide(ONE_TB_BI).compareTo(BigInteger.ZERO) > 0) {
    256            displaySize = size.divide(ONE_TB_BI) + " TB";
    257        } else if (size.divide(ONE_GB_BI).compareTo(BigInteger.ZERO) > 0) {
    258            displaySize = size.divide(ONE_GB_BI) + " GB";
    259        } else if (size.divide(ONE_MB_BI).compareTo(BigInteger.ZERO) > 0) {
    260            displaySize = size.divide(ONE_MB_BI) + " MB";
    261        } else if (size.divide(ONE_KB_BI).compareTo(BigInteger.ZERO) > 0) {
    262            displaySize = size.divide(ONE_KB_BI) + " KB";
    263        } else {
    264            displaySize = size + " bytes";
    265        }
    266        return displaySize;
    267    }
    268
    269    /**
    270     * Returns a human-readable version of the file size, where the input represents a specific number of bytes.
    271     * <p>
    272     * If the size is over 1GB, the size is returned as the number of whole GB, the size is rounded down to the
    273     * nearest GB boundary.
    274     * </p>
    275     * <p>
    276     * Similarly for the 1MB and 1KB boundaries.
    277     * </p>
    278     *
    279     * @param size the number of bytes.
    280     * @return a human-readable display value (includes units - EB, PB, TB, GB, MB, KB or bytes).
    281     * @see <a href="https://issues.apache.org/jira/browse/IO-226">IO-226 - should the rounding be changed?</a>
    282     */
    283    // See https://issues.apache.org/jira/browse/IO-226 - should the rounding be changed?
    284    public static String byteCountToDisplaySize(final long size) {
    285        return byteCountToDisplaySize(BigInteger.valueOf(size));
    286    }
    287
    288    /**
    289     * Returns a human-readable version of the file size, where the input represents a specific number of bytes.
    290     * <p>
    291     * If the size is over 1GB, the size is returned as the number of whole GB, the size is rounded down to the
    292     * nearest GB boundary.
    293     * </p>
    294     * <p>
    295     * Similarly for the 1MB and 1KB boundaries.
    296     * </p>
    297     *
    298     * @param size the number of bytes.
    299     * @return a human-readable display value (includes units - EB, PB, TB, GB, MB, KB or bytes).
    300     * @see <a href="https://issues.apache.org/jira/browse/IO-226">IO-226 - should the rounding be changed?</a>
    301     * @since 2.12.0
    302     */
    303    // See https://issues.apache.org/jira/browse/IO-226 - should the rounding be changed?
    304    public static String byteCountToDisplaySize(final Number size) {
    305        return byteCountToDisplaySize(size.longValue());
    306    }
    307
    308    /**
    309     * Requires that the given {@link File} is non-null and exists (if strict is true).
    310     *
    311     * @param file The {@link File} to check.
    312     * @param strict whether to check that the {@code file} exists.
    313     * @throws FileNotFoundException if the file does not exist.
    314     * @throws NullPointerException  if the given {@link File} is {@code null}.
    315     */
    316    private static void checkExists(final File file, final boolean strict) throws FileNotFoundException {
    317        Objects.requireNonNull(file, PROTOCOL_FILE);
    318        if (strict && !file.exists() && !isSymlink(file)) {
    319            throw new FileNotFoundException(file.toString());
    320        }
    321    }
    322
    323    /**
    324     * Requires that the given {@link File} exists, and throws a {@link FileNotFoundException} if it doesn't.
    325     *
    326     * @param file The {@link File} to check.
    327     * @param name The NullPointerException message.
    328     * @throws FileNotFoundException if the file does not exist.
    329     * @throws NullPointerException  if the given {@link File} is {@code null}.
    330     */
    331    private static void checkFileExists(final File file, final String name) throws FileNotFoundException {
    332        Objects.requireNonNull(file, name);
    333        if (!file.isFile()) {
    334            if (file.exists()) {
    335                throw new IllegalArgumentException("Parameter '" + name + "' is not a file: " + file);
    336            }
    337            if (!Files.isSymbolicLink(file.toPath())) {
    338                throw new FileNotFoundException("Source '" + file + "' does not exist");
    339            }
    340        }
    341    }
    342
    343    private static File checkIsFile(final File file, final String name) {
    344        if (file.isFile()) {
    345            return file;
    346        }
    347        throw new IllegalArgumentException(String.format("Parameter '%s' is not a file: %s", name, file));
    348    }
    349
    350    /**
    351     * Computes the checksum of a file using the specified checksum object. Multiple files may be checked using one
    352     * {@link Checksum} instance if desired simply by reusing the same checksum object. For example:
    353     *
    354     * <pre>
    355     * long checksum = FileUtils.checksum(file, new CRC32()).getValue();
    356     * </pre>
    357     *
    358     * @param file the file to checksum, must not be {@code null}
    359     * @param checksum the checksum object to be used, must not be {@code null}
    360     * @return the checksum specified, updated with the content of the file.
    361     * @throws NullPointerException if the given {@link File} is {@code null}.
    362     * @throws NullPointerException if the given {@link Checksum} is {@code null}.
    363     * @throws IllegalArgumentException if the given {@link File} is not a file.
    364     * @throws FileNotFoundException if the file does not exist.
    365     * @throws IOException if an IO error occurs reading the file.
    366     * @since 1.3
    367     */
    368    public static Checksum checksum(final File file, final Checksum checksum) throws IOException {
    369        checkFileExists(file, PROTOCOL_FILE);
    370        Objects.requireNonNull(checksum, "checksum");
    371        try (InputStream inputStream = new CheckedInputStream(Files.newInputStream(file.toPath()), checksum)) {
    372            IOUtils.consume(inputStream);
    373        }
    374        return checksum;
    375    }
    376
    377    /**
    378     * Computes the checksum of a file using the CRC32 checksum routine.
    379     * The value of the checksum is returned.
    380     *
    381     * @param file the file to checksum, must not be {@code null}
    382     * @return the checksum value.
    383     * @throws NullPointerException if the {@code file} is {@code null}.
    384     * @throws IllegalArgumentException if the {@code file} does not exist or is not a file.
    385     * @throws IOException              if an IO error occurs reading the file.
    386     * @since 1.3
    387     */
    388    public static long checksumCRC32(final File file) throws IOException {
    389        return checksum(file, new CRC32()).getValue();
    390    }
    391
    392    /**
    393     * Cleans a directory without deleting it.
    394     *
    395     * @param directory directory to clean.
    396     * @throws NullPointerException if the given {@link File} is {@code null}.
    397     * @throws IllegalArgumentException if the {@code directory} does not exist or is not a directory.
    398     * @throws IOException if an I/O error occurs.
    399     * @see #forceDelete(File)
    400     */
    401    public static void cleanDirectory(final File directory) throws IOException {
    402        IOConsumer.forAll(f -> forceDelete(f, false), listFiles(directory, null));
    403    }
    404
    405    /**
    406     * Cleans a directory without deleting it.
    407     *
    408     * @param directory directory to clean, must not be {@code null}
    409     * @throws NullPointerException if the given {@link File} is {@code null}.
    410     * @throws IllegalArgumentException if the {@code directory} does not exist or is not a directory.
    411     * @throws IOException if an I/O error occurs.
    412     * @see #forceDeleteOnExit(File)
    413     */
    414    private static void cleanDirectoryOnExit(final File directory) throws IOException {
    415        IOConsumer.forAll(FileUtils::forceDeleteOnExit, listFiles(directory, null));
    416    }
    417
    418    /**
    419     * Tests whether the contents of two files are equal.
    420     * <p>
    421     * This method checks to see if the two files are different lengths or if they point to the same file, before
    422     * resorting to byte-by-byte comparison of the contents.
    423     * </p>
    424     *
    425     * @param file1 the first file.
    426     * @param file2 the second file.
    427     * @return true if the content of the files are equal or they both don't exist, false otherwise.
    428     * @throws IllegalArgumentException when an input is not a file.
    429     * @throws IOException If an I/O error occurs.
    430     * @see PathUtils#fileContentEquals(Path,Path)
    431     */
    432    public static boolean contentEquals(final File file1, final File file2) throws IOException {
    433        if (file1 == null && file2 == null) {
    434            return true;
    435        }
    436        if (file1 == null || file2 == null) {
    437            return false;
    438        }
    439        final boolean file1Exists = file1.exists();
    440        if (file1Exists != file2.exists()) {
    441            return false;
    442        }
    443        if (!file1Exists) {
    444            // two not existing files are equal
    445            return true;
    446        }
    447        checkIsFile(file1, "file1");
    448        checkIsFile(file2, "file2");
    449        if (file1.length() != file2.length()) {
    450            // lengths differ, cannot be equal
    451            return false;
    452        }
    453        if (file1.getCanonicalFile().equals(file2.getCanonicalFile())) {
    454            // same file
    455            return true;
    456        }
    457        return PathUtils.fileContentEquals(file1.toPath(), file2.toPath());
    458    }
    459
    460    /**
    461     * Compares the contents of two files to determine if they are equal or not.
    462     * <p>
    463     * This method checks to see if the two files point to the same file,
    464     * before resorting to line-by-line comparison of the contents.
    465     * </p>
    466     *
    467     * @param file1       the first file.
    468     * @param file2       the second file.
    469     * @param charsetName the name of the requested charset.
    470     *                    May be null, in which case the platform default is used.
    471     * @return true if the content of the files are equal or neither exists,
    472     * false otherwise.
    473     * @throws IllegalArgumentException when an input is not a file.
    474     * @throws IOException in case of an I/O error.
    475     * @throws UnsupportedCharsetException If the named charset is unavailable (unchecked exception).
    476     * @see IOUtils#contentEqualsIgnoreEOL(Reader, Reader)
    477     * @since 2.2
    478     */
    479    public static boolean contentEqualsIgnoreEOL(final File file1, final File file2, final String charsetName) throws IOException {
    480        if (file1 == null && file2 == null) {
    481            return true;
    482        }
    483        if (file1 == null || file2 == null) {
    484            return false;
    485        }
    486        final boolean file1Exists = file1.exists();
    487        if (file1Exists != file2.exists()) {
    488            return false;
    489        }
    490        if (!file1Exists) {
    491            // two not existing files are equal
    492            return true;
    493        }
    494        checkFileExists(file1, "file1");
    495        checkFileExists(file2, "file2");
    496        if (file1.getCanonicalFile().equals(file2.getCanonicalFile())) {
    497            // same file
    498            return true;
    499        }
    500        final Charset charset = Charsets.toCharset(charsetName);
    501        try (Reader input1 = new InputStreamReader(Files.newInputStream(file1.toPath()), charset);
    502                Reader input2 = new InputStreamReader(Files.newInputStream(file2.toPath()), charset)) {
    503            return IOUtils.contentEqualsIgnoreEOL(input1, input2);
    504        }
    505    }
    506
    507    /**
    508     * Converts a Collection containing {@link File} instances into array
    509     * representation. This is to account for the difference between
    510     * File.listFiles() and FileUtils.listFiles().
    511     *
    512     * @param files a Collection containing {@link File} instances.
    513     * @return an array of {@link File}
    514     */
    515    public static File[] convertFileCollectionToFileArray(final Collection<File> files) {
    516        return files.toArray(EMPTY_FILE_ARRAY);
    517    }
    518
    519    /**
    520     * Copies a whole directory to a new location, preserving the file dates.
    521     * <p>
    522     * This method copies the specified directory and all its child directories and files to the specified destination.
    523     * The destination is the new location and name of the directory. That is, copying /home/bar to /tmp/bang
    524     * copies the contents of /home/bar into /tmp/bang. It does not create /tmp/bang/bar.
    525     * </p>
    526     * <p>
    527     * The destination directory is created if it does not exist. If the destination directory does exist, then this
    528     * method merges the source with the destination, with the source taking precedence.
    529     * </p>
    530     * <p>
    531     * <strong>Note:</strong> This method tries to preserve the file's last
    532     * modified date/times using {@link BasicFileAttributeView#setTimes(FileTime, FileTime, FileTime)}. However it is
    533     * not guaranteed that the operation will succeed. If the modification operation fails, it falls back to
    534     * {@link File#setLastModified(long)}. If that fails, the method throws IOException.
    535     * </p>
    536     * <p>
    537     * Symbolic links in the source directory are copied to new symbolic links in the destination
    538     * directory that point to the original target. The target of the link is not copied unless
    539     * it is also under the source directory. Even if it is under the source directory, the new symbolic
    540     * link in the destination points to the original target in the source directory, not to the
    541     * newly created copy of the target.
    542     * </p>
    543     *
    544     * @param srcDir an existing directory to copy, must not be {@code null}.
    545     * @param destDir the new directory, must not be {@code null}.
    546     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
    547     * @throws IllegalArgumentException if {@code srcDir} exists but is not a directory,
    548     *     the source and the destination directory are the same.
    549     * @throws FileNotFoundException if the source does not exist.
    550     * @throws IOException if an error occurs, the destination is not writable, or setting the last-modified time didn't succeed.
    551     * @since 1.1
    552     */
    553    public static void copyDirectory(final File srcDir, final File destDir) throws IOException {
    554        copyDirectory(srcDir, destDir, true);
    555    }
    556
    557    /**
    558     * Copies a whole directory to a new location.
    559     * <p>
    560     * This method copies the contents of the specified source directory to within the specified destination directory.
    561     * </p>
    562     * <p>
    563     * The destination directory is created if it does not exist. If the destination directory does exist, then this
    564     * method merges the source with the destination, with the source taking precedence.
    565     * </p>
    566     * <p>
    567     * <strong>Note:</strong> Setting {@code preserveFileDate} to {@code true} tries to preserve the files' last
    568     * modified date/times using {@link File#setLastModified(long)}. However it is not guaranteed that those operations
    569     * will succeed. If the modification operation fails, the method throws IOException.
    570     * </p>
    571     *
    572     * @param srcDir an existing directory to copy, must not be {@code null}.
    573     * @param destDir the new directory, must not be {@code null}.
    574     * @param preserveFileDate true if the file date of the copy should be the same as the original.
    575     * @throws IllegalArgumentException if {@code srcDir} exists but is not a directory, or
    576     *     the source and the destination directory are the same.
    577     * @throws FileNotFoundException if the source does not exist.
    578     * @throws IOException if an error occurs, the destination is not writable, or setting the last-modified time didn't succeed.
    579     * @since 1.1
    580     */
    581    public static void copyDirectory(final File srcDir, final File destDir, final boolean preserveFileDate)
    582        throws IOException {
    583        copyDirectory(srcDir, destDir, null, preserveFileDate);
    584    }
    585
    586    /**
    587     * Copies a filtered directory to a new location preserving the file dates.
    588     * <p>
    589     * This method copies the contents of the specified source directory to within the specified destination directory.
    590     * </p>
    591     * <p>
    592     * The destination directory is created if it does not exist. If the destination directory does exist, then this
    593     * method merges the source with the destination, with the source taking precedence.
    594     * </p>
    595     * <p>
    596     * <strong>Note:</strong> This method tries to preserve the files' last modified date/times using
    597     * {@link File#setLastModified(long)}. However it is not guaranteed that those operations will succeed. If the
    598     * modification operation fails, the method throws IOException.
    599     * </p>
    600     * <strong>Example: Copy directories only</strong>
    601     *
    602     * <pre>
    603     * // only copy the directory structure
    604     * FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY);
    605     * </pre>
    606     *
    607     * <strong>Example: Copy directories and txt files</strong>
    608     *
    609     * <pre>
    610     * // Create a filter for ".txt" files
    611     * IOFileFilter txtSuffixFilter = FileFilterUtils.suffixFileFilter(".txt");
    612     * IOFileFilter txtFiles = FileFilterUtils.andFileFilter(FileFileFilter.INSTANCE, txtSuffixFilter);
    613     *
    614     * // Create a filter for either directories or ".txt" files
    615     * FileFilter filter = FileFilterUtils.orFileFilter(DirectoryFileFilter.DIRECTORY, txtFiles);
    616     *
    617     * // Copy using the filter
    618     * FileUtils.copyDirectory(srcDir, destDir, filter);
    619     * </pre>
    620     *
    621     * @param srcDir an existing directory to copy, must not be {@code null}.
    622     * @param destDir the new directory, must not be {@code null}.
    623     * @param filter the filter to apply, null means copy all directories and files should be the same as the original.
    624     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
    625     * @throws IllegalArgumentException if {@code srcDir} exists but is not a directory, or
    626     *     the source and the destination directory are the same.
    627     * @throws FileNotFoundException if the source does not exist.
    628     * @throws IOException if an error occurs, the destination is not writable, or setting the last-modified time didn't succeed.
    629     * @since 1.4
    630     */
    631    public static void copyDirectory(final File srcDir, final File destDir, final FileFilter filter)
    632        throws IOException {
    633        copyDirectory(srcDir, destDir, filter, true);
    634    }
    635
    636    /**
    637     * Copies a filtered directory to a new location.
    638     * <p>
    639     * This method copies the contents of the specified source directory to within the specified destination directory.
    640     * </p>
    641     * <p>
    642     * The destination directory is created if it does not exist. If the destination directory does exist, then this
    643     * method merges the source with the destination, with the source taking precedence.
    644     * </p>
    645     * <p>
    646     * <strong>Note:</strong> Setting {@code preserveFileDate} to {@code true} tries to preserve the file's last
    647     * modified date/times using {@link BasicFileAttributeView#setTimes(FileTime, FileTime, FileTime)}. However, it is
    648     * not guaranteed that the operation will succeed. If the modification operation fails it falls back to
    649     * {@link File#setLastModified(long)}. If that fails, the method throws IOException.
    650     * </p>
    651     * <strong>Example: Copy directories only</strong>
    652     *
    653     * <pre>
    654     * // only copy the directory structure
    655     * FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY, false);
    656     * </pre>
    657     *
    658     * <strong>Example: Copy directories and txt files</strong>
    659     *
    660     * <pre>
    661     * // Create a filter for ".txt" files
    662     * IOFileFilter txtSuffixFilter = FileFilterUtils.suffixFileFilter(".txt");
    663     * IOFileFilter txtFiles = FileFilterUtils.andFileFilter(FileFileFilter.INSTANCE, txtSuffixFilter);
    664     *
    665     * // Create a filter for either directories or ".txt" files
    666     * FileFilter filter = FileFilterUtils.orFileFilter(DirectoryFileFilter.DIRECTORY, txtFiles);
    667     *
    668     * // Copy using the filter
    669     * FileUtils.copyDirectory(srcDir, destDir, filter, false);
    670     * </pre>
    671     *
    672     * @param srcDir an existing directory to copy, must not be {@code null}.
    673     * @param destDir the new directory, must not be {@code null}.
    674     * @param filter the filter to apply, null means copy all directories and files.
    675     * @param preserveFileDate true if the file date of the copy should be the same as the original.
    676     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
    677     * @throws IllegalArgumentException if {@code srcDir} exists but is not a directory,
    678     *     the source and the destination directory are the same, or the destination is not writable.
    679     * @throws FileNotFoundException if the source does not exist.
    680     * @throws IOException if an error occurs or setting the last-modified time didn't succeed.
    681     * @since 1.4
    682     */
    683    public static void copyDirectory(final File srcDir, final File destDir, final FileFilter filter, final boolean preserveFileDate) throws IOException {
    684        copyDirectory(srcDir, destDir, filter, preserveFileDate, StandardCopyOption.REPLACE_EXISTING, LinkOption.NOFOLLOW_LINKS);
    685    }
    686
    687    /**
    688     * Copies a filtered directory to a new location.
    689     * <p>
    690     * This method copies the contents of the specified source directory to within the specified destination directory.
    691     * </p>
    692     * <p>
    693     * The destination directory is created if it does not exist. If the destination directory does exist, then this
    694     * method merges the source with the destination, with the source taking precedence.
    695     * </p>
    696     * <p>
    697     * <strong>Note:</strong> Setting {@code preserveFileDate} to {@code true} tries to preserve the file's last
    698     * modified date/times using {@link BasicFileAttributeView#setTimes(FileTime, FileTime, FileTime)}. However, it is
    699     * not guaranteed that the operation will succeed. If the modification operation fails it falls back to
    700     * {@link File#setLastModified(long)}. If that fails, the method throws IOException.
    701     * </p>
    702     * <strong>Example: Copy directories only</strong>
    703     *
    704     * <pre>
    705     * // only copy the directory structure
    706     * FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY, false);
    707     * </pre>
    708     *
    709     * <strong>Example: Copy directories and txt files</strong>
    710     *
    711     * <pre>
    712     * // Create a filter for ".txt" files
    713     * IOFileFilter txtSuffixFilter = FileFilterUtils.suffixFileFilter(".txt");
    714     * IOFileFilter txtFiles = FileFilterUtils.andFileFilter(FileFileFilter.INSTANCE, txtSuffixFilter);
    715     *
    716     * // Create a filter for either directories or ".txt" files
    717     * FileFilter filter = FileFilterUtils.orFileFilter(DirectoryFileFilter.DIRECTORY, txtFiles);
    718     *
    719     * // Copy using the filter
    720     * FileUtils.copyDirectory(srcDir, destDir, filter, false);
    721     * </pre>
    722     *
    723     * @param srcDir an existing directory to copy, must not be {@code null}
    724     * @param destDir the new directory, must not be {@code null}
    725     * @param fileFilter the filter to apply, null means copy all directories and files.
    726     * @param preserveFileDate true if the file date of the copy should be the same as the original.
    727     * @param copyOptions options specifying how the copy should be done, for example {@link StandardCopyOption}.
    728     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
    729     * @throws IllegalArgumentException if {@code srcDir} exists but is not a directory, or
    730     *     the source and the destination directory are the same.
    731     * @throws FileNotFoundException if the source does not exist.
    732     * @throws IOException if an error occurs, the destination is not writable, or setting the last-modified time didn't succeed.
    733     * @since 2.8.0
    734     */
    735    public static void copyDirectory(final File srcDir, final File destDir, final FileFilter fileFilter, final boolean preserveFileDate,
    736            final CopyOption... copyOptions) throws IOException {
    737        Objects.requireNonNull(destDir, "destination");
    738        requireDirectoryExists(srcDir, "srcDir");
    739        requireCanonicalPathsNotEquals(srcDir, destDir);
    740        // Cater for destination being directory within the source directory (see IO-141)
    741        List<String> exclusionList = null;
    742        final String srcDirCanonicalPath = srcDir.getCanonicalPath();
    743        final String destDirCanonicalPath = destDir.getCanonicalPath();
    744        if (destDirCanonicalPath.startsWith(srcDirCanonicalPath)) {
    745            final File[] srcFiles = listFiles(srcDir, fileFilter);
    746            if (srcFiles.length > 0) {
    747                exclusionList = new ArrayList<>(srcFiles.length);
    748                for (final File srcFile : srcFiles) {
    749                    exclusionList.add(new File(destDir, srcFile.getName()).getCanonicalPath());
    750                }
    751            }
    752        }
    753        doCopyDirectory(srcDir, destDir, fileFilter, exclusionList, preserveFileDate, copyOptions);
    754    }
    755
    756    /**
    757     * Copies a directory to within another directory preserving the file dates.
    758     * <p>
    759     * This method copies the source directory and all its contents to a directory of the same name in the specified
    760     * destination directory.
    761     * </p>
    762     * <p>
    763     * The destination directory is created if it does not exist. If the destination directory does exist, then this
    764     * method merges the source with the destination, with the source taking precedence.
    765     * </p>
    766     * <p>
    767     * <strong>Note:</strong> Setting {@code preserveFileDate} to {@code true} tries to preserve the file's last
    768     * modified date/times using {@link BasicFileAttributeView#setTimes(FileTime, FileTime, FileTime)}. However, it is
    769     * not guaranteed that the operation will succeed. If the modification operation fails it falls back to
    770     * {@link File#setLastModified(long)} and if that fails, the method throws IOException.
    771     * </p>
    772     *
    773     * @param sourceDir an existing directory to copy, must not be {@code null}.
    774     * @param destinationDir the directory to place the copy in, must not be {@code null}.
    775     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
    776     * @throws IllegalArgumentException if the source or destination is invalid.
    777     * @throws FileNotFoundException if the source does not exist.
    778     * @throws IOException if an error occurs, the destination is not writable, or setting the last-modified time didn't succeed.
    779     * @since 1.2
    780     */
    781    public static void copyDirectoryToDirectory(final File sourceDir, final File destinationDir) throws IOException {
    782        Objects.requireNonNull(sourceDir, "sourceDir");
    783        requireDirectoryIfExists(destinationDir, "destinationDir");
    784        copyDirectory(sourceDir, new File(destinationDir, sourceDir.getName()), true);
    785    }
    786
    787    /**
    788     * Copies a file to a new location preserving the file date.
    789     * <p>
    790     * This method copies the contents of the specified source file to the specified destination file. The directory
    791     * holding the destination file is created if it does not exist. If the destination file exists, then this method
    792     * overwrites it. A symbolic link is resolved before copying so the new file is not a link.
    793     * </p>
    794     * <p>
    795     * <strong>Note:</strong> This method tries to preserve the file's last modified date/times using
    796     * {@link BasicFileAttributeView#setTimes(FileTime, FileTime, FileTime)}. However, it is not guaranteed that the
    797     * operation will succeed. If the modification operation fails, it falls back to
    798     * {@link File#setLastModified(long)}, and if that fails, the method throws IOException.
    799     * </p>
    800     *
    801     * @param srcFile an existing file to copy, must not be {@code null}.
    802     * @param destFile the new file, must not be {@code null}.
    803     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
    804     * @throws IOException if source or destination is invalid.
    805     * @throws IOException if an error occurs or setting the last-modified time didn't succeed.
    806     * @throws IOException if the output file length is not the same as the input file length after the copy completes.
    807     * @see #copyFileToDirectory(File, File)
    808     * @see #copyFile(File, File, boolean)
    809     */
    810    public static void copyFile(final File srcFile, final File destFile) throws IOException {
    811        copyFile(srcFile, destFile, StandardCopyOption.REPLACE_EXISTING);
    812    }
    813
    814    /**
    815     * Copies an existing file to a new file location.
    816     * <p>
    817     * This method copies the contents of the specified source file to the specified destination file. The directory
    818     * holding the destination file is created if it does not exist. If the destination file exists, then this method
    819     * overwrites it. A symbolic link is resolved before copying so the new file is not a link.
    820     * </p>
    821     * <p>
    822     * <strong>Note:</strong> Setting {@code preserveFileDate} to {@code true} tries to preserve the file's last
    823     * modified date/times using {@link BasicFileAttributeView#setTimes(FileTime, FileTime, FileTime)}. However, it is
    824     * not guaranteed that the operation will succeed. If the modification operation fails, it falls back to
    825     * {@link File#setLastModified(long)}, and if that fails, the method throws IOException.
    826     * </p>
    827     *
    828     * @param srcFile an existing file to copy, must not be {@code null}.
    829     * @param destFile the new file, must not be {@code null}.
    830     * @param preserveFileDate true if the file date of the copy should be the same as the original.
    831     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
    832     * @throws IOException if source or destination is invalid.
    833     * @throws IOException if an error occurs or setting the last-modified time didn't succeed.
    834     * @throws IOException if the output file length is not the same as the input file length after the copy completes.
    835     * @see #copyFile(File, File, boolean, CopyOption...)
    836     */
    837    public static void copyFile(final File srcFile, final File destFile, final boolean preserveFileDate) throws IOException {
    838        copyFile(srcFile, destFile, preserveFileDate, StandardCopyOption.REPLACE_EXISTING);
    839    }
    840
    841    /**
    842     * Copies the contents of a file to a new location.
    843     * <p>
    844     * This method copies the contents of the specified source file to the specified destination file. The directory
    845     * holding the destination file is created if it does not exist. If the destination file exists, you can overwrite
    846     * it with {@link StandardCopyOption#REPLACE_EXISTING}.
    847     * </p>
    848     *
    849     * <p>
    850     * By default, a symbolic link is resolved before copying so the new file is not a link.
    851     * To copy symbolic links as links, you can pass {@code LinkOption.NO_FOLLOW_LINKS} as the last argument.
    852     * </p>
    853     *
    854     * <p>
    855     * <strong>Note:</strong> Setting {@code preserveFileDate} to {@code true} tries to preserve the file's last
    856     * modified date/times using {@link BasicFileAttributeView#setTimes(FileTime, FileTime, FileTime)}. However, it is
    857     * not guaranteed that the operation will succeed. If the modification operation fails, it falls back to
    858     * {@link File#setLastModified(long)}, and if that fails, the method throws IOException.
    859     * </p>
    860     *
    861     * @param srcFile an existing file to copy, must not be {@code null}.
    862     * @param destFile the new file, must not be {@code null}.
    863     * @param preserveFileDate true if the file date of the copy should be the same as the original.
    864     * @param copyOptions options specifying how the copy should be done, for example {@link StandardCopyOption}.
    865     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
    866     * @throws FileNotFoundException if the source does not exist.
    867     * @throws IllegalArgumentException if {@code srcFile} or {@code destFile} is not a file.
    868     * @throws IOException if the output file length is not the same as the input file length after the copy completes.
    869     * @throws IOException if an I/O error occurs, setting the last-modified time didn't succeed,
    870     *     or the destination is not writable.
    871     * @see #copyFileToDirectory(File, File, boolean)
    872     * @since 2.8.0
    873     */
    874    public static void copyFile(final File srcFile, final File destFile, final boolean preserveFileDate, final CopyOption... copyOptions) throws IOException {
    875        Objects.requireNonNull(destFile, "destination");
    876        checkFileExists(srcFile, "srcFile");
    877        requireCanonicalPathsNotEquals(srcFile, destFile);
    878        createParentDirectories(destFile);
    879        if (destFile.exists()) {
    880            checkFileExists(destFile, "destFile");
    881        }
    882        final Path srcPath = srcFile.toPath();
    883        Files.copy(srcPath, destFile.toPath(), copyOptions);
    884        // On Windows, the last modified time is copied by default.
    885        if (preserveFileDate && !Files.isSymbolicLink(srcPath) && !setTimes(srcFile, destFile)) {
    886            throw new IOException("Cannot set the file time.");
    887        }
    888    }
    889
    890    /**
    891     * Copies a file to a new location.
    892     * <p>
    893     * This method copies the contents of the specified source file to the specified destination file. The directory
    894     * holding the destination file is created if it does not exist. If the destination file exists, you can overwrite
    895     * it if you use {@link StandardCopyOption#REPLACE_EXISTING}.
    896     * </p>
    897     *
    898     * @param srcFile an existing file to copy, must not be {@code null}.
    899     * @param destFile the new file, must not be {@code null}.
    900     * @param copyOptions options specifying how the copy should be done, for example {@link StandardCopyOption}.
    901     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
    902     * @throws FileNotFoundException if the source does not exist.
    903     * @throws IllegalArgumentException if source is not a file.
    904     * @throws IOException if an I/O error occurs.
    905     * @see StandardCopyOption
    906     * @since 2.9.0
    907     */
    908    public static void copyFile(final File srcFile, final File destFile, final CopyOption... copyOptions) throws IOException {
    909        copyFile(srcFile, destFile, true, copyOptions);
    910    }
    911
    912    /**
    913     * Copies bytes from a {@link File} to an {@link OutputStream}.
    914     * <p>
    915     * This method buffers the input internally, so there is no need to use a {@link BufferedInputStream}.
    916     * </p>
    917     *
    918     * @param input  the {@link File} to read.
    919     * @param output the {@link OutputStream} to write.
    920     * @return the number of bytes copied.
    921     * @throws NullPointerException if the File is {@code null}.
    922     * @throws NullPointerException if the OutputStream is {@code null}.
    923     * @throws IOException          if an I/O error occurs.
    924     * @since 2.1
    925     */
    926    public static long copyFile(final File input, final OutputStream output) throws IOException {
    927        try (InputStream fis = Files.newInputStream(input.toPath())) {
    928            return IOUtils.copyLarge(fis, output);
    929        }
    930    }
    931
    932    /**
    933     * Copies a file to a directory preserving the file date.
    934     * <p>
    935     * This method copies the contents of the specified source file to a file of the same name in the specified
    936     * destination directory. The destination directory is created if it does not exist. If the destination file exists,
    937     * then this method will overwrite it.
    938     * </p>
    939     * <p>
    940     * <strong>Note:</strong> This method tries to preserve the file's last modified date/times using
    941     * {@link BasicFileAttributeView#setTimes(FileTime, FileTime, FileTime)}. However, it is not guaranteed that the
    942     * operation will succeed. If the modification operation fails it falls back to
    943     * {@link File#setLastModified(long)} and if that fails, the method throws IOException.
    944     * </p>
    945     *
    946     * @param srcFile an existing file to copy, must not be {@code null}.
    947     * @param destDir the directory to place the copy in, must not be {@code null}.
    948     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
    949     * @throws IllegalArgumentException if source or destination is invalid.
    950     * @throws IOException if an error occurs or setting the last-modified time didn't succeed.
    951     * @see #copyFile(File, File, boolean)
    952     */
    953    public static void copyFileToDirectory(final File srcFile, final File destDir) throws IOException {
    954        copyFileToDirectory(srcFile, destDir, true);
    955    }
    956
    957    /**
    958     * Copies a file to a directory optionally preserving the file date.
    959     * <p>
    960     * This method copies the contents of the specified source file to a file of the same name in the specified
    961     * destination directory. The destination directory is created if it does not exist. If the destination file exists,
    962     * then this method will overwrite it.
    963     * </p>
    964     * <p>
    965     * <strong>Note:</strong> Setting {@code preserveFileDate} to {@code true} tries to preserve the file's last
    966     * modified date/times using {@link BasicFileAttributeView#setTimes(FileTime, FileTime, FileTime)}. However, it is
    967     * not guaranteed that the operation will succeed. If the modification operation fails it falls back to
    968     * {@link File#setLastModified(long)} and if that fails, the method throws IOException.
    969     * </p>
    970     *
    971     * @param sourceFile an existing file to copy, must not be {@code null}.
    972     * @param destinationDir the directory to place the copy in, must not be {@code null}.
    973     * @param preserveFileDate true if the file date of the copy should be the same as the original.
    974     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
    975     * @throws IOException if an error occurs or setting the last-modified time didn't succeed.
    976     * @throws IOException if the output file length is not the same as the input file length after the copy completes.
    977     * @see #copyFile(File, File, CopyOption...)
    978     * @since 1.3
    979     */
    980    public static void copyFileToDirectory(final File sourceFile, final File destinationDir, final boolean preserveFileDate) throws IOException {
    981        Objects.requireNonNull(sourceFile, "sourceFile");
    982        requireDirectoryIfExists(destinationDir, "destinationDir");
    983        copyFile(sourceFile, new File(destinationDir, sourceFile.getName()), preserveFileDate);
    984    }
    985
    986    /**
    987     * Copies bytes from an {@link InputStream} {@code source} to a file
    988     * {@code destination}. The directories up to {@code destination}
    989     * will be created if they don't already exist. {@code destination}
    990     * will be overwritten if it already exists.
    991     * <p>
    992     * <em>The {@code source} stream is closed.</em>
    993     * </p>
    994     * <p>
    995     * See {@link #copyToFile(InputStream, File)} for a method that does not close the input stream.
    996     * </p>
    997     *
    998     * @param source      the {@link InputStream} to copy bytes from, must not be {@code null}, will be closed.
    999     * @param destination the non-directory {@link File} to write bytes to
    1000     *                    (possibly overwriting), must not be {@code null}
    1001     * @throws IOException if {@code destination} is a directory.
    1002     * @throws IOException if {@code destination} cannot be written.
    1003     * @throws IOException if {@code destination} needs creating but can't be.
    1004     * @throws IOException if an IO error occurs during copying.
    1005     * @since 2.0
    1006     */
    1007    public static void copyInputStreamToFile(final InputStream source, final File destination) throws IOException {
    1008        try (InputStream inputStream = source) {
    1009            copyToFile(inputStream, destination);
    1010        }
    1011    }
    1012
    1013    /**
    1014     * Copies a file or directory to within another directory preserving the file dates.
    1015     * <p>
    1016     * This method copies the source file or directory, along with all its contents, to a directory of the same name in the
    1017     * specified destination directory.
    1018     * </p>
    1019     * <p>
    1020     * The destination directory is created if it does not exist. If the destination directory does exist, then this method
    1021     * merges the source with the destination, with the source taking precedence.
    1022     * </p>
    1023     * <p>
    1024     * <strong>Note:</strong> Setting {@code preserveFileDate} to {@code true} tries to preserve the file's last
    1025     * modified date/times using {@link BasicFileAttributeView#setTimes(FileTime, FileTime, FileTime)}. However, it is
    1026     * not guaranteed that the operation will succeed. If the modification operation fails it falls back to
    1027     * {@link File#setLastModified(long)} and if that fails, the method throws IOException.
    1028     * </p>
    1029     *
    1030     * @param sourceFile an existing file or directory to copy, must not be {@code null}.
    1031     * @param destinationDir the directory to place the copy in, must not be {@code null}.
    1032     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
    1033     * @throws IllegalArgumentException if the source or destination is invalid.
    1034     * @throws FileNotFoundException if the source does not exist.
    1035     * @throws IOException if an error occurs or setting the last-modified time didn't succeed.
    1036     * @see #copyDirectoryToDirectory(File, File)
    1037     * @see #copyFileToDirectory(File, File)
    1038     * @since 2.6
    1039     */
    1040    public static void copyToDirectory(final File sourceFile, final File destinationDir) throws IOException {
    1041        Objects.requireNonNull(sourceFile, "sourceFile");
    1042        if (sourceFile.isFile()) {
    1043            copyFileToDirectory(sourceFile, destinationDir);
    1044        } else if (sourceFile.isDirectory()) {
    1045            copyDirectoryToDirectory(sourceFile, destinationDir);
    1046        } else {
    1047            throw new FileNotFoundException("The source " + sourceFile + " does not exist");
    1048        }
    1049    }
    1050
    1051    /**
    1052     * Copies a files to a directory preserving each file's date.
    1053     * <p>
    1054     * This method copies the contents of the specified source files
    1055     * to a file of the same name in the specified destination directory.
    1056     * The destination directory is created if it does not exist.
    1057     * If the destination file exists, then this method will overwrite it.
    1058     * </p>
    1059     * <p>
    1060     * <strong>Note:</strong> This method tries to preserve the file's last
    1061     * modified date/times using {@link BasicFileAttributeView#setTimes(FileTime, FileTime, FileTime)}. However, it is
    1062     * not guaranteed that the operation will succeed. If the modification operation fails it falls back to
    1063     * {@link File#setLastModified(long)} and if that fails, the method throws IOException.
    1064     * </p>
    1065     *
    1066     * @param sourceIterable  existing files to copy, must not be {@code null}.
    1067     * @param destinationDir  the directory to place the copies in, must not be {@code null}.
    1068     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
    1069     * @throws IOException if source or destination is invalid.
    1070     * @throws IOException if an error occurs or setting the last-modified time didn't succeed.
    1071     * @see #copyFileToDirectory(File, File)
    1072     * @since 2.6
    1073     */
    1074    public static void copyToDirectory(final Iterable<File> sourceIterable, final File destinationDir) throws IOException {
    1075        Objects.requireNonNull(sourceIterable, "sourceIterable");
    1076        for (final File src : sourceIterable) {
    1077            copyFileToDirectory(src, destinationDir);
    1078        }
    1079    }
    1080
    1081    /**
    1082     * Copies bytes from an {@link InputStream} source to a {@link File} destination. The directories
    1083     * up to {@code destination} will be created if they don't already exist. {@code destination} will be
    1084     * overwritten if it already exists. The {@code source} stream is left open, e.g. for use with
    1085     * {@link java.util.zip.ZipInputStream ZipInputStream}. See {@link #copyInputStreamToFile(InputStream, File)} for a
    1086     * method that closes the input stream.
    1087     *
    1088     * @param inputStream the {@link InputStream} to copy bytes from, must not be {@code null}
    1089     * @param file the non-directory {@link File} to write bytes to (possibly overwriting), must not be
    1090     *        {@code null}
    1091     * @throws NullPointerException if the InputStream is {@code null}.
    1092     * @throws NullPointerException if the File is {@code null}.
    1093     * @throws IllegalArgumentException if the file object is a directory.
    1094     * @throws IllegalArgumentException if the file is not writable.
    1095     * @throws IOException if the directories could not be created.
    1096     * @throws IOException if an IO error occurs during copying.
    1097     * @since 2.5
    1098     */
    1099    public static void copyToFile(final InputStream inputStream, final File file) throws IOException {
    1100        try (OutputStream out = newOutputStream(file, false)) {
    1101            IOUtils.copy(inputStream, out);
    1102        }
    1103    }
    1104
    1105    /**
    1106     * Copies bytes from the URL {@code source} to a file
    1107     * {@code destination}. The directories up to {@code destination}
    1108     * will be created if they don't already exist. {@code destination}
    1109     * will be overwritten if it already exists.
    1110     * <p>
    1111     * Warning: this method does not set a connection or read timeout and thus
    1112     * might block forever. Use {@link #copyURLToFile(URL, File, int, int)}
    1113     * with reasonable timeouts to prevent this.
    1114     * </p>
    1115     *
    1116     * @param source      the {@link URL} to copy bytes from, must not be {@code null}
    1117     * @param destination the non-directory {@link File} to write bytes to
    1118     *                    (possibly overwriting), must not be {@code null}
    1119     * @throws IOException if {@code source} URL cannot be opened.
    1120     * @throws IOException if {@code destination} is a directory.
    1121     * @throws IOException if {@code destination} cannot be written.
    1122     * @throws IOException if {@code destination} needs creating but can't be.
    1123     * @throws IOException if an IO error occurs during copying.
    1124     */
    1125    public static void copyURLToFile(final URL source, final File destination) throws IOException {
    1126        final Path path = destination.toPath();
    1127        PathUtils.createParentDirectories(path);
    1128        PathUtils.copy(source::openStream, path, StandardCopyOption.REPLACE_EXISTING);
    1129    }
    1130
    1131    /**
    1132     * Copies bytes from the URL {@code source} to a file {@code destination}. The directories up to
    1133     * {@code destination} will be created if they don't already exist. {@code destination} will be
    1134     * overwritten if it already exists.
    1135     *
    1136     * @param source the {@link URL} to copy bytes from, must not be {@code null}
    1137     * @param destination the non-directory {@link File} to write bytes to (possibly overwriting), must not be
    1138     *        {@code null}
    1139     * @param connectionTimeoutMillis the number of milliseconds until this method will time out if no connection could
    1140     *        be established to the {@code source}
    1141     * @param readTimeoutMillis the number of milliseconds until this method will time out if no data could be read from
    1142     *        the {@code source}
    1143     * @throws IOException if {@code source} URL cannot be opened.
    1144     * @throws IOException if {@code destination} is a directory.
    1145     * @throws IOException if {@code destination} cannot be written.
    1146     * @throws IOException if {@code destination} needs creating but can't be.
    1147     * @throws IOException if an IO error occurs during copying.
    1148     * @since 2.0
    1149     */
    1150    public static void copyURLToFile(final URL source, final File destination, final int connectionTimeoutMillis, final int readTimeoutMillis)
    1151        throws IOException {
    1152        try (CloseableURLConnection urlConnection = CloseableURLConnection.open(source)) {
    1153            urlConnection.setConnectTimeout(connectionTimeoutMillis);
    1154            urlConnection.setReadTimeout(readTimeoutMillis);
    1155            try (InputStream stream = urlConnection.getInputStream()) {
    1156                copyInputStreamToFile(stream, destination);
    1157            }
    1158        }
    1159    }
    1160
    1161    /**
    1162     * Creates all parent directories for a File object, including any necessary but non-existent parent directories. If a parent directory already exists or
    1163     * is null, nothing happens.
    1164     *
    1165     * @param file the File that may need parents, may be null.
    1166     * @return The parent directory, or {@code null} if the given File does have a parent.
    1167     * @throws IOException       if the directory was not created along with all its parent directories.
    1168     * @throws SecurityException See {@link File#mkdirs()}.
    1169     * @since 2.9.0
    1170     */
    1171    public static File createParentDirectories(final File file) throws IOException {
    1172        return mkdirs(getParentFile(file));
    1173    }
    1174
    1175    /**
    1176     * Gets the current directory.
    1177     *
    1178     * @return the current directory.
    1179     * @since 2.12.0
    1180     */
    1181    public static File current() {
    1182        return PathUtils.current().toFile();
    1183    }
    1184
    1185    /**
    1186     * Decodes the specified URL as per RFC 3986, transforming
    1187     * percent-encoded octets to characters by decoding with the UTF-8 character
    1188     * set. This function is primarily intended for usage with
    1189     * {@link java.net.URL} which unfortunately does not enforce proper URLs. As
    1190     * such, this method will leniently accept invalid characters or malformed
    1191     * percent-encoded octets and simply pass them literally through to the
    1192     * result string. Except for rare edge cases, this will make unencoded URLs
    1193     * pass through unaltered.
    1194     *
    1195     * @param url The URL to decode, may be {@code null}.
    1196     * @return The decoded URL or {@code null} if the input was
    1197     * {@code null}.
    1198     */
    1199    static String decodeUrl(final String url) {
    1200        String decoded = url;
    1201        if (url != null && url.indexOf('%') >= 0) {
    1202            final int n = url.length();
    1203            final StringBuilder builder = new StringBuilder();
    1204            final ByteBuffer byteBuffer = ByteBuffer.allocate(n);
    1205            for (int i = 0; i < n; ) {
    1206                if (url.charAt(i) == '%') {
    1207                    try {
    1208                        do {
    1209                            final byte octet = (byte) Integer.parseInt(url.substring(i + 1, i + 3), 16);
    1210                            byteBuffer.put(octet);
    1211                            i += 3;
    1212                        } while (i < n && url.charAt(i) == '%');
    1213                        continue;
    1214                    } catch (final IndexOutOfBoundsException | NumberFormatException ignored) {
    1215                        // malformed percent-encoded octet, fall through and
    1216                        // append characters literally
    1217                    } finally {
    1218                        if (byteBuffer.position() > 0) {
    1219                            byteBuffer.flip();
    1220                            builder.append(StandardCharsets.UTF_8.decode(byteBuffer).toString());
    1221                            byteBuffer.clear();
    1222                        }
    1223                    }
    1224                }
    1225                builder.append(url.charAt(i++));
    1226            }
    1227            decoded = builder.toString();
    1228        }
    1229        return decoded;
    1230    }
    1231
    1232    /**
    1233     * Deletes the given File but throws an IOException if it cannot, unlike {@link File#delete()} which returns a
    1234     * boolean.
    1235     *
    1236     * @param file The file to delete.
    1237     * @return the given file.
    1238     * @throws NullPointerException     if the parameter is {@code null}.
    1239     * @throws IOException              if the file cannot be deleted.
    1240     * @see File#delete()
    1241     * @since 2.9.0
    1242     */
    1243    public static File delete(final File file) throws IOException {
    1244        Objects.requireNonNull(file, PROTOCOL_FILE);
    1245        Files.delete(file.toPath());
    1246        return file;
    1247    }
    1248
    1249    /**
    1250     * Deletes a directory recursively.
    1251     *
    1252     * @param directory directory to delete.
    1253     * @throws IOException              in case deletion is unsuccessful.
    1254     * @throws NullPointerException     if the parameter is {@code null}.
    1255     * @throws IllegalArgumentException if {@code directory} is not a directory.
    1256     */
    1257    public static void deleteDirectory(final File directory) throws IOException {
    1258        Objects.requireNonNull(directory, "directory");
    1259        if (!directory.exists()) {
    1260            return;
    1261        }
    1262        if (!isSymlink(directory)) {
    1263            cleanDirectory(directory);
    1264        }
    1265        delete(directory);
    1266    }
    1267
    1268    /**
    1269     * Requests a directory for deletion recursively when the virtual machine terminates.
    1270     *
    1271     * @param directory directory to delete, must not be {@code null}
    1272     * @throws NullPointerException if the directory is {@code null}.
    1273     * @throws IOException          in case deletion is unsuccessful.
    1274     */
    1275    private static void deleteDirectoryOnExit(final File directory) throws IOException {
    1276        if (!directory.exists()) {
    1277            return;
    1278        }
    1279        directory.deleteOnExit();
    1280        if (!isSymlink(directory)) {
    1281            cleanDirectoryOnExit(directory);
    1282        }
    1283    }
    1284
    1285    /**
    1286     * Deletes a file, never throwing an exception. If file is a directory, delete it and all subdirectories.
    1287     * <p>
    1288     * The difference between File.delete() and this method are:
    1289     * </p>
    1290     * <ul>
    1291     * <li>A directory to be deleted does not have to be empty.</li>
    1292     * <li>No exceptions are thrown when a file or directory cannot be deleted.</li>
    1293     * </ul>
    1294     *
    1295     * @param file file or directory to delete, can be {@code null}.
    1296     * @return {@code true} if the file or directory was deleted, otherwise
    1297     * {@code false}.
    1298     * @since 1.4
    1299     */
    1300    public static boolean deleteQuietly(final File file) {
    1301        if (file == null) {
    1302            return false;
    1303        }
    1304        try {
    1305            if (file.isDirectory()) {
    1306                cleanDirectory(file);
    1307            }
    1308        } catch (final Exception ignored) {
    1309            // ignore
    1310        }
    1311        try {
    1312            return file.delete();
    1313        } catch (final Exception ignored) {
    1314            return false;
    1315        }
    1316    }
    1317
    1318    /**
    1319     * Tests whether the {@code parent} directory contains the {@code child} element (a file or directory).
    1320     * <p>
    1321     * Files are normalized before comparison.
    1322     * </p>
    1323     *
    1324     * Edge cases:
    1325     * <ul>
    1326     * <li>A {@code directory} must not be null: if null, throw NullPointerException</li>
    1327     * <li>A {@code directory} must be a directory: if not a directory, throw IllegalArgumentException</li>
    1328     * <li>A directory does not contain itself: return false</li>
    1329     * <li>A null child file is not contained in any parent: return false</li>
    1330     * </ul>
    1331     *
    1332     * @param directory the file to consider as the parent.
    1333     * @param child     the file to consider as the child.
    1334     * @return true is the candidate leaf is under by the specified composite. False otherwise.
    1335     * @throws IOException              if an IO error occurs while checking the files.
    1336     * @throws NullPointerException if the parent is {@code null}.
    1337     * @throws IllegalArgumentException if the parent is not a directory.
    1338     * @see FilenameUtils#directoryContains(String, String)
    1339     * @since 2.2
    1340     */
    1341    public static boolean directoryContains(final File directory, final File child) throws IOException {
    1342        requireDirectoryExists(directory, "directory");
    1343
    1344        if (child == null || !child.exists()) {
    1345            return false;
    1346        }
    1347
    1348        // Canonicalize paths (normalizes relative paths)
    1349        return FilenameUtils.directoryContains(directory.getCanonicalPath(), child.getCanonicalPath());
    1350    }
    1351
    1352    /**
    1353     * Internal copy directory method. Creates all destination parent directories,
    1354     * including any necessary but non-existent parent directories.
    1355     *
    1356     * @param srcDir the validated source directory, must not be {@code null}.
    1357     * @param destDir the validated destination directory, must not be {@code null}.
    1358     * @param fileFilter the filter to apply, null means copy all directories and files.
    1359     * @param exclusionList List of files and directories to exclude from the copy, may be null.
    1360     * @param preserveDirDate preserve the directories last modified dates.
    1361     * @param copyOptions options specifying how the copy should be done, see {@link StandardCopyOption}.
    1362     * @throws IOException if the directory was not created along with all its parent directories.
    1363     * @throws IllegalArgumentException if {@code destDir} is not writable.
    1364     * @throws SecurityException See {@link File#mkdirs()}.
    1365     */
    1366    private static void doCopyDirectory(final File srcDir, final File destDir, final FileFilter fileFilter, final List<String> exclusionList,
    1367        final boolean preserveDirDate, final CopyOption... copyOptions) throws IOException {
    1368        // recurse dirs, copy files.
    1369        final File[] srcFiles = listFiles(srcDir, fileFilter);
    1370        requireDirectoryIfExists(destDir, "destDir");
    1371        mkdirs(destDir);
    1372        for (final File srcFile : srcFiles) {
    1373            final File dstFile = new File(destDir, srcFile.getName());
    1374            if (exclusionList == null || !exclusionList.contains(srcFile.getCanonicalPath())) {
    1375                if (srcFile.isDirectory()) {
    1376                    doCopyDirectory(srcFile, dstFile, fileFilter, exclusionList, preserveDirDate, copyOptions);
    1377                } else {
    1378                    copyFile(srcFile, dstFile, preserveDirDate, copyOptions);
    1379                }
    1380            }
    1381        }
    1382        // Do this last, as the above has probably affected directory metadata
    1383        if (preserveDirDate) {
    1384            setTimes(srcDir, destDir);
    1385        }
    1386    }
    1387
    1388    /**
    1389     * Deletes a file or directory. For a directory, delete it and all subdirectories.
    1390     * <p>
    1391     * The difference between File.delete() and this method are:
    1392     * </p>
    1393     * <ul>
    1394     * <li>The directory does not have to be empty.</li>
    1395     * <li>You get an exception when a file or directory cannot be deleted.</li>
    1396     * </ul>
    1397     *
    1398     * @param file file or directory to delete, must not be {@code null}.
    1399     * @throws NullPointerException  if the file is {@code null}.
    1400     * @throws FileNotFoundException if the file was not found.
    1401     * @throws IOException           in case deletion is unsuccessful.
    1402     */
    1403    public static void forceDelete(final File file) throws IOException {
    1404        forceDelete(file, true);
    1405    }
    1406
    1407    /**
    1408     * Deletes a file or directory. For a directory, delete it and all subdirectories.
    1409     * <p>
    1410     * The difference between File.delete() and this method are:
    1411     * </p>
    1412     * <ul>
    1413     * <li>The directory does not have to be empty.</li>
    1414     * <li>You get an exception when a file or directory cannot be deleted.</li>
    1415     * </ul>
    1416     *
    1417     * @param file file or directory to delete, must not be {@code null}.
    1418     * @param strict whether to throw a FileNotFoundException.
    1419     * @throws NullPointerException  if the file is {@code null}.
    1420     * @throws FileNotFoundException if the file was not found.
    1421     * @throws IOException           in case deletion is unsuccessful.
    1422     */
    1423    private static void forceDelete(final File file, final boolean strict) throws IOException {
    1424        checkExists(file, strict); // fail-fast
    1425        final Counters.PathCounters deleteCounters;
    1426        try {
    1427            deleteCounters = PathUtils.delete(file.toPath(), PathUtils.EMPTY_LINK_OPTION_ARRAY, StandardDeleteOption.OVERRIDE_READ_ONLY);
    1428        } catch (final NoSuchFileException e) {
    1429            // Map NIO to IO exception
    1430            final FileNotFoundException nioEx = new FileNotFoundException("Cannot delete file: " + file);
    1431            nioEx.initCause(e);
    1432            throw nioEx;
    1433        } catch (final IOException e) {
    1434            throw new IOException("Cannot delete file: " + file, e);
    1435        }
    1436        if (deleteCounters.getFileCounter().get() < 1 && deleteCounters.getDirectoryCounter().get() < 1) {
    1437            // didn't find a file to delete.
    1438            throw new FileNotFoundException("File does not exist: " + file);
    1439        }
    1440    }
    1441
    1442    /**
    1443     * Requests a file to be deleted when the virtual machine terminates.
    1444     * If file is directory delete it and all subdirectories.
    1445     *
    1446     * @param file file or directory to delete, must not be {@code null}.
    1447     * @throws NullPointerException if the file is {@code null}.
    1448     * @throws IOException          in case deletion is unsuccessful.
    1449     */
    1450    public static void forceDeleteOnExit(final File file) throws IOException {
    1451        Objects.requireNonNull(file, PROTOCOL_FILE);
    1452        if (file.isDirectory()) {
    1453            deleteDirectoryOnExit(file);
    1454        } else {
    1455            file.deleteOnExit();
    1456        }
    1457    }
    1458
    1459    /**
    1460     * Creates all directories for a File object, including any necessary but non-existent parent directories. If the {@code directory} already exists or is
    1461     * null, nothing happens.
    1462     * <p>
    1463     * Calls {@link File#mkdirs()} and throws an {@link IOException} on failure.
    1464     * </p>
    1465     *
    1466     * @param directory the receiver for {@code mkdirs()}. If the {@code directory} already exists or is null, nothing happens.
    1467     * @throws IOException       if the directory was not created along with all its parent directories.
    1468     * @throws IOException       if the given file object is not a directory.
    1469     * @throws SecurityException See {@link File#mkdirs()}.
    1470     * @see File#mkdirs()
    1471     */
    1472    public static void forceMkdir(final File directory) throws IOException {
    1473        mkdirs(directory);
    1474    }
    1475
    1476    /**
    1477     * Creates all directories for a File object, including any necessary but non-existent parent directories. If the parent directory already exists or is
    1478     * null, nothing happens.
    1479     * <p>
    1480     * Calls {@link File#mkdirs()} for the parent of {@code file}.
    1481     * </p>
    1482     *
    1483     * @param file file with parents to create, must not be {@code null}.
    1484     * @throws NullPointerException if the file is {@code null}.
    1485     * @throws IOException          if the directory was not created along with all its parent directories.
    1486     * @throws SecurityException    See {@link File#mkdirs()}.
    1487     * @see File#mkdirs()
    1488     * @since 2.5
    1489     */
    1490    public static void forceMkdirParent(final File file) throws IOException {
    1491        forceMkdir(getParentFile(Objects.requireNonNull(file, PROTOCOL_FILE)));
    1492    }
    1493
    1494    /**
    1495     * Constructs a file from the set of name elements.
    1496     *
    1497     * @param directory the parent directory.
    1498     * @param names the name elements.
    1499     * @return the new file.
    1500     * @since 2.1
    1501     */
    1502    public static File getFile(final File directory, final String... names) {
    1503        Objects.requireNonNull(directory, "directory");
    1504        Objects.requireNonNull(names, "names");
    1505        File file = directory;
    1506        for (final String name : names) {
    1507            file = new File(file, name);
    1508        }
    1509        return file;
    1510    }
    1511
    1512    /**
    1513     * Constructs a file from the set of name elements.
    1514     *
    1515     * @param names the name elements.
    1516     * @return the file.
    1517     * @since 2.1
    1518     */
    1519    public static File getFile(final String... names) {
    1520        Objects.requireNonNull(names, "names");
    1521        File file = null;
    1522        for (final String name : names) {
    1523            if (file == null) {
    1524                file = new File(name);
    1525            } else {
    1526                file = new File(file, name);
    1527            }
    1528        }
    1529        return file;
    1530    }
    1531
    1532    /**
    1533     * Gets the parent of the given file. The given file may be null. Note that a file's parent may be null as well.
    1534     *
    1535     * @param file The file to query, may be null.
    1536     * @return The parent file or {@code null}. Note that a file's parent may be null as well.
    1537     */
    1538    private static File getParentFile(final File file) {
    1539        return file == null ? null : file.getParentFile();
    1540    }
    1541
    1542    /**
    1543     * Gets a {@link File} representing the system temporary directory based on the Java system property {@code java.io.tmpdir}.
    1544     *
    1545     * @return the system temporary directory as a File.
    1546     * @since 2.0
    1547     */
    1548    public static File getTempDirectory() {
    1549        return new File(getTempDirectoryPath());
    1550    }
    1551
    1552    /**
    1553     * Gets the path to the system temporary directory.
    1554     * <p>
    1555     * WARNING: This method reads the Java system property {@code java.io.tmpdir}, which may or may not have a trailing file separator. This can affect code
    1556     * that uses String processing to manipulate pathnames rather than the standard library methods in classes such as {@link File}.
    1557     * </p>
    1558     *
    1559     * @return the path to the system temporary directory as a String.
    1560     * @since 2.0
    1561     */
    1562    public static String getTempDirectoryPath() {
    1563        return System.getProperty("java.io.tmpdir");
    1564    }
    1565
    1566    /**
    1567     * Gets a {@link File} representing the user's home directory based on the Java system property {@code user.home}.
    1568     *
    1569     * @return the user's home directory.
    1570     * @since 2.0
    1571     */
    1572    public static File getUserDirectory() {
    1573        return new File(getUserDirectoryPath());
    1574    }
    1575
    1576    /**
    1577     * Gets the path to the user's home directory based on the Java system property {@code user.home}.
    1578     *
    1579     * @return the path to the user's home directory.
    1580     * @since 2.0
    1581     */
    1582    public static String getUserDirectoryPath() {
    1583        return System.getProperty("user.home");
    1584    }
    1585
    1586    /**
    1587     * Tests whether the specified {@link File} is a directory or not. Implemented as a
    1588     * null-safe delegate to {@link Files#isDirectory(Path path, LinkOption... options)}.
    1589     *
    1590     * @param   file the path to the file.
    1591     * @param   options options indicating how symbolic links are handled.
    1592     * @return  {@code true} if the file is a directory; {@code false} if
    1593     *          the path is null, the file does not exist, is not a directory, or it cannot
    1594     *          be determined if the file is a directory or not.
    1595     * @throws SecurityException     In the case of the default provider, and a security manager is installed, the
    1596     *                               {@link SecurityManager#checkRead(String) checkRead} method is invoked to check read
    1597     *                               access to the directory.
    1598     * @since 2.9.0
    1599     */
    1600    public static boolean isDirectory(final File file, final LinkOption... options) {
    1601        return file != null && Files.isDirectory(file.toPath(), options);
    1602    }
    1603
    1604    /**
    1605     * Tests whether the directory is empty.
    1606     *
    1607     * @param directory the directory to query.
    1608     * @return whether the directory is empty.
    1609     * @throws IOException if an I/O error occurs.
    1610     * @throws NotDirectoryException if the file could not otherwise be opened because it is not a directory
    1611     *                               <em>(optional specific exception)</em>.
    1612     * @since 2.9.0
    1613     */
    1614    public static boolean isEmptyDirectory(final File directory) throws IOException {
    1615        return PathUtils.isEmptyDirectory(directory.toPath());
    1616    }
    1617
    1618    /**
    1619     * Tests if the specified {@link File} is newer than the specified {@link ChronoLocalDate}
    1620     * at the end of day.
    1621     *
    1622     * <p>
    1623     * Note: The input date is assumed to be in the system default time-zone with the time
    1624     * part set to the current time. To use a non-default time-zone use the method
    1625     * {@link #isFileNewer(File, ChronoLocalDateTime, ZoneId)
    1626     * isFileNewer(file, chronoLocalDate.atTime(LocalTime.now(zoneId)), zoneId)} where
    1627     * {@code zoneId} is a valid {@link ZoneId}.
    1628     * </p>
    1629     *
    1630     * @param file            the {@link File} of which the modification date must be compared.
    1631     * @param chronoLocalDate the date reference.
    1632     * @return true if the {@link File} exists and has been modified after the given
    1633     * {@link ChronoLocalDate} at the current time.
    1634     * @throws UncheckedIOException if an I/O error occurs.
    1635     * @throws NullPointerException if the file or local date is {@code null}.
    1636     * @since 2.8.0
    1637     */
    1638    public static boolean isFileNewer(final File file, final ChronoLocalDate chronoLocalDate) {
    1639        return isFileNewer(file, chronoLocalDate, LocalTime.MAX);
    1640    }
    1641
    1642    /**
    1643     * Tests if the specified {@link File} is newer than the specified {@link ChronoLocalDate}
    1644     * at the specified time.
    1645     *
    1646     * <p>
    1647     * Note: The input date and time are assumed to be in the system default time-zone. To use a
    1648     * non-default time-zone use the method {@link #isFileNewer(File, ChronoLocalDateTime, ZoneId)
    1649     * isFileNewer(file, chronoLocalDate.atTime(localTime), zoneId)} where {@code zoneId} is a valid
    1650     * {@link ZoneId}.
    1651     * </p>
    1652     *
    1653     * @param file            the {@link File} of which the modification date must be compared.
    1654     * @param chronoLocalDate the date reference.
    1655     * @param localTime       the time reference.
    1656     * @return true if the {@link File} exists and has been modified after the given
    1657     * {@link ChronoLocalDate} at the given time.
    1658     * @throws UncheckedIOException if an I/O error occurs.
    1659     * @throws NullPointerException if the file, local date or zone ID is {@code null}.
    1660     * @since 2.8.0
    1661     */
    1662    public static boolean isFileNewer(final File file, final ChronoLocalDate chronoLocalDate, final LocalTime localTime) {
    1663        Objects.requireNonNull(chronoLocalDate, "chronoLocalDate");
    1664        Objects.requireNonNull(localTime, "localTime");
    1665        return isFileNewer(file, chronoLocalDate.atTime(localTime));
    1666    }
    1667
    1668    /**
    1669     * Tests if the specified {@link File} is newer than the specified {@link ChronoLocalDate} at the specified
    1670     * {@link OffsetTime}.
    1671     *
    1672     * @param file the {@link File} of which the modification date must be compared.
    1673     * @param chronoLocalDate the date reference.
    1674     * @param offsetTime the time reference.
    1675     * @return true if the {@link File} exists and has been modified after the given {@link ChronoLocalDate} at the given
    1676     *         {@link OffsetTime}.
    1677     * @throws UncheckedIOException if an I/O error occurs.
    1678     * @throws NullPointerException if the file, local date or zone ID is {@code null}.
    1679     * @since 2.12.0
    1680     */
    1681    public static boolean isFileNewer(final File file, final ChronoLocalDate chronoLocalDate, final OffsetTime offsetTime) {
    1682        Objects.requireNonNull(chronoLocalDate, "chronoLocalDate");
    1683        Objects.requireNonNull(offsetTime, "offsetTime");
    1684        return isFileNewer(file, chronoLocalDate.atTime(offsetTime.toLocalTime()));
    1685    }
    1686
    1687    /**
    1688     * Tests if the specified {@link File} is newer than the specified {@link ChronoLocalDateTime}
    1689     * at the system-default time zone.
    1690     *
    1691     * <p>
    1692     * Note: The input date and time is assumed to be in the system default time-zone. To use a
    1693     * non-default time-zone use the method {@link #isFileNewer(File, ChronoLocalDateTime, ZoneId)
    1694     * isFileNewer(file, chronoLocalDateTime, zoneId)} where {@code zoneId} is a valid
    1695     * {@link ZoneId}.
    1696     * </p>
    1697     *
    1698     * @param file                the {@link File} of which the modification date must be compared.
    1699     * @param chronoLocalDateTime the date reference.
    1700     * @return true if the {@link File} exists and has been modified after the given
    1701     * {@link ChronoLocalDateTime} at the system-default time zone.
    1702     * @throws UncheckedIOException if an I/O error occurs.
    1703     * @throws NullPointerException if the file or local date time is {@code null}.
    1704     * @since 2.8.0
    1705     */
    1706    public static boolean isFileNewer(final File file, final ChronoLocalDateTime<?> chronoLocalDateTime) {
    1707        return isFileNewer(file, chronoLocalDateTime, ZoneId.systemDefault());
    1708    }
    1709
    1710    /**
    1711     * Tests if the specified {@link File} is newer than the specified {@link ChronoLocalDateTime}
    1712     * at the specified {@link ZoneId}.
    1713     *
    1714     * @param file                the {@link File} of which the modification date must be compared.
    1715     * @param chronoLocalDateTime the date reference.
    1716     * @param zoneId              the time zone.
    1717     * @return true if the {@link File} exists and has been modified after the given
    1718     * {@link ChronoLocalDateTime} at the given {@link ZoneId}.
    1719     * @throws UncheckedIOException if an I/O error occurs.
    1720     * @throws NullPointerException if the file, local date time or zone ID is {@code null}.
    1721     * @since 2.8.0
    1722     */
    1723    public static boolean isFileNewer(final File file, final ChronoLocalDateTime<?> chronoLocalDateTime, final ZoneId zoneId) {
    1724        Objects.requireNonNull(chronoLocalDateTime, "chronoLocalDateTime");
    1725        Objects.requireNonNull(zoneId, "zoneId");
    1726        return isFileNewer(file, chronoLocalDateTime.atZone(zoneId));
    1727    }
    1728
    1729    /**
    1730     * Tests if the specified {@link File} is newer than the specified {@link ChronoZonedDateTime}.
    1731     *
    1732     * @param file                the {@link File} of which the modification date must be compared.
    1733     * @param chronoZonedDateTime the date reference.
    1734     * @return true if the {@link File} exists and has been modified after the given
    1735     * {@link ChronoZonedDateTime}.
    1736     * @throws NullPointerException if the file or zoned date time is {@code null}.
    1737     * @throws UncheckedIOException if an I/O error occurs.
    1738     * @since 2.8.0
    1739     */
    1740    public static boolean isFileNewer(final File file, final ChronoZonedDateTime<?> chronoZonedDateTime) {
    1741        Objects.requireNonNull(file, PROTOCOL_FILE);
    1742        Objects.requireNonNull(chronoZonedDateTime, "chronoZonedDateTime");
    1743        return Uncheck.getAsBoolean(() -> PathUtils.isNewer(file.toPath(), chronoZonedDateTime));
    1744    }
    1745
    1746    /**
    1747     * Tests if the specified {@link File} is newer than the specified {@link Date}.
    1748     *
    1749     * @param file the {@link File} of which the modification date must be compared.
    1750     * @param date the date reference.
    1751     * @return true if the {@link File} exists and has been modified
    1752     * after the given {@link Date}.
    1753     * @throws UncheckedIOException if an I/O error occurs.
    1754     * @throws NullPointerException if the file or date is {@code null}.
    1755     */
    1756    public static boolean isFileNewer(final File file, final Date date) {
    1757        Objects.requireNonNull(date, "date");
    1758        return isFileNewer(file, date.getTime());
    1759    }
    1760
    1761    /**
    1762     * Tests if the specified {@link File} is newer than the reference {@link File}.
    1763     *
    1764     * @param file      the {@link File} of which the modification date must be compared.
    1765     * @param reference the {@link File} of which the modification date is used.
    1766     * @return true if the {@link File} exists and has been modified more
    1767     * recently than the reference {@link File}.
    1768     * @throws NullPointerException if the file or reference file is {@code null}.
    1769     * @throws UncheckedIOException if the reference file doesn't exist.
    1770     */
    1771    public static boolean isFileNewer(final File file, final File reference) {
    1772        return Uncheck.getAsBoolean(() -> PathUtils.isNewer(file.toPath(), reference.toPath()));
    1773    }
    1774
    1775    /**
    1776     * Tests if the specified {@link File} is newer than the specified {@link FileTime}.
    1777     *
    1778     * @param file the {@link File} of which the modification date must be compared.
    1779     * @param fileTime the file time reference.
    1780     * @return true if the {@link File} exists and has been modified after the given {@link FileTime}.
    1781     * @throws IOException if an I/O error occurs.
    1782     * @throws NullPointerException if the file or local date is {@code null}.
    1783     * @since 2.12.0
    1784     */
    1785    public static boolean isFileNewer(final File file, final FileTime fileTime) throws IOException {
    1786        Objects.requireNonNull(file, PROTOCOL_FILE);
    1787        return PathUtils.isNewer(file.toPath(), fileTime);
    1788    }
    1789
    1790    /**
    1791     * Tests if the specified {@link File} is newer than the specified {@link Instant}.
    1792     *
    1793     * @param file the {@link File} of which the modification date must be compared.
    1794     * @param instant the date reference.
    1795     * @return true if the {@link File} exists and has been modified after the given {@link Instant}.
    1796     * @throws NullPointerException if the file or instant is {@code null}.
    1797     * @throws UncheckedIOException if an I/O error occurs.
    1798     * @since 2.8.0
    1799     */
    1800    public static boolean isFileNewer(final File file, final Instant instant) {
    1801        Objects.requireNonNull(instant, "instant");
    1802        return Uncheck.getAsBoolean(() -> PathUtils.isNewer(file.toPath(), instant));
    1803    }
    1804
    1805    /**
    1806     * Tests if the specified {@link File} is newer than the specified time reference.
    1807     *
    1808     * @param file       the {@link File} of which the modification date must be compared.
    1809     * @param timeMillis the time reference measured in milliseconds since the
    1810     *                   epoch (00:00:00 GMT, January 1, 1970).
    1811     * @return true if the {@link File} exists and has been modified after the given time reference.
    1812     * @throws UncheckedIOException if an I/O error occurs.
    1813     * @throws NullPointerException if the file is {@code null}.
    1814     */
    1815    public static boolean isFileNewer(final File file, final long timeMillis) {
    1816        Objects.requireNonNull(file, PROTOCOL_FILE);
    1817        return Uncheck.getAsBoolean(() -> PathUtils.isNewer(file.toPath(), timeMillis));
    1818    }
    1819
    1820    /**
    1821     * Tests if the specified {@link File} is newer than the specified {@link OffsetDateTime}.
    1822     *
    1823     * @param file the {@link File} of which the modification date must be compared.
    1824     * @param offsetDateTime the date reference.
    1825     * @return true if the {@link File} exists and has been modified before the given {@link OffsetDateTime}.
    1826     * @throws UncheckedIOException if an I/O error occurs.
    1827     * @throws NullPointerException if the file or zoned date time is {@code null}.
    1828     * @since 2.12.0
    1829     */
    1830    public static boolean isFileNewer(final File file, final OffsetDateTime offsetDateTime) {
    1831        Objects.requireNonNull(offsetDateTime, "offsetDateTime");
    1832        return isFileNewer(file, offsetDateTime.toInstant());
    1833    }
    1834
    1835    /**
    1836     * Tests if the specified {@link File} is older than the specified {@link ChronoLocalDate}
    1837     * at the end of day.
    1838     *
    1839     * <p>
    1840     * Note: The input date is assumed to be in the system default time-zone with the time
    1841     * part set to the current time. To use a non-default time-zone use the method
    1842     * {@link #isFileOlder(File, ChronoLocalDateTime, ZoneId)
    1843     * isFileOlder(file, chronoLocalDate.atTime(LocalTime.now(zoneId)), zoneId)} where
    1844     * {@code zoneId} is a valid {@link ZoneId}.
    1845     * </p>
    1846     *
    1847     * @param file            the {@link File} of which the modification date must be compared.
    1848     * @param chronoLocalDate the date reference.
    1849     * @return true if the {@link File} exists and has been modified before the given
    1850     * {@link ChronoLocalDate} at the current time.
    1851     * @throws NullPointerException if the file or local date is {@code null}.
    1852     * @throws UncheckedIOException if an I/O error occurs.
    1853     * @see ZoneId#systemDefault()
    1854     * @see LocalTime#now()
    1855     * @since 2.8.0
    1856     */
    1857    public static boolean isFileOlder(final File file, final ChronoLocalDate chronoLocalDate) {
    1858        return isFileOlder(file, chronoLocalDate, LocalTime.MAX);
    1859    }
    1860
    1861    /**
    1862     * Tests if the specified {@link File} is older than the specified {@link ChronoLocalDate}
    1863     * at the specified {@link LocalTime}.
    1864     *
    1865     * <p>
    1866     * Note: The input date and time are assumed to be in the system default time-zone. To use a
    1867     * non-default time-zone use the method {@link #isFileOlder(File, ChronoLocalDateTime, ZoneId)
    1868     * isFileOlder(file, chronoLocalDate.atTime(localTime), zoneId)} where {@code zoneId} is a valid
    1869     * {@link ZoneId}.
    1870     * </p>
    1871     *
    1872     * @param file            the {@link File} of which the modification date must be compared.
    1873     * @param chronoLocalDate the date reference.
    1874     * @param localTime       the time reference.
    1875     * @return true if the {@link File} exists and has been modified before the
    1876     * given {@link ChronoLocalDate} at the specified time.
    1877     * @throws UncheckedIOException if an I/O error occurs.
    1878     * @throws NullPointerException if the file, local date or local time is {@code null}.
    1879     * @see ZoneId#systemDefault()
    1880     * @since 2.8.0
    1881     */
    1882    public static boolean isFileOlder(final File file, final ChronoLocalDate chronoLocalDate, final LocalTime localTime) {
    1883        Objects.requireNonNull(chronoLocalDate, "chronoLocalDate");
    1884        Objects.requireNonNull(localTime, "localTime");
    1885        return isFileOlder(file, chronoLocalDate.atTime(localTime));
    1886    }
    1887
    1888    /**
    1889     * Tests if the specified {@link File} is older than the specified {@link ChronoLocalDate} at the specified
    1890     * {@link OffsetTime}.
    1891     *
    1892     * @param file the {@link File} of which the modification date must be compared.
    1893     * @param chronoLocalDate the date reference.
    1894     * @param offsetTime the time reference.
    1895     * @return true if the {@link File} exists and has been modified after the given {@link ChronoLocalDate} at the given
    1896     *         {@link OffsetTime}.
    1897     * @throws NullPointerException if the file, local date or zone ID is {@code null}.
    1898     * @throws UncheckedIOException if an I/O error occurs.
    1899     * @since 2.12.0
    1900     */
    1901    public static boolean isFileOlder(final File file, final ChronoLocalDate chronoLocalDate, final OffsetTime offsetTime) {
    1902        Objects.requireNonNull(chronoLocalDate, "chronoLocalDate");
    1903        Objects.requireNonNull(offsetTime, "offsetTime");
    1904        return isFileOlder(file, chronoLocalDate.atTime(offsetTime.toLocalTime()));
    1905    }
    1906
    1907    /**
    1908     * Tests if the specified {@link File} is older than the specified {@link ChronoLocalDateTime}
    1909     * at the system-default time zone.
    1910     *
    1911     * <p>
    1912     * Note: The input date and time is assumed to be in the system default time-zone. To use a
    1913     * non-default time-zone use the method {@link #isFileOlder(File, ChronoLocalDateTime, ZoneId)
    1914     * isFileOlder(file, chronoLocalDateTime, zoneId)} where {@code zoneId} is a valid
    1915     * {@link ZoneId}.
    1916     * </p>
    1917     *
    1918     * @param file                the {@link File} of which the modification date must be compared.
    1919     * @param chronoLocalDateTime the date reference.
    1920     * @return true if the {@link File} exists and has been modified before the given
    1921     * {@link ChronoLocalDateTime} at the system-default time zone.
    1922     * @throws NullPointerException if the file or local date time is {@code null}.
    1923     * @throws UncheckedIOException if an I/O error occurs.
    1924     * @see ZoneId#systemDefault()
    1925     * @since 2.8.0
    1926     */
    1927    public static boolean isFileOlder(final File file, final ChronoLocalDateTime<?> chronoLocalDateTime) {
    1928        return isFileOlder(file, chronoLocalDateTime, ZoneId.systemDefault());
    1929    }
    1930
    1931    /**
    1932     * Tests if the specified {@link File} is older than the specified {@link ChronoLocalDateTime}
    1933     * at the specified {@link ZoneId}.
    1934     *
    1935     * @param file          the {@link File} of which the modification date must be compared.
    1936     * @param chronoLocalDateTime the date reference.
    1937     * @param zoneId        the time zone.
    1938     * @return true if the {@link File} exists and has been modified before the given
    1939     * {@link ChronoLocalDateTime} at the given {@link ZoneId}.
    1940     * @throws NullPointerException if the file, local date time or zone ID is {@code null}.
    1941     * @throws UncheckedIOException if an I/O error occurs.
    1942     * @since 2.8.0
    1943     */
    1944    public static boolean isFileOlder(final File file, final ChronoLocalDateTime<?> chronoLocalDateTime, final ZoneId zoneId) {
    1945        Objects.requireNonNull(chronoLocalDateTime, "chronoLocalDateTime");
    1946        Objects.requireNonNull(zoneId, "zoneId");
    1947        return isFileOlder(file, chronoLocalDateTime.atZone(zoneId));
    1948    }
    1949
    1950    /**
    1951     * Tests if the specified {@link File} is older than the specified {@link ChronoZonedDateTime}.
    1952     *
    1953     * @param file                the {@link File} of which the modification date must be compared.
    1954     * @param chronoZonedDateTime the date reference.
    1955     * @return true if the {@link File} exists and has been modified before the given
    1956     * {@link ChronoZonedDateTime}.
    1957     * @throws NullPointerException if the file or zoned date time is {@code null}.
    1958     * @throws UncheckedIOException if an I/O error occurs.
    1959     * @since 2.8.0
    1960     */
    1961    public static boolean isFileOlder(final File file, final ChronoZonedDateTime<?> chronoZonedDateTime) {
    1962        Objects.requireNonNull(chronoZonedDateTime, "chronoZonedDateTime");
    1963        return isFileOlder(file, chronoZonedDateTime.toInstant());
    1964    }
    1965
    1966    /**
    1967     * Tests if the specified {@link File} is older than the specified {@link Date}.
    1968     *
    1969     * @param file the {@link File} of which the modification date must be compared.
    1970     * @param date the date reference.
    1971     * @return true if the {@link File} exists and has been modified before the given {@link Date}.
    1972     * @throws NullPointerException if the file or date is {@code null}.
    1973     * @throws UncheckedIOException if an I/O error occurs.
    1974     */
    1975    public static boolean isFileOlder(final File file, final Date date) {
    1976        Objects.requireNonNull(date, "date");
    1977        return isFileOlder(file, date.getTime());
    1978    }
    1979
    1980    /**
    1981     * Tests if the specified {@link File} is older than the reference {@link File}.
    1982     *
    1983     * @param file      the {@link File} of which the modification date must be compared.
    1984     * @param reference the {@link File} of which the modification date is used.
    1985     * @return true if the {@link File} exists and has been modified before the reference {@link File}.
    1986     * @throws NullPointerException if the file or reference file is {@code null}.
    1987     * @throws FileNotFoundException if the reference file doesn't exist.
    1988     * @throws UncheckedIOException if an I/O error occurs.
    1989     */
    1990    public static boolean isFileOlder(final File file, final File reference) throws FileNotFoundException {
    1991        return Uncheck.getAsBoolean(() -> PathUtils.isOlder(file.toPath(), reference.toPath()));
    1992    }
    1993
    1994    /**
    1995     * Tests if the specified {@link File} is older than the specified {@link FileTime}.
    1996     *
    1997     * @param file the {@link File} of which the modification date must be compared.
    1998     * @param fileTime the file time reference.
    1999     * @return true if the {@link File} exists and has been modified before the given {@link FileTime}.
    2000     * @throws IOException if an I/O error occurs.
    2001     * @throws NullPointerException if the file or local date is {@code null}.
    2002     * @since 2.12.0
    2003     */
    2004    public static boolean isFileOlder(final File file, final FileTime fileTime) throws IOException {
    2005        Objects.requireNonNull(file, PROTOCOL_FILE);
    2006        return PathUtils.isOlder(file.toPath(), fileTime);
    2007    }
    2008
    2009    /**
    2010     * Tests if the specified {@link File} is older than the specified {@link Instant}.
    2011     *
    2012     * @param file    the {@link File} of which the modification date must be compared.
    2013     * @param instant the date reference.
    2014     * @return true if the {@link File} exists and has been modified before the given {@link Instant}.
    2015     * @throws NullPointerException if the file or instant is {@code null}.
    2016     * @since 2.8.0
    2017     */
    2018    public static boolean isFileOlder(final File file, final Instant instant) {
    2019        Objects.requireNonNull(instant, "instant");
    2020        return Uncheck.getAsBoolean(() -> PathUtils.isOlder(file.toPath(), instant));
    2021    }
    2022
    2023    /**
    2024     * Tests if the specified {@link File} is older than the specified time reference.
    2025     *
    2026     * @param file       the {@link File} of which the modification date must be compared.
    2027     * @param timeMillis the time reference measured in milliseconds since the
    2028     *                   epoch (00:00:00 GMT, January 1, 1970).
    2029     * @return true if the {@link File} exists and has been modified before the given time reference.
    2030     * @throws NullPointerException if the file is {@code null}.
    2031     * @throws UncheckedIOException if an I/O error occurs.
    2032     */
    2033    public static boolean isFileOlder(final File file, final long timeMillis) {
    2034        Objects.requireNonNull(file, PROTOCOL_FILE);
    2035        return Uncheck.getAsBoolean(() -> PathUtils.isOlder(file.toPath(), timeMillis));
    2036    }
    2037
    2038    /**
    2039     * Tests if the specified {@link File} is older than the specified {@link OffsetDateTime}.
    2040     *
    2041     * @param file the {@link File} of which the modification date must be compared.
    2042     * @param offsetDateTime the date reference.
    2043     * @return true if the {@link File} exists and has been modified before the given {@link OffsetDateTime}.
    2044     * @throws NullPointerException if the file or zoned date time is {@code null}
    2045     * @since 2.12.0
    2046     */
    2047    public static boolean isFileOlder(final File file, final OffsetDateTime offsetDateTime) {
    2048        Objects.requireNonNull(offsetDateTime, "offsetDateTime");
    2049        return isFileOlder(file, offsetDateTime.toInstant());
    2050    }
    2051
    2052    /**
    2053     * Tests whether the given URL is a file URL.
    2054     *
    2055     * @param url The URL to test.
    2056     * @return Whether the given URL is a file URL.
    2057     */
    2058    private static boolean isFileProtocol(final URL url) {
    2059        return PROTOCOL_FILE.equalsIgnoreCase(url.getProtocol());
    2060    }
    2061
    2062    /**
    2063     * Tests whether the specified {@link File} is a regular file or not. Implemented as a
    2064     * null-safe delegate to {@link Files#isRegularFile(Path path, LinkOption... options)}.
    2065     *
    2066     * @param   file the path to the file.
    2067     * @param   options options indicating how symbolic links are handled.
    2068     * @return  {@code true} if the file is a regular file; {@code false} if
    2069     *          the path is null, the file does not exist, is not a regular file, or it cannot
    2070     *          be determined if the file is a regular file or not.
    2071     * @throws SecurityException     In the case of the default provider, and a security manager is installed, the
    2072     *                               {@link SecurityManager#checkRead(String) checkRead} method is invoked to check read
    2073     *                               access to the directory.
    2074     * @since 2.9.0
    2075     */
    2076    public static boolean isRegularFile(final File file, final LinkOption... options) {
    2077        return file != null && Files.isRegularFile(file.toPath(), options);
    2078    }
    2079
    2080    /**
    2081     * Tests whether the specified file is a symbolic link rather than an actual file.
    2082     * <p>
    2083     * This method delegates to {@link Files#isSymbolicLink(Path path)}
    2084     * </p>
    2085     *
    2086     * @param file the file to test, may be null.
    2087     * @return true if the file is a symbolic link, see {@link Files#isSymbolicLink(Path path)}.
    2088     * @since 2.0
    2089     * @see Files#isSymbolicLink(Path)
    2090     */
    2091    public static boolean isSymlink(final File file) {
    2092        return file != null && Files.isSymbolicLink(file.toPath());
    2093    }
    2094
    2095    /**
    2096     * Iterates over the files in given directory (and optionally
    2097     * its subdirectories).
    2098     * <p>
    2099     * The resulting iterator MUST be consumed in its entirety in order to close its underlying stream.
    2100     * </p>
    2101     * <p>
    2102     * All files found are filtered by an IOFileFilter.
    2103     * </p>
    2104     *
    2105     * @param directory  The directory to search.
    2106     * @param fileFilter filter to apply when finding files.
    2107     * @param dirFilter  optional filter to apply when finding subdirectories.
    2108     *                   If this parameter is {@code null}, subdirectories will not be included in the
    2109     *                   search. Use TrueFileFilter.INSTANCE to match all directories.
    2110     * @return an iterator of {@link File} for the matching files.
    2111     * @see org.apache.commons.io.filefilter.FileFilterUtils
    2112     * @see org.apache.commons.io.filefilter.NameFileFilter
    2113     * @since 1.2
    2114     */
    2115    public static Iterator<File> iterateFiles(final File directory, final IOFileFilter fileFilter, final IOFileFilter dirFilter) {
    2116        return listFiles(directory, fileFilter, dirFilter).iterator();
    2117    }
    2118
    2119    /**
    2120     * Iterates over the files in a given directory (and optionally
    2121     * its subdirectories) which match an array of extensions.
    2122     * <p>
    2123     * The resulting iterator MUST be consumed in its entirety in order to close its underlying stream.
    2124     * </p>
    2125     *
    2126     * @param directory  The directory to search.
    2127     * @param extensions an array of extensions, for example, <code>{"java", "xml"}</code>. If this
    2128     *                   parameter is {@code null}, all files are returned.
    2129     * @param recursive  if true all subdirectories are searched as well.
    2130     * @return an iterator of {@link File} with the matching files.
    2131     * @since 1.2
    2132     */
    2133    public static Iterator<File> iterateFiles(final File directory, final String[] extensions, final boolean recursive) {
    2134        return StreamIterator.iterator(Uncheck.get(() -> streamFiles(directory, recursive, extensions)));
    2135    }
    2136
    2137    /**
    2138     * Iterates over the files in given directory (and optionally
    2139     * its subdirectories).
    2140     * <p>
    2141     * The resulting iterator MUST be consumed in its entirety in order to close its underlying stream.
    2142     * </p>
    2143     * <p>
    2144     * All files found are filtered by an IOFileFilter.
    2145     * </p>
    2146     * <p>
    2147     * The resulting iterator includes the subdirectories themselves.
    2148     * </p>
    2149     *
    2150     * @param directory  The directory to search.
    2151     * @param fileFilter filter to apply when finding files.
    2152     * @param dirFilter  optional filter to apply when finding subdirectories.
    2153     *                   If this parameter is {@code null}, subdirectories will not be included in the
    2154     *                   search. Use TrueFileFilter.INSTANCE to match all directories.
    2155     * @return an iterator of {@link File} for the matching files.
    2156     * @see org.apache.commons.io.filefilter.FileFilterUtils
    2157     * @see org.apache.commons.io.filefilter.NameFileFilter
    2158     * @since 2.2
    2159     */
    2160    public static Iterator<File> iterateFilesAndDirs(final File directory, final IOFileFilter fileFilter, final IOFileFilter dirFilter) {
    2161        return listFilesAndDirs(directory, fileFilter, dirFilter).iterator();
    2162    }
    2163
    2164    /**
    2165     * Returns the last modification time in milliseconds via
    2166     * {@link java.nio.file.Files#getLastModifiedTime(Path, LinkOption...)}.
    2167     * <p>
    2168     * For the best precision, use {@link #lastModifiedFileTime(File)}.
    2169     * </p>
    2170     * <p>
    2171     * Use this method to avoid issues with {@link File#lastModified()} like
    2172     * <a href="https://bugs.openjdk.java.net/browse/JDK-8177809">JDK-8177809</a> where {@link File#lastModified()} is
    2173     * losing milliseconds (always ends in 000). This bug exists in OpenJDK 8 and 9, and is fixed in 10.
    2174     * </p>
    2175     *
    2176     * @param file The File to query.
    2177     * @return See {@link java.nio.file.attribute.FileTime#toMillis()}.
    2178     * @throws IOException if an I/O error occurs.
    2179     * @since 2.9.0
    2180     */
    2181    public static long lastModified(final File file) throws IOException {
    2182        // https://bugs.openjdk.java.net/browse/JDK-8177809
    2183        // File.lastModified() is losing milliseconds (always ends in 000)
    2184        // This bug is in OpenJDK 8 and 9, and fixed in 10.
    2185        return lastModifiedFileTime(file).toMillis();
    2186    }
    2187
    2188    /**
    2189     * Returns the last modification {@link FileTime} via
    2190     * {@link java.nio.file.Files#getLastModifiedTime(Path, LinkOption...)}.
    2191     * <p>
    2192     * Use this method to avoid issues with {@link File#lastModified()} like
    2193     * <a href="https://bugs.openjdk.java.net/browse/JDK-8177809">JDK-8177809</a> where {@link File#lastModified()} is
    2194     * losing milliseconds (always ends in 000). This bug exists in OpenJDK 8 and 9, and is fixed in 10.
    2195     * </p>
    2196     *
    2197     * @param file The File to query.
    2198     * @return See {@link java.nio.file.Files#getLastModifiedTime(Path, LinkOption...)}.
    2199     * @throws IOException if an I/O error occurs.
    2200     * @since 2.12.0
    2201     */
    2202    public static FileTime lastModifiedFileTime(final File file) throws IOException {
    2203        // https://bugs.openjdk.java.net/browse/JDK-8177809
    2204        // File.lastModified() is losing milliseconds (always ends in 000)
    2205        // This bug is in OpenJDK 8 and 9, and fixed in 10.
    2206        return Files.getLastModifiedTime(Objects.requireNonNull(file, PROTOCOL_FILE).toPath());
    2207    }
    2208
    2209    /**
    2210     * Returns the last modification time in milliseconds via
    2211     * {@link java.nio.file.Files#getLastModifiedTime(Path, LinkOption...)}.
    2212     * <p>
    2213     * For the best precision, use {@link #lastModifiedFileTime(File)}.
    2214     * </p>
    2215     * <p>
    2216     * Use this method to avoid issues with {@link File#lastModified()} like
    2217     * <a href="https://bugs.openjdk.java.net/browse/JDK-8177809">JDK-8177809</a> where {@link File#lastModified()} is
    2218     * losing milliseconds (always ends in 000). This bug exists in OpenJDK 8 and 9, and is fixed in 10.
    2219     * </p>
    2220     *
    2221     * @param file The File to query.
    2222     * @return See {@link java.nio.file.attribute.FileTime#toMillis()}.
    2223     * @throws UncheckedIOException if an I/O error occurs.
    2224     * @since 2.9.0
    2225     */
    2226    public static long lastModifiedUnchecked(final File file) {
    2227        // https://bugs.openjdk.java.net/browse/JDK-8177809
    2228        // File.lastModified() is losing milliseconds (always ends in 000)
    2229        // This bug is in OpenJDK 8 and 9, and fixed in 10.
    2230        return Uncheck.apply(FileUtils::lastModified, file);
    2231    }
    2232
    2233    /**
    2234     * Returns an Iterator for the lines in a {@link File} using the default encoding for the VM.
    2235     *
    2236     * @param file the file to open for input, must not be {@code null}.
    2237     * @return an Iterator of the lines in the file, never {@code null}.
    2238     * @throws NullPointerException if file is {@code null}.
    2239     * @throws FileNotFoundException if the file does not exist, is a directory rather than a regular file, or for some
    2240     *         other reason cannot be opened for reading.
    2241     * @throws IOException if an I/O error occurs.
    2242     * @see #lineIterator(File, String)
    2243     * @since 1.3
    2244     */
    2245    public static LineIterator lineIterator(final File file) throws IOException {
    2246        return lineIterator(file, null);
    2247    }
    2248
    2249    /**
    2250     * Returns an Iterator for the lines in a {@link File}.
    2251     * <p>
    2252     * This method opens an {@link InputStream} for the file.
    2253     * When you have finished with the iterator you should close the stream
    2254     * to free internal resources. This can be done by using a try-with-resources block or calling the
    2255     * {@link LineIterator#close()} method.
    2256     * </p>
    2257     * <p>
    2258     * The recommended usage pattern is:
    2259     * </p>
    2260     * <pre>
    2261     * LineIterator it = FileUtils.lineIterator(file, StandardCharsets.UTF_8.name());
    2262     * try {
    2263     *   while (it.hasNext()) {
    2264     *     String line = it.nextLine();
    2265     *     // do something with line
    2266     *   }
    2267     * } finally {
    2268     *   LineIterator.closeQuietly(iterator);
    2269     * }
    2270     * </pre>
    2271     * <p>
    2272     * If an exception occurs during the creation of the iterator, the
    2273     * underlying stream is closed.
    2274     * </p>
    2275     *
    2276     * @param file     the file to open for input, must not be {@code null}.
    2277     * @param charsetName the name of the requested charset, {@code null} means platform default.
    2278     * @return a LineIterator for lines in the file, never {@code null}; MUST be closed by the caller.
    2279     * @throws NullPointerException if file is {@code null}.
    2280     * @throws FileNotFoundException if the file does not exist, is a directory rather than a regular file, or for some
    2281     *         other reason cannot be opened for reading.
    2282     * @throws IOException if an I/O error occurs.
    2283     * @since 1.2
    2284     */
    2285    @SuppressWarnings("resource") // Caller closes the result LineIterator.
    2286    public static LineIterator lineIterator(final File file, final String charsetName) throws IOException {
    2287        InputStream inputStream = null;
    2288        try {
    2289            inputStream = Files.newInputStream(file.toPath());
    2290            return IOUtils.lineIterator(inputStream, charsetName);
    2291        } catch (final IOException | RuntimeException ex) {
    2292            IOUtils.closeQuietlySuppress(inputStream, ex);
    2293            throw ex;
    2294        }
    2295    }
    2296
    2297    private static AccumulatorPathVisitor listAccumulate(final File directory, final IOFileFilter fileFilter, final IOFileFilter dirFilter,
    2298            final FileVisitOption... options) throws IOException {
    2299        final boolean isDirFilterSet = dirFilter != null;
    2300        final FileEqualsFileFilter rootDirFilter = new FileEqualsFileFilter(directory);
    2301        final PathFilter dirPathFilter = isDirFilterSet ? rootDirFilter.or(dirFilter) : rootDirFilter;
    2302        // @formatter:off
    2303        final AccumulatorPathVisitor visitor = AccumulatorPathVisitor.builder()
    2304                .setPathCounters(Counters.noopPathCounters())
    2305                .setFileFilter(fileFilter)
    2306                .setDirectoryFilter(dirPathFilter)
    2307                .setVisitFileFailedFunction((p, e) -> FileVisitResult.CONTINUE)
    2308                .get();
    2309        // @formatter:on
    2310        final Set<FileVisitOption> optionSet = new HashSet<>();
    2311        if (options != null) {
    2312            Collections.addAll(optionSet, options);
    2313        }
    2314        Files.walkFileTree(directory.toPath(), optionSet, toMaxDepth(isDirFilterSet), visitor);
    2315        return visitor;
    2316    }
    2317
    2318    /**
    2319     * Lists files in a directory, asserting that the supplied directory exists and is a directory.
    2320     *
    2321     * @param directory  The directory to list.
    2322     * @param fileFilter Optional file filter, may be null.
    2323     * @return The files in the directory, never {@code null}.
    2324     * @throws NullPointerException     if the {@code directory} is {@code null}.
    2325     * @throws IllegalArgumentException if the {@code directory} exists but is not a directory.
    2326     * @throws IOException              if an I/O error occurs per {@link File#listFiles()} and {@link File#listFiles(FileFilter)}.
    2327     * @throws SecurityException        If a security manager exists and its {@link SecurityManager#checkRead(String)} method denies read access to the
    2328     *                                  directory.
    2329     */
    2330    private static File[] listFiles(final File directory, final FileFilter fileFilter) throws IOException {
    2331        requireDirectoryExists(directory, "directory");
    2332        final File[] files = directory.listFiles(fileFilter);
    2333        if (files == null) {
    2334            // null if the directory does not denote a directory, or if an I/O error occurs.
    2335            throw new IOException("Unknown I/O error listing contents of directory: " + directory);
    2336        }
    2337        return files;
    2338    }
    2339
    2340    /**
    2341     * Finds files within a given directory (and optionally its
    2342     * subdirectories). All files found are filtered by an IOFileFilter.
    2343     * <p>
    2344     * If your search should recurse into subdirectories you can pass in
    2345     * an IOFileFilter for directories. You don't need to bind a
    2346     * DirectoryFileFilter (via logical AND) to this filter. This method does
    2347     * that for you.
    2348     * </p>
    2349     * <p>
    2350     * An example: If you want to search through all directories called
    2351     * "temp" you pass in {@code FileFilterUtils.NameFileFilter("temp")}.
    2352     * </p>
    2353     * <p>
    2354     * Another common usage of this method is find files in a directory
    2355     * tree but ignoring the directories generated CVS. You can simply pass
    2356     * in {@code FileFilterUtils.makeCVSAware(null)}.
    2357     * </p>
    2358     *
    2359     * @param directory  The directory to search.
    2360     * @param fileFilter filter to apply when finding files. Must not be {@code null},
    2361     *                   use {@link TrueFileFilter#INSTANCE} to match all files in selected directories.
    2362     * @param dirFilter  optional filter to apply when finding subdirectories.
    2363     *                   If this parameter is {@code null}, subdirectories will not be included in the
    2364     *                   search. Use {@link TrueFileFilter#INSTANCE} to match all directories.
    2365     * @return a collection of {@link File} with the matching files.
    2366     * @see org.apache.commons.io.filefilter.FileFilterUtils
    2367     * @see org.apache.commons.io.filefilter.NameFileFilter
    2368     */
    2369    public static Collection<File> listFiles(final File directory, final IOFileFilter fileFilter, final IOFileFilter dirFilter) {
    2370        final AccumulatorPathVisitor visitor = Uncheck
    2371            .apply(d -> listAccumulate(d, FileFileFilter.INSTANCE.and(fileFilter), dirFilter, FileVisitOption.FOLLOW_LINKS), directory);
    2372        return toList(visitor.getFileList().stream().map(Path::toFile));
    2373    }
    2374
    2375    /**
    2376     * Lists Files in the given {@code directory}, adding each file to the given list.
    2377     *
    2378     * @param directory A File for an assumed directory, not null.
    2379     * @param files The list to add found Files, not null.
    2380     * @param recursive Whether or not to recurse into subdirectories.
    2381     * @param filter How to filter files, not null.
    2382     * @return The given list.
    2383     */
    2384    @SuppressWarnings("null")
    2385    private static List<File> listFiles(final File directory, final List<File> files, final boolean recursive, final FilenameFilter filter) {
    2386        final File[] listFiles = directory.listFiles();
    2387        if (listFiles != null) {
    2388            // Only allocate if you must.
    2389            final List<File> dirs = recursive ? new ArrayList<>() : null;
    2390            Arrays.stream(listFiles).forEach(f -> {
    2391                if (recursive && f.isDirectory()) {
    2392                    dirs.add(f);
    2393                } else if (f.isFile() && filter.accept(directory, f.getName())) {
    2394                    files.add(f);
    2395                }
    2396            });
    2397            if (recursive) {
    2398                dirs.forEach(d -> listFiles(d, files, true, filter));
    2399            }
    2400        }
    2401        return files;
    2402    }
    2403
    2404    /**
    2405     * Lists files within a given directory (and optionally its subdirectories)
    2406     * which match an array of extensions.
    2407     *
    2408     * @param directory  The directory to search.
    2409     * @param extensions an array of extensions, for example, <code>{"java", "xml"}</code>. If this
    2410     *                   parameter is {@code null}, all files are returned.
    2411     * @param recursive  if true all subdirectories are searched as well.
    2412     * @return a collection of {@link File} with the matching files.
    2413     */
    2414    public static Collection<File> listFiles(final File directory, final String[] extensions, final boolean recursive) {
    2415        return listFiles(directory, new ArrayList<>(), recursive, extensions != null ? toSuffixFileFilter(extensions) : TrueFileFilter.INSTANCE);
    2416    }
    2417
    2418    /**
    2419     * Finds files within a given directory (and optionally its
    2420     * subdirectories). All files found are filtered by an IOFileFilter.
    2421     * <p>
    2422     * The resulting collection includes the starting directory and
    2423     * any subdirectories that match the directory filter.
    2424     * </p>
    2425     *
    2426     * @param directory  The directory to search.
    2427     * @param fileFilter filter to apply when finding files.
    2428     * @param dirFilter  optional filter to apply when finding subdirectories.
    2429     *                   If this parameter is {@code null}, subdirectories will not be included in the
    2430     *                   search. Use TrueFileFilter.INSTANCE to match all directories.
    2431     * @return a collection of {@link File} with the matching files.
    2432     * @see org.apache.commons.io.FileUtils#listFiles
    2433     * @see org.apache.commons.io.filefilter.FileFilterUtils
    2434     * @see org.apache.commons.io.filefilter.NameFileFilter
    2435     * @since 2.2
    2436     */
    2437    public static Collection<File> listFilesAndDirs(final File directory, final IOFileFilter fileFilter, final IOFileFilter dirFilter) {
    2438        final AccumulatorPathVisitor visitor = Uncheck.apply(d -> listAccumulate(d, fileFilter, dirFilter, FileVisitOption.FOLLOW_LINKS),
    2439            directory);
    2440        final List<Path> list = visitor.getFileList();
    2441        list.addAll(visitor.getDirList());
    2442        return toList(list.stream().map(Path::toFile));
    2443    }
    2444
    2445    /**
    2446     * Calls {@link File#mkdirs()} and throws an {@link IOException} on failure.
    2447     * <p>
    2448     * Creates all directories for a File object, including any necessary but non-existent parent directories. If the {@code directory} already exists or is
    2449     * null, nothing happens.
    2450     * </p>
    2451     *
    2452     * @param directory the receiver for {@code mkdirs()}. If the {@code directory} already exists or is null, nothing happens.
    2453     * @return the given directory.
    2454     * @throws IOException       if the directory was not created along with all its parent directories.
    2455     * @throws IOException       if the given file object is not a directory.
    2456     * @throws SecurityException See {@link File#mkdirs()}.
    2457     * @see File#mkdirs()
    2458     */
    2459    private static File mkdirs(final File directory) throws IOException {
    2460        if (directory != null && !directory.mkdirs() && !directory.isDirectory()) {
    2461            throw new IOException("Cannot create directory '" + directory + "'.");
    2462        }
    2463        return directory;
    2464    }
    2465
    2466    /**
    2467     * Moves a directory.
    2468     * <p>
    2469     * When the destination directory is on another file system, do a "copy and delete".
    2470     * </p>
    2471     *
    2472     * @param srcDir the directory to be moved.
    2473     * @param destDir the destination directory.
    2474     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
    2475     * @throws IllegalArgumentException if {@code srcDir} exists but is not a directory.
    2476     * @throws FileNotFoundException if the source does not exist.
    2477     * @throws IOException if an error occurs or setting the last-modified time didn't succeed.
    2478     * @since 1.4
    2479     */
    2480    public static void moveDirectory(final File srcDir, final File destDir) throws IOException {
    2481        Objects.requireNonNull(destDir, "destination");
    2482        requireDirectoryExists(srcDir, "srcDir");
    2483        requireAbsent(destDir, "destDir");
    2484        if (!srcDir.renameTo(destDir)) {
    2485            if (destDir.getCanonicalPath().startsWith(srcDir.getCanonicalPath() + File.separator)) {
    2486                throw new IOException("Cannot move directory: " + srcDir + " to a subdirectory of itself: " + destDir);
    2487            }
    2488            copyDirectory(srcDir, destDir);
    2489            deleteDirectory(srcDir);
    2490            if (srcDir.exists()) {
    2491                throw new IOException("Failed to delete original directory '" + srcDir +
    2492                        "' after copy to '" + destDir + "'");
    2493            }
    2494        }
    2495    }
    2496
    2497    /**
    2498     * Moves a directory to another directory.
    2499     * <p>
    2500     * If {@code createDestDir} is true, creates all destination parent directories, including any necessary but non-existent parent directories.
    2501     * </p>
    2502     *
    2503     * @param source the directory to be moved.
    2504     * @param destDir the destination file.
    2505     * @param createDestDir If {@code true} create the destination directory, otherwise if {@code false} throw an
    2506     *        IOException.
    2507     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
    2508     * @throws IllegalArgumentException if the source or destination is invalid.
    2509     * @throws FileNotFoundException if the source does not exist.
    2510     * @throws IOException if the directory was not created along with all its parent directories, if enabled.
    2511     * @throws IOException if an error occurs or setting the last-modified time didn't succeed.
    2512     * @throws SecurityException See {@link File#mkdirs()}.
    2513     * @since 1.4
    2514     */
    2515    public static void moveDirectoryToDirectory(final File source, final File destDir, final boolean createDestDir) throws IOException {
    2516        validateMoveParameters(source, destDir);
    2517        if (!destDir.isDirectory()) {
    2518            if (destDir.exists()) {
    2519                throw new IOException("Destination '" + destDir + "' is not a directory");
    2520            }
    2521            if (!createDestDir) {
    2522                throw new FileNotFoundException("Destination directory '" + destDir + "' does not exist [createDestDir=" + false + "]");
    2523            }
    2524            mkdirs(destDir);
    2525        }
    2526        moveDirectory(source, new File(destDir, source.getName()));
    2527    }
    2528
    2529    /**
    2530     * Moves a file preserving attributes.
    2531     * <p>
    2532     * Shorthand for {@code moveFile(srcFile, destFile, StandardCopyOption.COPY_ATTRIBUTES)}.
    2533     * </p>
    2534     * <p>
    2535     * When the destination file is on another file system, do a "copy and delete".
    2536     * </p>
    2537     *
    2538     * @param srcFile the file to be moved.
    2539     * @param destFile the destination file.
    2540     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
    2541     * @throws FileExistsException if the destination file exists.
    2542     * @throws FileNotFoundException if the source file does not exist.
    2543     * @throws IllegalArgumentException if {@code srcFile} is a directory.
    2544     * @throws IOException if an error occurs.
    2545     * @since 1.4
    2546     */
    2547    public static void moveFile(final File srcFile, final File destFile) throws IOException {
    2548        moveFile(srcFile, destFile, StandardCopyOption.COPY_ATTRIBUTES);
    2549    }
    2550
    2551    /**
    2552     * Moves a file.
    2553     * <p>
    2554     * When the destination file is on another file system, do a "copy and delete".
    2555     * </p>
    2556     *
    2557     * @param srcFile the file to be moved.
    2558     * @param destFile the destination file.
    2559     * @param copyOptions Copy options.
    2560     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
    2561     * @throws FileExistsException if the destination file exists.
    2562     * @throws FileNotFoundException if the source file does not exist.
    2563     * @throws IllegalArgumentException if {@code srcFile} is a directory.
    2564     * @throws IOException if an error occurs or setting the last-modified time didn't succeed.
    2565     * @since 2.9.0
    2566     */
    2567    public static void moveFile(final File srcFile, final File destFile, final CopyOption... copyOptions) throws IOException {
    2568        Objects.requireNonNull(destFile, "destFile");
    2569        checkFileExists(srcFile, "srcFile");
    2570        requireAbsent(destFile, "destFile");
    2571        final boolean rename = srcFile.renameTo(destFile);
    2572        if (!rename) {
    2573            // Don't interfere with file date on move, handled by StandardCopyOption.COPY_ATTRIBUTES
    2574            copyFile(srcFile, destFile, false, copyOptions);
    2575            if (!srcFile.delete()) {
    2576                deleteQuietly(destFile);
    2577                throw new IOException("Failed to delete original file '" + srcFile + "' after copy to '" + destFile + "'");
    2578            }
    2579        }
    2580    }
    2581
    2582    /**
    2583     * Moves a file into a directory.
    2584     * <p>
    2585     * If {@code createDestDir} is true, creates all destination parent directories, including any necessary but non-existent parent directories.
    2586     * </p>
    2587     *
    2588     * @param srcFile the file to be moved.
    2589     * @param destDir the directory to move the file into.
    2590     * @param createDestDir if {@code true} create the destination directory. If {@code false} throw an
    2591     *        IOException if the destination directory does not already exist.
    2592     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
    2593     * @throws FileExistsException if the destination file exists.
    2594     * @throws FileNotFoundException if the source file does not exist.
    2595     * @throws IOException if source or destination is invalid.
    2596     * @throws IOException if the directory was not created along with all its parent directories, if enabled.
    2597     * @throws IOException if an error occurs or setting the last-modified time didn't succeed.
    2598     * @throws SecurityException See {@link File#mkdirs()}.
    2599     * @throws IllegalArgumentException if {@code destDir} exists but is not a directory.
    2600     * @since 1.4
    2601     */
    2602    public static void moveFileToDirectory(final File srcFile, final File destDir, final boolean createDestDir) throws IOException {
    2603        validateMoveParameters(srcFile, destDir);
    2604        if (!destDir.exists() && createDestDir) {
    2605            mkdirs(destDir);
    2606        }
    2607        requireDirectoryExists(destDir, "destDir");
    2608        moveFile(srcFile, new File(destDir, srcFile.getName()));
    2609    }
    2610
    2611    /**
    2612     * Moves a file or directory into a destination directory.
    2613     * <p>
    2614     * If {@code createDestDir} is true, creates all destination parent directories, including any necessary but non-existent parent directories.
    2615     * </p>
    2616     * <p>
    2617     * When the destination is on another file system, do a "copy and delete".
    2618     * </p>
    2619     *
    2620     * @param src           the file or directory to be moved.
    2621     * @param destDir       the destination directory.
    2622     * @param createDestDir if {@code true} create the destination directory. If {@code false} throw an
    2623     *        IOException if the destination directory does not already exist.
    2624     * @throws NullPointerException  if any of the given {@link File}s are {@code null}.
    2625     * @throws FileExistsException   if the directory or file exists in the destination directory.
    2626     * @throws FileNotFoundException if the source file does not exist.
    2627     * @throws IOException           if source or destination is invalid.
    2628     * @throws IOException           if an error occurs or setting the last-modified time didn't succeed.
    2629     * @since 1.4
    2630     */
    2631    public static void moveToDirectory(final File src, final File destDir, final boolean createDestDir) throws IOException {
    2632        validateMoveParameters(src, destDir);
    2633        if (src.isDirectory()) {
    2634            moveDirectoryToDirectory(src, destDir, createDestDir);
    2635        } else {
    2636            moveFileToDirectory(src, destDir, createDestDir);
    2637        }
    2638    }
    2639
    2640    /**
    2641     * Creates a new OutputStream by opening or creating a file, returning an output stream that may be used to write bytes
    2642     * to the file.
    2643     *
    2644     * @param append Whether or not to append.
    2645     * @param file the File.
    2646     * @return a new OutputStream.
    2647     * @throws IOException if an I/O error occurs.
    2648     * @see PathUtils#newOutputStream(Path, boolean)
    2649     * @since 2.12.0
    2650     */
    2651    public static OutputStream newOutputStream(final File file, final boolean append) throws IOException {
    2652        return PathUtils.newOutputStream(Objects.requireNonNull(file, PROTOCOL_FILE).toPath(), append);
    2653    }
    2654
    2655    /**
    2656     * Opens a {@link FileInputStream} for the specified file, providing better error messages than simply calling
    2657     * {@code new FileInputStream(file)}.
    2658     * <p>
    2659     * At the end of the method either the stream will be successfully opened, or an exception will have been thrown.
    2660     * </p>
    2661     * <p>
    2662     * An exception is thrown if the file does not exist. An exception is thrown if the file object exists but is a
    2663     * directory. An exception is thrown if the file exists but cannot be read.
    2664     * </p>
    2665     *
    2666     * @param file the file to open for input, must not be {@code null}.
    2667     * @return a new {@link FileInputStream} for the specified file.
    2668     * @throws NullPointerException if file is {@code null}.
    2669     * @throws FileNotFoundException if the file does not exist, is a directory rather than a regular file, or for some
    2670     *         other reason cannot be opened for reading.
    2671     * @throws IOException See FileNotFoundException above, FileNotFoundException is a subclass of IOException.
    2672     * @since 1.3
    2673     */
    2674    public static FileInputStream openInputStream(final File file) throws IOException {
    2675        Objects.requireNonNull(file, PROTOCOL_FILE);
    2676        return new FileInputStream(file);
    2677    }
    2678
    2679    /**
    2680     * Opens a {@link FileOutputStream} for the specified file, checking and
    2681     * creating the parent directory if it does not exist.
    2682     * <p>
    2683     * At the end of the method either the stream will be successfully opened,
    2684     * or an exception will have been thrown.
    2685     * </p>
    2686     * <p>
    2687     * The parent directory will be created if it does not exist.
    2688     * The file will be created if it does not exist.
    2689     * An exception is thrown if the file object exists but is a directory.
    2690     * An exception is thrown if the file exists but cannot be written to.
    2691     * An exception is thrown if the parent directory cannot be created.
    2692     * </p>
    2693     *
    2694     * @param file the file to open for output, must not be {@code null}.
    2695     * @return a new {@link FileOutputStream} for the specified file.
    2696     * @throws NullPointerException if the file object is {@code null}.
    2697     * @throws IllegalArgumentException if the file object is a directory.
    2698     * @throws IllegalArgumentException if the file is not writable.
    2699     * @throws IOException if the directories could not be created.
    2700     * @since 1.3
    2701     */
    2702    public static FileOutputStream openOutputStream(final File file) throws IOException {
    2703        return openOutputStream(file, false);
    2704    }
    2705
    2706    /**
    2707     * Opens a {@link FileOutputStream} for the specified file, checking and
    2708     * creating the parent directory if it does not exist.
    2709     * <p>
    2710     * At the end of the method either the stream will be successfully opened,
    2711     * or an exception will have been thrown.
    2712     * </p>
    2713     * <p>
    2714     * The parent directory will be created if it does not exist.
    2715     * The file will be created if it does not exist.
    2716     * An exception is thrown if the file object exists but is a directory.
    2717     * An exception is thrown if the file exists but cannot be written to.
    2718     * An exception is thrown if the parent directory cannot be created.
    2719     * </p>
    2720     *
    2721     * @param file   the file to open for output, must not be {@code null}.
    2722     * @param append if {@code true}, then bytes will be added to the
    2723     *               end of the file rather than overwriting.
    2724     * @return a new {@link FileOutputStream} for the specified file.
    2725     * @throws NullPointerException if the file object is {@code null}.
    2726     * @throws IllegalArgumentException if the file object is a directory.
    2727     * @throws IOException if the directories could not be created, or the file is not writable.
    2728     * @since 2.1
    2729     */
    2730    public static FileOutputStream openOutputStream(final File file, final boolean append) throws IOException {
    2731        Objects.requireNonNull(file, PROTOCOL_FILE);
    2732        if (file.exists()) {
    2733            checkIsFile(file, PROTOCOL_FILE);
    2734        } else {
    2735            createParentDirectories(file);
    2736        }
    2737        return new FileOutputStream(file, append);
    2738    }
    2739
    2740    /**
    2741     * Reads the contents of a file into a byte array.
    2742     * The file is always closed.
    2743     *
    2744     * @param file the file to read, must not be {@code null}.
    2745     * @return the file contents, never {@code null}.
    2746     * @throws NullPointerException if file is {@code null}.
    2747     * @throws IOException if an I/O error occurs, including when the file does not exist, is a directory rather than a
    2748     *         regular file, or for some other reason why the file cannot be opened for reading.
    2749     * @since 1.1
    2750     */
    2751    public static byte[] readFileToByteArray(final File file) throws IOException {
    2752        Objects.requireNonNull(file, PROTOCOL_FILE);
    2753        return Files.readAllBytes(file.toPath());
    2754    }
    2755
    2756    /**
    2757     * Reads the contents of a file into a String using the virtual machine's {@linkplain Charset#defaultCharset() default charset}. The
    2758     * file is always closed.
    2759     *
    2760     * @param file the file to read, must not be {@code null}.
    2761     * @return the file contents, never {@code null}.
    2762     * @throws NullPointerException if file is {@code null}.
    2763     * @throws IOException          if an I/O error occurs, including when the file does not exist, is a directory rather than a regular file, or for some other
    2764     *                              reason why the file cannot be opened for reading.
    2765     * @since 1.3.1
    2766     * @deprecated Use {@link #readFileToString(File, Charset)} instead (and specify the appropriate encoding).
    2767     */
    2768    @Deprecated
    2769    public static String readFileToString(final File file) throws IOException {
    2770        return readFileToString(file, Charset.defaultCharset());
    2771    }
    2772
    2773    /**
    2774     * Reads the contents of a file into a String.
    2775     * The file is always closed.
    2776     *
    2777     * @param file     the file to read, must not be {@code null}.
    2778     * @param charsetName the name of the requested charset, {@code null} means platform default.
    2779     * @return the file contents, never {@code null}.
    2780     * @throws NullPointerException if file is {@code null}.
    2781     * @throws IOException if an I/O error occurs, including when the file does not exist, is a directory rather than a
    2782     *         regular file, or for some other reason why the file cannot be opened for reading.
    2783     * @since 2.3
    2784     */
    2785    public static String readFileToString(final File file, final Charset charsetName) throws IOException {
    2786        return IOUtils.toString(() -> Files.newInputStream(file.toPath()), Charsets.toCharset(charsetName));
    2787    }
    2788
    2789    /**
    2790     * Reads the contents of a file into a String. The file is always closed.
    2791     *
    2792     * @param file     the file to read, must not be {@code null}.
    2793     * @param charsetName the name of the requested charset, {@code null} means platform default.
    2794     * @return the file contents, never {@code null}.
    2795     * @throws NullPointerException if file is {@code null}.
    2796     * @throws IOException if an I/O error occurs, including when the file does not exist, is a directory rather than a
    2797     *         regular file, or for some other reason why the file cannot be opened for reading.
    2798     * @throws java.nio.charset.UnsupportedCharsetException if the named charset is unavailable.
    2799     * @since 2.3
    2800     */
    2801    public static String readFileToString(final File file, final String charsetName) throws IOException {
    2802        return readFileToString(file, Charsets.toCharset(charsetName));
    2803    }
    2804
    2805    /**
    2806     * Reads the contents of a file line by line to a List of Strings using the virtual machine's {@linkplain Charset#defaultCharset() default charset}.
    2807     * The file is always closed.
    2808     *
    2809     * @param file the file to read, must not be {@code null}.
    2810     * @return the list of Strings representing each line in the file, never {@code null}.
    2811     * @throws NullPointerException if file is {@code null}.
    2812     * @throws IOException          if an I/O error occurs, including when the file does not exist, is a directory rather than a regular file, or for some other
    2813     *                              reason why the file cannot be opened for reading.
    2814     * @since 1.3
    2815     * @deprecated Use {@link #readLines(File, Charset)} instead (and specify the appropriate encoding).
    2816     */
    2817    @Deprecated
    2818    public static List<String> readLines(final File file) throws IOException {
    2819        return readLines(file, Charset.defaultCharset());
    2820    }
    2821
    2822    /**
    2823     * Reads the contents of a file line by line to a List of Strings.
    2824     * The file is always closed.
    2825     *
    2826     * @param file     the file to read, must not be {@code null}.
    2827     * @param charset the charset to use, {@code null} means platform default.
    2828     * @return the list of Strings representing each line in the file, never {@code null}.
    2829     * @throws NullPointerException if file is {@code null}.
    2830     * @throws IOException if an I/O error occurs, including when the file does not exist, is a directory rather than a
    2831     *         regular file, or for some other reason why the file cannot be opened for reading.
    2832     * @since 2.3
    2833     */
    2834    public static List<String> readLines(final File file, final Charset charset) throws IOException {
    2835        return Files.readAllLines(file.toPath(), Charsets.toCharset(charset));
    2836    }
    2837
    2838    /**
    2839     * Reads the contents of a file line by line to a List of Strings. The file is always closed.
    2840     *
    2841     * @param file     the file to read, must not be {@code null}.
    2842     * @param charsetName the name of the requested charset, {@code null} means platform default.
    2843     * @return the list of Strings representing each line in the file, never {@code null}.
    2844     * @throws NullPointerException if file is {@code null}.
    2845     * @throws IOException if an I/O error occurs, including when the file does not exist, is a directory rather than a
    2846     *         regular file, or for some other reason why the file cannot be opened for reading.
    2847     * @throws java.nio.charset.UnsupportedCharsetException if the named charset is unavailable.
    2848     * @since 1.1
    2849     */
    2850    public static List<String> readLines(final File file, final String charsetName) throws IOException {
    2851        return readLines(file, Charsets.toCharset(charsetName));
    2852    }
    2853
    2854    private static void requireAbsent(final File file, final String name) throws FileExistsException {
    2855        if (file.exists()) {
    2856            throw new FileExistsException(String.format("File element in parameter '%s' already exists: '%s'", name, file));
    2857        }
    2858    }
    2859
    2860    /**
    2861     * Throws IllegalArgumentException if the given files' canonical representations are equal.
    2862     *
    2863     * @param file1 The first file to compare.
    2864     * @param file2 The second file to compare.
    2865     * @throws IOException if an I/O error occurs.
    2866     * @throws IllegalArgumentException if the given files' canonical representations are equal.
    2867     */
    2868    private static void requireCanonicalPathsNotEquals(final File file1, final File file2) throws IOException {
    2869        final String canonicalPath = file1.getCanonicalPath();
    2870        if (canonicalPath.equals(file2.getCanonicalPath())) {
    2871            throw new IllegalArgumentException(String
    2872                .format("File canonical paths are equal: '%s' (file1='%s', file2='%s')", canonicalPath, file1, file2));
    2873        }
    2874    }
    2875
    2876    /**
    2877     * Requires that the given {@link File} exists and is a directory.
    2878     *
    2879     * @param directory The {@link File} to check.
    2880     * @param name The parameter name to use in the exception message in case of null input or if the file is not a directory.
    2881     * @throws NullPointerException if the given {@link File} is {@code null}.
    2882     * @throws FileNotFoundException if the given {@link File} does not exist.
    2883     * @throws IllegalArgumentException if the given {@link File} exists but is not a directory.
    2884     */
    2885    private static void requireDirectoryExists(final File directory, final String name) throws FileNotFoundException {
    2886        Objects.requireNonNull(directory, name);
    2887        if (!directory.isDirectory()) {
    2888            if (directory.exists()) {
    2889                throw new IllegalArgumentException("Parameter '" + name + "' is not a directory: '" + directory + "'");
    2890            }
    2891            throw new FileNotFoundException("Directory '" + directory + "' does not exist.");
    2892        }
    2893    }
    2894
    2895    /**
    2896     * Requires that the given {@link File} is a directory if it exists.
    2897     *
    2898     * @param directory The {@link File} to check.
    2899     * @param name The parameter name to use in the exception message in case of null input.
    2900     * @throws NullPointerException if the given {@link File} is {@code null}.
    2901     * @throws IllegalArgumentException if the given {@link File} exists but is not a directory.
    2902     */
    2903    private static void requireDirectoryIfExists(final File directory, final String name) {
    2904        Objects.requireNonNull(directory, name);
    2905        if (directory.exists() && !directory.isDirectory()) {
    2906            throw new IllegalArgumentException("Parameter '" + name + "' is not a directory: '" + directory + "'");
    2907        }
    2908    }
    2909
    2910    /**
    2911     * Sets file lastModifiedTime, lastAccessTime and creationTime to match source file
    2912     *
    2913     * @param sourceFile The source file to query.
    2914     * @param targetFile The target file or directory to set.
    2915     * @return {@code true} if and only if the operation succeeded; {@code false} otherwise.
    2916     * @throws NullPointerException if sourceFile is {@code null}.
    2917     * @throws NullPointerException if targetFile is {@code null}.
    2918     */
    2919    private static boolean setTimes(final File sourceFile, final File targetFile) {
    2920        Objects.requireNonNull(sourceFile, "sourceFile");
    2921        Objects.requireNonNull(targetFile, "targetFile");
    2922        try {
    2923            // Set creation, modified, last accessed to match source file
    2924            final BasicFileAttributes srcAttr = Files.readAttributes(sourceFile.toPath(), BasicFileAttributes.class);
    2925            final BasicFileAttributeView destAttrView = Files.getFileAttributeView(targetFile.toPath(), BasicFileAttributeView.class);
    2926            // null guards are not needed; BasicFileAttributes.setTimes(...) is null safe
    2927            destAttrView.setTimes(srcAttr.lastModifiedTime(), srcAttr.lastAccessTime(), srcAttr.creationTime());
    2928            return true;
    2929        } catch (final IOException ignored) {
    2930            // Fallback: Only set modified time to match source file
    2931            return targetFile.setLastModified(sourceFile.lastModified());
    2932        }
    2933        // TODO: (Help!) Determine historically why setLastModified(File, File) needed PathUtils.setLastModifiedTime() if
    2934        // sourceFile.isFile() was true, but needed setLastModifiedTime(File, long) if sourceFile.isFile() was false
    2935    }
    2936
    2937    /**
    2938     * Returns the size of the specified file or directory. If the provided
    2939     * {@link File} is a regular file, then the file's length is returned.
    2940     * If the argument is a directory, then the size of the directory is
    2941     * calculated recursively. If a directory or subdirectory is security
    2942     * restricted, its size will not be included.
    2943     * <p>
    2944     * Note that overflow is not detected, and the return value may be negative if
    2945     * overflow occurs. See {@link #sizeOfAsBigInteger(File)} for an alternative
    2946     * method that does not overflow.
    2947     * </p>
    2948     *
    2949     * @param file the regular file or directory to return the size
    2950     *             of (must not be {@code null}).
    2951     *
    2952     * @return the length of the file, or recursive size of the directory,
    2953     * provided (in bytes).
    2954     *
    2955     * @throws NullPointerException     if the file is {@code null}.
    2956     * @throws IllegalArgumentException if the file does not exist.
    2957     * @throws UncheckedIOException if an IO error occurs.
    2958     * @since 2.0
    2959     */
    2960    public static long sizeOf(final File file) {
    2961        return Uncheck.getAsLong(() -> PathUtils.sizeOf(file.toPath()));
    2962    }
    2963
    2964    /**
    2965     * Returns the size of the specified file or directory. If the provided
    2966     * {@link File} is a regular file, then the file's length is returned.
    2967     * If the argument is a directory, then the size of the directory is
    2968     * calculated recursively. If a directory or subdirectory is security
    2969     * restricted, its size will not be included.
    2970     *
    2971     * @param file the regular file or directory to return the size
    2972     *             of (must not be {@code null}).
    2973     *
    2974     * @return the length of the file, or recursive size of the directory,
    2975     * provided (in bytes).
    2976     *
    2977     * @throws NullPointerException     if the file is {@code null}.
    2978     * @throws IllegalArgumentException if the file does not exist.
    2979     * @throws UncheckedIOException if an IO error occurs.
    2980     * @since 2.4
    2981     */
    2982    public static BigInteger sizeOfAsBigInteger(final File file) {
    2983        return Uncheck.get(() -> PathUtils.sizeOfAsBigInteger(file.toPath()));
    2984    }
    2985
    2986    /**
    2987     * Counts the size of a directory recursively (sum of the length of all files).
    2988     * <p>
    2989     * Note that overflow is not detected, and the return value may be negative if
    2990     * overflow occurs. See {@link #sizeOfDirectoryAsBigInteger(File)} for an alternative
    2991     * method that does not overflow.
    2992     * </p>
    2993     *
    2994     * @param directory directory to inspect, must not be {@code null}.
    2995     * @return size of directory in bytes, 0 if directory is security restricted, a negative number when the real total
    2996     * is greater than {@link Long#MAX_VALUE}.
    2997     * @throws IllegalArgumentException if the given {@link File} exists but is not a directory.
    2998     * @throws NullPointerException if the directory is {@code null}.
    2999     * @throws UncheckedIOException if an IO error occurs.
    3000     */
    3001    public static long sizeOfDirectory(final File directory) {
    3002        try {
    3003            requireDirectoryExists(directory, "directory");
    3004        } catch (final FileNotFoundException e) {
    3005            throw new UncheckedIOException(e);
    3006        }
    3007        return Uncheck.getAsLong(() -> PathUtils.sizeOfDirectory(directory.toPath()));
    3008    }
    3009
    3010    /**
    3011     * Counts the size of a directory recursively (sum of the length of all files).
    3012     *
    3013     * @param directory directory to inspect, must not be {@code null}.
    3014     * @return size of directory in bytes, 0 if directory is security restricted.
    3015     * @throws IllegalArgumentException if the given {@link File} exists but is not a directory.
    3016     * @throws NullPointerException if the directory is {@code null}.
    3017     * @throws UncheckedIOException if an IO error occurs.
    3018     * @since 2.4
    3019     */
    3020    public static BigInteger sizeOfDirectoryAsBigInteger(final File directory) {
    3021        try {
    3022            requireDirectoryExists(directory, "directory");
    3023        } catch (final FileNotFoundException e) {
    3024            throw new UncheckedIOException(e);
    3025        }
    3026        return Uncheck.get(() -> PathUtils.sizeOfDirectoryAsBigInteger(directory.toPath()));
    3027    }
    3028
    3029    /**
    3030     * Streams over the files in a given directory (and optionally its subdirectories) which match an array of extensions.
    3031     * <p>
    3032     * The returned {@link Stream} may wrap one or more {@link DirectoryStream}s. When you require timely disposal of file system resources, use a
    3033     * {@code try}-with-resources block to ensure invocation of the stream's {@link Stream#close()} method after the stream operations are completed. Calling a
    3034     * closed stream causes a {@link IllegalStateException}.
    3035     * </p>
    3036     *
    3037     * @param directory  The directory to search.
    3038     * @param recursive  if true all subdirectories are searched as well.
    3039     * @param extensions an array of extensions, for example, <code>{"java", "xml"}</code>. If this parameter is {@code null}, all files are returned.
    3040     * @return a Stream of {@link File} for matching files.
    3041     * @throws IOException if an I/O error is thrown when accessing the starting file.
    3042     * @since 2.9.0
    3043     */
    3044    public static Stream<File> streamFiles(final File directory, final boolean recursive, final String... extensions) throws IOException {
    3045        // @formatter:off
    3046        final IOFileFilter filter = extensions == null
    3047            ? FileFileFilter.INSTANCE
    3048            : FileFileFilter.INSTANCE.and(toSuffixFileFilter(extensions));
    3049        // @formatter:on
    3050        return PathUtils.walk(directory.toPath(), filter, toMaxDepth(recursive), false, FileVisitOption.FOLLOW_LINKS).map(Path::toFile);
    3051    }
    3052
    3053    /**
    3054     * Converts from a {@link URL} to a {@link File}.
    3055     * <p>
    3056     * Syntax such as {@code file:///my%20docs/file.txt} will be correctly decoded to {@code /my docs/file.txt}. UTF-8 is used to decode percent-encoded octets
    3057     * to characters. Additionally, malformed percent-encoded octets are handled leniently by passing them through literally.
    3058     * </p>
    3059     *
    3060     * @param url the file URL to convert, {@code null} returns {@code null}.
    3061     * @return the equivalent {@link File} object, or {@code null} if the URL's protocol is not {@code file}.
    3062     */
    3063    public static File toFile(final URL url) {
    3064        if (url == null || !isFileProtocol(url)) {
    3065            return null;
    3066        }
    3067        final String fileName = url.getFile().replace('/', File.separatorChar);
    3068        return new File(decodeUrl(fileName));
    3069    }
    3070
    3071    /**
    3072     * Converts each of an array of {@link URL} to a {@link File}.
    3073     * <p>
    3074     * Returns an array of the same size as the input. If the input is {@code null}, an empty array is returned. If the input contains {@code null}, the output
    3075     * array contains {@code null} at the same index.
    3076     * </p>
    3077     * <p>
    3078     * This method will decode the URL. Syntax such as {@code file:///my%20docs/file.txt} will be correctly decoded to {@code /my docs/file.txt}.
    3079     * </p>
    3080     *
    3081     * @param urls the file URLs to convert, {@code null} returns empty array.
    3082     * @return a non-{@code null} array of Files matching the input, with a {@code null} item if there was a {@code null} at that index in the input array.
    3083     * @throws IllegalArgumentException if any file is not a URL file.
    3084     * @throws IllegalArgumentException if any file is incorrectly encoded.
    3085     * @since 1.1
    3086     */
    3087    public static File[] toFiles(final URL... urls) {
    3088        if (IOUtils.length(urls) == 0) {
    3089            return EMPTY_FILE_ARRAY;
    3090        }
    3091        final File[] files = new File[urls.length];
    3092        for (int i = 0; i < urls.length; i++) {
    3093            final URL url = urls[i];
    3094            if (url != null) {
    3095                if (!isFileProtocol(url)) {
    3096                    throw new IllegalArgumentException("Can only convert file URL to a File: " + url);
    3097                }
    3098                files[i] = toFile(url);
    3099            }
    3100        }
    3101        return files;
    3102    }
    3103
    3104    /**
    3105     * Consumes all of the given stream.
    3106     * <p>
    3107     * When called from a FileTreeWalker, the walker <em>closes</em> the stream because {@link FileTreeWalker#next()} calls {@code top.stream().close()}.
    3108     * </p>
    3109     *
    3110     * @param stream The stream to consume.
    3111     * @return a new List.
    3112     */
    3113    private static List<File> toList(final Stream<File> stream) {
    3114        return stream.collect(Collectors.toList());
    3115    }
    3116
    3117    /**
    3118     * Converts whether or not to recurse into a recursion max depth.
    3119     *
    3120     * @param recursive whether or not to recurse.
    3121     * @return the recursion depth.
    3122     */
    3123    private static int toMaxDepth(final boolean recursive) {
    3124        return recursive ? Integer.MAX_VALUE : 1;
    3125    }
    3126
    3127    /**
    3128     * Converts an array of file extensions to suffixes.
    3129     *
    3130     * @param extensions an array of extensions, for example: {@code ["java", "xml"]}.
    3131     * @return an array of suffixes, for example: {@code [".java", ".xml"]}.
    3132     * @throws NullPointerException if the parameter is null.
    3133     */
    3134    private static String[] toSuffixes(final String... extensions) {
    3135        return Stream.of(Objects.requireNonNull(extensions, "extensions")).map(s -> s.charAt(0) == '.' ? s : "." + s).toArray(String[]::new);
    3136    }
    3137
    3138    private static SuffixFileFilter toSuffixFileFilter(final String... extensions) {
    3139        return new SuffixFileFilter(toSuffixes(extensions));
    3140    }
    3141
    3142    /**
    3143     * Implements behavior similar to the Unix "touch" utility. Creates a new file with size 0, or, if the file exists, just
    3144     * updates the file's modified time. This method throws an IOException if the last modified date
    3145     * of the file cannot be set. It creates parent directories if they do not exist.
    3146     *
    3147     * @param file the File to touch.
    3148     * @throws NullPointerException if the parameter is {@code null}.
    3149     * @throws IOException if setting the last-modified time failed or an I/O problem occurs.
    3150     */
    3151    public static void touch(final File file) throws IOException {
    3152        PathUtils.touch(Objects.requireNonNull(file, PROTOCOL_FILE).toPath());
    3153    }
    3154
    3155    /**
    3156     * Converts each element of an array of {@link File} to a {@link URL}.
    3157     * <p>
    3158     * Returns an array of the same size as the input.
    3159     * </p>
    3160     *
    3161     * @param files the files to convert, must not be {@code null}.
    3162     * @return an array of URLs matching the input.
    3163     * @throws IOException          if a file cannot be converted.
    3164     * @throws NullPointerException if any argument is null.
    3165     */
    3166    public static URL[] toURLs(final File... files) throws IOException {
    3167        Objects.requireNonNull(files, "files");
    3168        final URL[] urls = new URL[files.length];
    3169        for (int i = 0; i < urls.length; i++) {
    3170            urls[i] = files[i].toURI().toURL();
    3171        }
    3172        return urls;
    3173    }
    3174
    3175    /**
    3176     * Validates the given arguments.
    3177     * <ul>
    3178     * <li>Throws {@link NullPointerException} if {@code source} is null</li>
    3179     * <li>Throws {@link NullPointerException} if {@code destination} is null</li>
    3180     * <li>Throws {@link FileNotFoundException} if {@code source} does not exist</li>
    3181     * </ul>
    3182     *
    3183     * @param source      the file or directory to be moved.
    3184     * @param destination the destination file or directory.
    3185     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
    3186     * @throws FileNotFoundException if the source file does not exist.
    3187     */
    3188    private static void validateMoveParameters(final File source, final File destination) throws FileNotFoundException {
    3189        Objects.requireNonNull(source, "source");
    3190        Objects.requireNonNull(destination, "destination");
    3191        if (!source.exists()) {
    3192            throw new FileNotFoundException("Source '" + source + "' does not exist");
    3193        }
    3194    }
    3195
    3196    /**
    3197     * Waits for the file system to detect a file's presence, with a timeout.
    3198     * <p>
    3199     * This method repeatedly tests {@link Files#exists(Path, LinkOption...)} until it returns
    3200     * true up to the maximum time specified in seconds.
    3201     * </p>
    3202     *
    3203     * @param file    the file to check, must not be {@code null}.
    3204     * @param seconds the maximum time in seconds to wait.
    3205     * @return true if file exists.
    3206     * @throws NullPointerException if the file is {@code null}.
    3207     */
    3208    public static boolean waitFor(final File file, final int seconds) {
    3209        Objects.requireNonNull(file, PROTOCOL_FILE);
    3210        return PathUtils.waitFor(file.toPath(), Duration.ofSeconds(seconds), PathUtils.EMPTY_LINK_OPTION_ARRAY);
    3211    }
    3212
    3213    /**
    3214     * Writes a CharSequence to a file creating the file if it does not exist using the virtual machine's {@linkplain Charset#defaultCharset() default charset}.
    3215     *
    3216     * @param file the file to write.
    3217     * @param data the content to write to the file.
    3218     * @throws IOException in case of an I/O error.
    3219     * @since 2.0
    3220     * @deprecated Use {@link #write(File, CharSequence, Charset)} instead (and specify the appropriate encoding).
    3221     */
    3222    @Deprecated
    3223    public static void write(final File file, final CharSequence data) throws IOException {
    3224        write(file, data, Charset.defaultCharset(), false);
    3225    }
    3226
    3227    /**
    3228     * Writes a CharSequence to a file creating the file if it does not exist using the virtual machine's {@linkplain Charset#defaultCharset() default charset}.
    3229     *
    3230     * @param file   the file to write.
    3231     * @param data   the content to write to the file.
    3232     * @param append if {@code true}, then the data will be added to the end of the file rather than overwriting.
    3233     * @throws IOException in case of an I/O error.
    3234     * @since 2.1
    3235     * @deprecated Use {@link #write(File, CharSequence, Charset, boolean)} instead (and specify the appropriate encoding).
    3236     */
    3237    @Deprecated
    3238    public static void write(final File file, final CharSequence data, final boolean append) throws IOException {
    3239        write(file, data, Charset.defaultCharset(), append);
    3240    }
    3241
    3242    /**
    3243     * Writes a CharSequence to a file creating the file if it does not exist.
    3244     *
    3245     * @param file     the file to write.
    3246     * @param data     the content to write to the file.
    3247     * @param charset the name of the requested charset, {@code null} means platform default.
    3248     * @throws IOException in case of an I/O error.
    3249     * @since 2.3
    3250     */
    3251    public static void write(final File file, final CharSequence data, final Charset charset) throws IOException {
    3252        write(file, data, charset, false);
    3253    }
    3254
    3255    /**
    3256     * Writes a CharSequence to a file creating the file if it does not exist.
    3257     *
    3258     * @param file     the file to write.
    3259     * @param data     the content to write to the file.
    3260     * @param charset the charset to use, {@code null} means platform default.
    3261     * @param append   if {@code true}, then the data will be added to the.
    3262     *                 end of the file rather than overwriting.
    3263     * @throws IOException in case of an I/O error.
    3264     * @since 2.3
    3265     */
    3266    public static void write(final File file, final CharSequence data, final Charset charset, final boolean append) throws IOException {
    3267        writeStringToFile(file, Objects.toString(data, null), charset, append);
    3268    }
    3269
    3270    /**
    3271     * Writes a CharSequence to a file creating the file if it does not exist.
    3272     *
    3273     * @param file     the file to write.
    3274     * @param data     the content to write to the file.
    3275     * @param charsetName the name of the requested charset, {@code null} means platform default.
    3276     * @throws IOException                          in case of an I/O error.
    3277     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM.
    3278     * @since 2.0
    3279     */
    3280    public static void write(final File file, final CharSequence data, final String charsetName) throws IOException {
    3281        write(file, data, charsetName, false);
    3282    }
    3283
    3284    /**
    3285     * Writes a CharSequence to a file creating the file if it does not exist.
    3286     *
    3287     * @param file     the file to write.
    3288     * @param data     the content to write to the file.
    3289     * @param charsetName the name of the requested charset, {@code null} means platform default.
    3290     * @param append   if {@code true}, then the data will be added to the
    3291     *                 end of the file rather than overwriting.
    3292     * @throws IOException                 in case of an I/O error.
    3293     * @throws java.nio.charset.UnsupportedCharsetException if the encoding is not supported by the VM.
    3294     * @since 2.1
    3295     */
    3296    public static void write(final File file, final CharSequence data, final String charsetName, final boolean append) throws IOException {
    3297        write(file, data, Charsets.toCharset(charsetName), append);
    3298    }
    3299
    3300    // Must be called with a directory
    3301
    3302    /**
    3303     * Writes a byte array to a file creating the file if it does not exist.
    3304     * The parent directories of the file will be created if they do not exist.
    3305     *
    3306     * @param file the file to write to.
    3307     * @param data the content to write to the file.
    3308     * @throws IOException in case of an I/O error.
    3309     * @since 1.1
    3310     */
    3311    public static void writeByteArrayToFile(final File file, final byte[] data) throws IOException {
    3312        writeByteArrayToFile(file, data, false);
    3313    }
    3314
    3315    /**
    3316     * Writes a byte array to a file creating the file if it does not exist.
    3317     *
    3318     * @param file   the file to write to.
    3319     * @param data   the content to write to the file.
    3320     * @param append if {@code true}, then bytes will be added to the
    3321     *               end of the file rather than overwriting.
    3322     * @throws IOException in case of an I/O error.
    3323     * @since 2.1
    3324     */
    3325    public static void writeByteArrayToFile(final File file, final byte[] data, final boolean append) throws IOException {
    3326        writeByteArrayToFile(file, data, 0, data.length, append);
    3327    }
    3328
    3329    /**
    3330     * Writes {@code len} bytes from the specified byte array starting
    3331     * at offset {@code off} to a file, creating the file if it does
    3332     * not exist.
    3333     *
    3334     * @param file the file to write to.
    3335     * @param data the content to write to the file.
    3336     * @param off  the start offset in the data.
    3337     * @param len  the number of bytes to write.
    3338     * @throws IOException in case of an I/O error.
    3339     * @since 2.5
    3340     */
    3341    public static void writeByteArrayToFile(final File file, final byte[] data, final int off, final int len) throws IOException {
    3342        writeByteArrayToFile(file, data, off, len, false);
    3343    }
    3344
    3345    /**
    3346     * Writes {@code len} bytes from the specified byte array starting
    3347     * at offset {@code off} to a file, creating the file if it does
    3348     * not exist.
    3349     *
    3350     * @param file   the file to write to.
    3351     * @param data   the content to write to the file.
    3352     * @param off    the start offset in the data.
    3353     * @param len    the number of bytes to write.
    3354     * @param append if {@code true}, then bytes will be added to the
    3355     *               end of the file rather than overwriting.
    3356     * @throws IOException in case of an I/O error.
    3357     * @since 2.5
    3358     */
    3359    public static void writeByteArrayToFile(final File file, final byte[] data, final int off, final int len, final boolean append) throws IOException {
    3360        try (OutputStream out = newOutputStream(file, append)) {
    3361            out.write(data, off, len);
    3362        }
    3363    }
    3364
    3365    /**
    3366     * Writes the {@code toString()} value of each item in a collection to
    3367     * the specified {@link File} line by line.
    3368     * The default VM encoding and the default line ending will be used.
    3369     *
    3370     * @param file  the file to write to.
    3371     * @param lines the lines to write, {@code null} entries produce blank lines.
    3372     * @throws IOException in case of an I/O error.
    3373     * @since 1.3
    3374     */
    3375    public static void writeLines(final File file, final Collection<?> lines) throws IOException {
    3376        writeLines(file, null, lines, null, false);
    3377    }
    3378
    3379    /**
    3380     * Writes the {@code toString()} value of each item in a collection to
    3381     * the specified {@link File} line by line.
    3382     * The default VM encoding and the default line ending will be used.
    3383     *
    3384     * @param file   the file to write to.
    3385     * @param lines  the lines to write, {@code null} entries produce blank lines.
    3386     * @param append if {@code true}, then the lines will be added to the
    3387     *               end of the file rather than overwriting.
    3388     * @throws IOException in case of an I/O error.
    3389     * @since 2.1
    3390     */
    3391    public static void writeLines(final File file, final Collection<?> lines, final boolean append) throws IOException {
    3392        writeLines(file, null, lines, null, append);
    3393    }
    3394
    3395    /**
    3396     * Writes the {@code toString()} value of each item in a collection to
    3397     * the specified {@link File} line by line.
    3398     * The default VM encoding and the specified line ending will be used.
    3399     *
    3400     * @param file       the file to write to.
    3401     * @param lines      the lines to write, {@code null} entries produce blank lines.
    3402     * @param lineEnding the line separator to use, {@code null} is system default.
    3403     * @throws IOException in case of an I/O error.
    3404     * @since 1.3
    3405     */
    3406    public static void writeLines(final File file, final Collection<?> lines, final String lineEnding) throws IOException {
    3407        writeLines(file, null, lines, lineEnding, false);
    3408    }
    3409
    3410    /**
    3411     * Writes the {@code toString()} value of each item in a collection to
    3412     * the specified {@link File} line by line.
    3413     * The default VM encoding and the specified line ending will be used.
    3414     *
    3415     * @param file       the file to write to.
    3416     * @param lines      the lines to write, {@code null} entries produce blank lines.
    3417     * @param lineEnding the line separator to use, {@code null} is system default.
    3418     * @param append     if {@code true}, then the lines will be added to the
    3419     *                   end of the file rather than overwriting.
    3420     * @throws IOException in case of an I/O error.
    3421     * @since 2.1
    3422     */
    3423    public static void writeLines(final File file, final Collection<?> lines, final String lineEnding, final boolean append) throws IOException {
    3424        writeLines(file, null, lines, lineEnding, append);
    3425    }
    3426
    3427    /**
    3428     * Writes the {@code toString()} value of each item in a collection to
    3429     * the specified {@link File} line by line.
    3430     * The specified character encoding and the default line ending will be used.
    3431     * The parent directories of the file will be created if they do not exist.
    3432     *
    3433     * @param file     the file to write to.
    3434     * @param charsetName the name of the requested charset, {@code null} means platform default.
    3435     * @param lines    the lines to write, {@code null} entries produce blank lines.
    3436     * @throws IOException                          in case of an I/O error.
    3437     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM.
    3438     * @since 1.1
    3439     */
    3440    public static void writeLines(final File file, final String charsetName, final Collection<?> lines) throws IOException {
    3441        writeLines(file, charsetName, lines, null, false);
    3442    }
    3443
    3444    /**
    3445     * Writes the {@code toString()} value of each item in a collection to
    3446     * the specified {@link File} line by line, optionally appending.
    3447     * The specified character encoding and the default line ending will be used.
    3448     *
    3449     * @param file     the file to write to.
    3450     * @param charsetName the name of the requested charset, {@code null} means platform default.
    3451     * @param lines    the lines to write, {@code null} entries produce blank lines.
    3452     * @param append   if {@code true}, then the lines will be added to the
    3453     *                 end of the file rather than overwriting.
    3454     * @throws IOException                          in case of an I/O error.
    3455     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM.
    3456     * @since 2.1
    3457     */
    3458    public static void writeLines(final File file, final String charsetName, final Collection<?> lines, final boolean append) throws IOException {
    3459        writeLines(file, charsetName, lines, null, append);
    3460    }
    3461
    3462    /**
    3463     * Writes the {@code toString()} value of each item in a collection to
    3464     * the specified {@link File} line by line.
    3465     * The specified character encoding and the line ending will be used.
    3466     * The parent directories of the file will be created if they do not exist.
    3467     *
    3468     * @param file       the file to write to.
    3469     * @param charsetName   the name of the requested charset, {@code null} means platform default.
    3470     * @param lines      the lines to write, {@code null} entries produce blank lines.
    3471     * @param lineEnding the line separator to use, {@code null} is system default.
    3472     * @throws IOException                          in case of an I/O error.
    3473     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM.
    3474     * @since 1.1
    3475     */
    3476    public static void writeLines(final File file, final String charsetName, final Collection<?> lines, final String lineEnding) throws IOException {
    3477        writeLines(file, charsetName, lines, lineEnding, false);
    3478    }
    3479
    3480    /**
    3481     * Writes the {@code toString()} value of each item in a collection to
    3482     * the specified {@link File} line by line.
    3483     * The specified character encoding and the line ending will be used.
    3484     *
    3485     * @param file       the file to write to.
    3486     * @param charsetName   the name of the requested charset, {@code null} means platform default.
    3487     * @param lines      the lines to write, {@code null} entries produce blank lines.
    3488     * @param lineEnding the line separator to use, {@code null} is system default.
    3489     * @param append     if {@code true}, then the lines will be added to the
    3490     *                   end of the file rather than overwriting.
    3491     * @throws IOException                          in case of an I/O error.
    3492     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM.
    3493     * @since 2.1
    3494     */
    3495    public static void writeLines(final File file, final String charsetName, final Collection<?> lines, final String lineEnding, final boolean append)
    3496        throws IOException {
    3497        try (OutputStream out = new BufferedOutputStream(newOutputStream(file, append))) {
    3498            IOUtils.writeLines(lines, lineEnding, out, charsetName);
    3499        }
    3500    }
    3501
    3502    /**
    3503     * Writes a String to a file creating the file if it does not exist using the virtual machine's {@linkplain Charset#defaultCharset() default charset}.
    3504     *
    3505     * @param file the file to write.
    3506     * @param data the content to write to the file.
    3507     * @throws IOException in case of an I/O error.
    3508     * @deprecated Use {@link #writeStringToFile(File, String, Charset)} instead (and specify the appropriate encoding).
    3509     */
    3510    @Deprecated
    3511    public static void writeStringToFile(final File file, final String data) throws IOException {
    3512        writeStringToFile(file, data, Charset.defaultCharset(), false);
    3513    }
    3514
    3515    /**
    3516     * Writes a String to a file creating the file if it does not exist using the virtual machine's {@linkplain Charset#defaultCharset() default charset}.
    3517     *
    3518     * @param file   the file to write.
    3519     * @param data   the content to write to the file.
    3520     * @param append if {@code true}, then the String will be added to the end of the file rather than overwriting.
    3521     * @throws IOException in case of an I/O error.
    3522     * @since 2.1
    3523     * @deprecated Use {@link #writeStringToFile(File, String, Charset, boolean)} instead (and specify the appropriate encoding).
    3524     */
    3525    @Deprecated
    3526    public static void writeStringToFile(final File file, final String data, final boolean append) throws IOException {
    3527        writeStringToFile(file, data, Charset.defaultCharset(), append);
    3528    }
    3529
    3530    /**
    3531     * Writes a String to a file creating the file if it does not exist.
    3532     * The parent directories of the file will be created if they do not exist.
    3533     *
    3534     * @param file     the file to write.
    3535     * @param data     the content to write to the file.
    3536     * @param charset the charset to use, {@code null} means platform default.
    3537     * @throws IOException                          in case of an I/O error.
    3538     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM.
    3539     * @since 2.4
    3540     */
    3541    public static void writeStringToFile(final File file, final String data, final Charset charset) throws IOException {
    3542        writeStringToFile(file, data, charset, false);
    3543    }
    3544
    3545    /**
    3546     * Writes a String to a file, creating the file if it does not exist.
    3547     * The parent directories of the file are created if they do not exist.
    3548     *
    3549     * @param file     the file to write.
    3550     * @param data     the content to write to the file.
    3551     * @param charset the charset to use, {@code null} means platform default.
    3552     * @param append   if {@code true}, then the String will be added to the
    3553     *                 end of the file rather than overwriting.
    3554     * @throws IOException in case of an I/O error.
    3555     * @since 2.3
    3556     */
    3557    public static void writeStringToFile(final File file, final String data, final Charset charset, final boolean append) throws IOException {
    3558        try (OutputStream out = newOutputStream(file, append)) {
    3559            IOUtils.write(data, out, charset);
    3560        }
    3561    }
    3562
    3563    /**
    3564     * Writes a String to a file, creating the file if it does not exist.
    3565     * The parent directories of the file are created if they do not exist.
    3566     *
    3567     * @param file     the file to write.
    3568     * @param data     the content to write to the file.
    3569     * @param charsetName the name of the requested charset, {@code null} means platform default.
    3570     * @throws IOException                          in case of an I/O error.
    3571     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM.
    3572     */
    3573    public static void writeStringToFile(final File file, final String data, final String charsetName) throws IOException {
    3574        writeStringToFile(file, data, charsetName, false);
    3575    }
    3576
    3577    /**
    3578     * Writes a String to a file, creating the file if it does not exist.
    3579     * The parent directories of the file are created if they do not exist.
    3580     *
    3581     * @param file     the file to write.
    3582     * @param data     the content to write to the file.
    3583     * @param charsetName the name of the requested charset, {@code null} means platform default.
    3584     * @param append   if {@code true}, then the String will be added to the
    3585     *                 end of the file rather than overwriting.
    3586     * @throws IOException                 in case of an I/O error.
    3587     * @throws java.nio.charset.UnsupportedCharsetException if the encoding is not supported by the VM.
    3588     * @since 2.1
    3589     */
    3590    public static void writeStringToFile(final File file, final String data, final String charsetName, final boolean append) throws IOException {
    3591        writeStringToFile(file, data, Charsets.toCharset(charsetName), append);
    3592    }
    3593
    3594    /**
    3595     * Instances should NOT be constructed in standard programming.
    3596     *
    3597     * @deprecated TODO Make private in 3.0.
    3598     */
    3599    @Deprecated
    3600    public FileUtils() { //NOSONAR
    3601        // empty
    3602    }
    3603
    3604}