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