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