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