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