    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        Collections.addAll(optionSet, options);
    2241        Files.walkFileTree(directory.toPath(), optionSet, toMaxDepth(isDirFilterSet), visitor);
    2242        return visitor;
    2243    }
    2244
    2245    /**
    2246     * Lists files in a directory, asserting that the supplied directory exists and is a directory.
    2247     *
    2248     * @param directory The directory to list
    2249     * @param fileFilter Optional file filter, may be null.
    2250     * @return The files in the directory, never {@code null}.
    2251     * @throws NullPointerException if directory is {@code null}.
    2252     * @throws IllegalArgumentException if {@link directory} exists but is not a directory
    2253     * @throws IOException if an I/O error occurs.
    2254     */
    2255    private static File[] listFiles(final File directory, final FileFilter fileFilter) throws IOException {
    2256        requireDirectoryExists(directory, "directory");
    2257        final File[] files = fileFilter == null ? directory.listFiles() : directory.listFiles(fileFilter);
    2258        if (files == null) {
    2259            // null if the directory does not denote a directory, or if an I/O error occurs.
    2260            throw new IOException("Unknown I/O error listing contents of directory: " + directory);
    2261        }
    2262        return files;
    2263    }
    2264
    2265    /**
    2266     * Finds files within a given directory (and optionally its
    2267     * subdirectories). All files found are filtered by an IOFileFilter.
    2268     * <p>
    2269     * If your search should recurse into subdirectories you can pass in
    2270     * an IOFileFilter for directories. You don't need to bind a
    2271     * DirectoryFileFilter (via logical AND) to this filter. This method does
    2272     * that for you.
    2273     * </p>
    2274     * <p>
    2275     * An example: If you want to search through all directories called
    2276     * "temp" you pass in {@code FileFilterUtils.NameFileFilter("temp")}
    2277     * </p>
    2278     * <p>
    2279     * Another common usage of this method is find files in a directory
    2280     * tree but ignoring the directories generated CVS. You can simply pass
    2281     * in {@code FileFilterUtils.makeCVSAware(null)}.
    2282     * </p>
    2283     *
    2284     * @param directory  the directory to search in
    2285     * @param fileFilter filter to apply when finding files. Must not be {@code null},
    2286     *                   use {@link TrueFileFilter#INSTANCE} to match all files in selected directories.
    2287     * @param dirFilter  optional filter to apply when finding subdirectories.
    2288     *                   If this parameter is {@code null}, subdirectories will not be included in the
    2289     *                   search. Use {@link TrueFileFilter#INSTANCE} to match all directories.
    2290     * @return a collection of {@link File} with the matching files
    2291     * @see org.apache.commons.io.filefilter.FileFilterUtils
    2292     * @see org.apache.commons.io.filefilter.NameFileFilter
    2293     */
    2294    public static Collection<File> listFiles(final File directory, final IOFileFilter fileFilter, final IOFileFilter dirFilter) {
    2295        final AccumulatorPathVisitor visitor = Uncheck
    2296            .apply(d -> listAccumulate(d, FileFileFilter.INSTANCE.and(fileFilter), dirFilter, FileVisitOption.FOLLOW_LINKS), directory);
    2297        return toList(visitor.getFileList().stream().map(Path::toFile));
    2298    }
    2299
    2300    /**
    2301     * Lists files within a given directory (and optionally its subdirectories)
    2302     * which match an array of extensions.
    2303     *
    2304     * @param directory  the directory to search in
    2305     * @param extensions an array of extensions, for example, {"java","xml"}. If this
    2306     *                   parameter is {@code null}, all files are returned.
    2307     * @param recursive  if true all subdirectories are searched as well
    2308     * @return a collection of {@link File} with the matching files
    2309     */
    2310    public static Collection<File> listFiles(final File directory, final String[] extensions, final boolean recursive) {
    2311        try (Stream<File> fileStream = Uncheck.get(() -> streamFiles(directory, recursive, extensions))) {
    2312            return toList(fileStream);
    2313        }
    2314    }
    2315
    2316    /**
    2317     * Finds files within a given directory (and optionally its
    2318     * subdirectories). All files found are filtered by an IOFileFilter.
    2319     * <p>
    2320     * The resulting collection includes the starting directory and
    2321     * any subdirectories that match the directory filter.
    2322     * </p>
    2323     *
    2324     * @param directory  the directory to search in
    2325     * @param fileFilter filter to apply when finding files.
    2326     * @param dirFilter  optional filter to apply when finding subdirectories.
    2327     *                   If this parameter is {@code null}, subdirectories will not be included in the
    2328     *                   search. Use TrueFileFilter.INSTANCE to match all directories.
    2329     * @return a collection of {@link File} with the matching files
    2330     * @see org.apache.commons.io.FileUtils#listFiles
    2331     * @see org.apache.commons.io.filefilter.FileFilterUtils
    2332     * @see org.apache.commons.io.filefilter.NameFileFilter
    2333     * @since 2.2
    2334     */
    2335    public static Collection<File> listFilesAndDirs(final File directory, final IOFileFilter fileFilter, final IOFileFilter dirFilter) {
    2336        final AccumulatorPathVisitor visitor = Uncheck.apply(d -> listAccumulate(d, fileFilter, dirFilter, FileVisitOption.FOLLOW_LINKS),
    2337            directory);
    2338        final List<Path> list = visitor.getFileList();
    2339        list.addAll(visitor.getDirList());
    2340        return toList(list.stream().map(Path::toFile));
    2341    }
    2342
    2343    /**
    2344     * Calls {@link File#mkdirs()} and throws an {@link IOException} on failure.
    2345     * <p>
    2346     * Creates all directories for a File object, including any necessary but non-existent parent directories. If the {@code directory} already exists or is
    2347     * null, nothing happens.
    2348     * </p>
    2349     *
    2350     * @param directory the receiver for {@code mkdirs()}. If the {@code directory} already exists or is null, nothing happens.
    2351     * @return the given directory.
    2352     * @throws IOException       if the directory was not created along with all its parent directories.
    2353     * @throws IOException       if the given file object is not a directory.
    2354     * @throws SecurityException See {@link File#mkdirs()}.
    2355     * @see File#mkdirs()
    2356     */
    2357    private static File mkdirs(final File directory) throws IOException {
    2358        if (directory != null && !directory.mkdirs() && !directory.isDirectory()) {
    2359            throw new IOException("Cannot create directory '" + directory + "'.");
    2360        }
    2361        return directory;
    2362    }
    2363
    2364    /**
    2365     * Moves a directory.
    2366     * <p>
    2367     * When the destination directory is on another file system, do a "copy and delete".
    2368     * </p>
    2369     *
    2370     * @param srcDir the directory to be moved.
    2371     * @param destDir the destination directory.
    2372     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
    2373     * @throws IllegalArgumentException if {@code srcDir} exists but is not a directory
    2374     * @throws FileNotFoundException if the source does not exist.
    2375     * @throws IOException if an error occurs or setting the last-modified time didn't succeed.
    2376     * @since 1.4
    2377     */
    2378    public static void moveDirectory(final File srcDir, final File destDir) throws IOException {
    2379        Objects.requireNonNull(destDir, "destination");
    2380        requireDirectoryExists(srcDir, "srcDir");
    2381        requireAbsent(destDir, "destDir");
    2382        if (!srcDir.renameTo(destDir)) {
    2383            if (destDir.getCanonicalPath().startsWith(srcDir.getCanonicalPath() + File.separator)) {
    2384                throw new IOException("Cannot move directory: " + srcDir + " to a subdirectory of itself: " + destDir);
    2385            }
    2386            copyDirectory(srcDir, destDir);
    2387            deleteDirectory(srcDir);
    2388            if (srcDir.exists()) {
    2389                throw new IOException("Failed to delete original directory '" + srcDir +
    2390                        "' after copy to '" + destDir + "'");
    2391            }
    2392        }
    2393    }
    2394
    2395    /**
    2396     * Moves a directory to another directory.
    2397     * <p>
    2398     * If {@code createDestDir} is true, creates all destination parent directories, including any necessary but non-existent parent directories.
    2399     * </p>
    2400     *
    2401     * @param source the directory to be moved.
    2402     * @param destDir the destination file.
    2403     * @param createDestDir If {@code true} create the destination directory, otherwise if {@code false} throw an
    2404     *        IOException.
    2405     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
    2406     * @throws IllegalArgumentException if the source or destination is invalid.
    2407     * @throws FileNotFoundException if the source does not exist.
    2408     * @throws IOException if the directory was not created along with all its parent directories, if enabled.
    2409     * @throws IOException if an error occurs or setting the last-modified time didn't succeed.
    2410     * @throws SecurityException See {@link File#mkdirs()}.
    2411     * @since 1.4
    2412     */
    2413    public static void moveDirectoryToDirectory(final File source, final File destDir, final boolean createDestDir) throws IOException {
    2414        validateMoveParameters(source, destDir);
    2415        if (!destDir.isDirectory()) {
    2416            if (destDir.exists()) {
    2417                throw new IOException("Destination '" + destDir + "' is not a directory");
    2418            }
    2419            if (!createDestDir) {
    2420                throw new FileNotFoundException("Destination directory '" + destDir + "' does not exist [createDestDir=" + false + "]");
    2421            }
    2422            mkdirs(destDir);
    2423        }
    2424        moveDirectory(source, new File(destDir, source.getName()));
    2425    }
    2426
    2427    /**
    2428     * Moves a file preserving attributes.
    2429     * <p>
    2430     * Shorthand for {@code moveFile(srcFile, destFile, StandardCopyOption.COPY_ATTRIBUTES)}.
    2431     * </p>
    2432     * <p>
    2433     * When the destination file is on another file system, do a "copy and delete".
    2434     * </p>
    2435     *
    2436     * @param srcFile the file to be moved.
    2437     * @param destFile the destination file.
    2438     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
    2439     * @throws FileExistsException if the destination file exists.
    2440     * @throws FileNotFoundException if the source file does not exist.
    2441     * @throws IllegalArgumentException if {@code srcFile} is a directory
    2442     * @throws IOException if an error occurs.
    2443     * @since 1.4
    2444     */
    2445    public static void moveFile(final File srcFile, final File destFile) throws IOException {
    2446        moveFile(srcFile, destFile, StandardCopyOption.COPY_ATTRIBUTES);
    2447    }
    2448
    2449    /**
    2450     * Moves a file.
    2451     * <p>
    2452     * When the destination file is on another file system, do a "copy and delete".
    2453     * </p>
    2454     *
    2455     * @param srcFile the file to be moved.
    2456     * @param destFile the destination file.
    2457     * @param copyOptions Copy options.
    2458     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
    2459     * @throws FileExistsException if the destination file exists.
    2460     * @throws FileNotFoundException if the source file does not exist.
    2461     * @throws IllegalArgumentException if {@code srcFile} is a directory
    2462     * @throws IOException if an error occurs or setting the last-modified time didn't succeed.
    2463     * @since 2.9.0
    2464     */
    2465    public static void moveFile(final File srcFile, final File destFile, final CopyOption... copyOptions) throws IOException {
    2466        Objects.requireNonNull(destFile, "destination");
    2467        checkFileExists(srcFile, "srcFile");
    2468        requireAbsent(destFile, "destFile");
    2469        final boolean rename = srcFile.renameTo(destFile);
    2470        if (!rename) {
    2471            // Don't interfere with file date on move, handled by StandardCopyOption.COPY_ATTRIBUTES
    2472            copyFile(srcFile, destFile, false, copyOptions);
    2473            if (!srcFile.delete()) {
    2474                FileUtils.deleteQuietly(destFile);
    2475                throw new IOException("Failed to delete original file '" + srcFile + "' after copy to '" + destFile + "'");
    2476            }
    2477        }
    2478    }
    2479
    2480    /**
    2481     * Moves a file into a directory.
    2482     * <p>
    2483     * If {@code createDestDir} is true, creates all destination parent directories, including any necessary but non-existent parent directories.
    2484     * </p>
    2485     *
    2486     * @param srcFile the file to be moved.
    2487     * @param destDir the directory to move the file into
    2488     * @param createDestDir if {@code true} create the destination directory. If {@code false} throw an
    2489     *        IOException if the destination directory does not already exist.
    2490     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
    2491     * @throws FileExistsException if the destination file exists.
    2492     * @throws FileNotFoundException if the source file does not exist.
    2493     * @throws IOException if source or destination is invalid.
    2494     * @throws IOException if the directory was not created along with all its parent directories, if enabled.
    2495     * @throws IOException if an error occurs or setting the last-modified time didn't succeed.
    2496     * @throws SecurityException See {@link File#mkdirs()}.
    2497     * @throws IllegalArgumentException if {@code destDir} exists but is not a directory
    2498     * @since 1.4
    2499     */
    2500    public static void moveFileToDirectory(final File srcFile, final File destDir, final boolean createDestDir) throws IOException {
    2501        validateMoveParameters(srcFile, destDir);
    2502        if (!destDir.exists() && createDestDir) {
    2503            mkdirs(destDir);
    2504        }
    2505        requireDirectoryExists(destDir, "destDir");
    2506        moveFile(srcFile, new File(destDir, srcFile.getName()));
    2507    }
    2508
    2509    /**
    2510     * Moves a file or directory into a destination directory.
    2511     * <p>
    2512     * If {@code createDestDir} is true, creates all destination parent directories, including any necessary but non-existent parent directories.
    2513     * </p>
    2514     * <p>
    2515     * When the destination is on another file system, do a "copy and delete".
    2516     * </p>
    2517     *
    2518     * @param src           the file or directory to be moved.
    2519     * @param destDir       the destination directory.
    2520     * @param createDestDir if {@code true} create the destination directory. If {@code false} throw an
    2521     *        IOException if the destination directory does not already exist.
    2522     * @throws NullPointerException  if any of the given {@link File}s are {@code null}.
    2523     * @throws FileExistsException   if the directory or file exists in the destination directory.
    2524     * @throws FileNotFoundException if the source file does not exist.
    2525     * @throws IOException           if source or destination is invalid.
    2526     * @throws IOException           if an error occurs or setting the last-modified time didn't succeed.
    2527     * @since 1.4
    2528     */
    2529    public static void moveToDirectory(final File src, final File destDir, final boolean createDestDir) throws IOException {
    2530        validateMoveParameters(src, destDir);
    2531        if (src.isDirectory()) {
    2532            moveDirectoryToDirectory(src, destDir, createDestDir);
    2533        } else {
    2534            moveFileToDirectory(src, destDir, createDestDir);
    2535        }
    2536    }
    2537
    2538    /**
    2539     * Creates a new OutputStream by opening or creating a file, returning an output stream that may be used to write bytes
    2540     * to the file.
    2541     *
    2542     * @param append Whether or not to append.
    2543     * @param file the File.
    2544     * @return a new OutputStream.
    2545     * @throws IOException if an I/O error occurs.
    2546     * @see PathUtils#newOutputStream(Path, boolean)
    2547     * @since 2.12.0
    2548     */
    2549    public static OutputStream newOutputStream(final File file, final boolean append) throws IOException {
    2550        return PathUtils.newOutputStream(Objects.requireNonNull(file, "file").toPath(), append);
    2551    }
    2552
    2553    /**
    2554     * Opens a {@link FileInputStream} for the specified file, providing better error messages than simply calling
    2555     * {@code new FileInputStream(file)}.
    2556     * <p>
    2557     * At the end of the method either the stream will be successfully opened, or an exception will have been thrown.
    2558     * </p>
    2559     * <p>
    2560     * An exception is thrown if the file does not exist. An exception is thrown if the file object exists but is a
    2561     * directory. An exception is thrown if the file exists but cannot be read.
    2562     * </p>
    2563     *
    2564     * @param file the file to open for input, must not be {@code null}
    2565     * @return a new {@link FileInputStream} for the specified file
    2566     * @throws NullPointerException if file is {@code null}.
    2567     * @throws FileNotFoundException if the file does not exist, is a directory rather than a regular file, or for some
    2568     *         other reason cannot be opened for reading.
    2569     * @throws IOException See FileNotFoundException above, FileNotFoundException is a subclass of IOException.
    2570     * @since 1.3
    2571     */
    2572    public static FileInputStream openInputStream(final File file) throws IOException {
    2573        Objects.requireNonNull(file, "file");
    2574        return new FileInputStream(file);
    2575    }
    2576
    2577    /**
    2578     * Opens a {@link FileOutputStream} for the specified file, checking and
    2579     * creating the parent directory if it does not exist.
    2580     * <p>
    2581     * At the end of the method either the stream will be successfully opened,
    2582     * or an exception will have been thrown.
    2583     * </p>
    2584     * <p>
    2585     * The parent directory will be created if it does not exist.
    2586     * The file will be created if it does not exist.
    2587     * An exception is thrown if the file object exists but is a directory.
    2588     * An exception is thrown if the file exists but cannot be written to.
    2589     * An exception is thrown if the parent directory cannot be created.
    2590     * </p>
    2591     *
    2592     * @param file the file to open for output, must not be {@code null}
    2593     * @return a new {@link FileOutputStream} for the specified file
    2594     * @throws NullPointerException if the file object is {@code null}.
    2595     * @throws IllegalArgumentException if the file object is a directory
    2596     * @throws IllegalArgumentException if the file is not writable.
    2597     * @throws IOException if the directories could not be created.
    2598     * @since 1.3
    2599     */
    2600    public static FileOutputStream openOutputStream(final File file) throws IOException {
    2601        return openOutputStream(file, false);
    2602    }
    2603
    2604    /**
    2605     * Opens a {@link FileOutputStream} for the specified file, checking and
    2606     * creating the parent directory if it does not exist.
    2607     * <p>
    2608     * At the end of the method either the stream will be successfully opened,
    2609     * or an exception will have been thrown.
    2610     * </p>
    2611     * <p>
    2612     * The parent directory will be created if it does not exist.
    2613     * The file will be created if it does not exist.
    2614     * An exception is thrown if the file object exists but is a directory.
    2615     * An exception is thrown if the file exists but cannot be written to.
    2616     * An exception is thrown if the parent directory cannot be created.
    2617     * </p>
    2618     *
    2619     * @param file   the file to open for output, must not be {@code null}
    2620     * @param append if {@code true}, then bytes will be added to the
    2621     *               end of the file rather than overwriting
    2622     * @return a new {@link FileOutputStream} for the specified file
    2623     * @throws NullPointerException if the file object is {@code null}.
    2624     * @throws IllegalArgumentException if the file object is a directory
    2625     * @throws IOException if the directories could not be created, or the file is not writable
    2626     * @since 2.1
    2627     */
    2628    public static FileOutputStream openOutputStream(final File file, final boolean append) throws IOException {
    2629        Objects.requireNonNull(file, "file");
    2630        if (file.exists()) {
    2631            checkIsFile(file, "file");
    2632        } else {
    2633            createParentDirectories(file);
    2634        }
    2635        return new FileOutputStream(file, append);
    2636    }
    2637
    2638    /**
    2639     * Reads the contents of a file into a byte array.
    2640     * The file is always closed.
    2641     *
    2642     * @param file the file to read, must not be {@code null}
    2643     * @return the file contents, never {@code null}
    2644     * @throws NullPointerException if file is {@code null}.
    2645     * @throws IOException if an I/O error occurs, including when the file does not exist, is a directory rather than a
    2646     *         regular file, or for some other reason why the file cannot be opened for reading.
    2647     * @since 1.1
    2648     */
    2649    public static byte[] readFileToByteArray(final File file) throws IOException {
    2650        Objects.requireNonNull(file, "file");
    2651        return Files.readAllBytes(file.toPath());
    2652    }
    2653
    2654    /**
    2655     * Reads the contents of a file into a String using the default encoding for the VM.
    2656     * The file is always closed.
    2657     *
    2658     * @param file the file to read, must not be {@code null}
    2659     * @return the file contents, never {@code null}
    2660     * @throws NullPointerException if file is {@code null}.
    2661     * @throws IOException if an I/O error occurs, including when the file does not exist, is a directory rather than a
    2662     *         regular file, or for some other reason why the file cannot be opened for reading.
    2663     * @since 1.3.1
    2664     * @deprecated Use {@link #readFileToString(File, Charset)} instead (and specify the appropriate encoding)
    2665     */
    2666    @Deprecated
    2667    public static String readFileToString(final File file) throws IOException {
    2668        return readFileToString(file, Charset.defaultCharset());
    2669    }
    2670
    2671    /**
    2672     * Reads the contents of a file into a String.
    2673     * The file is always closed.
    2674     *
    2675     * @param file     the file to read, must not be {@code null}
    2676     * @param charsetName the name of the requested charset, {@code null} means platform default
    2677     * @return the file contents, never {@code null}
    2678     * @throws NullPointerException if file is {@code null}.
    2679     * @throws IOException if an I/O error occurs, including when the file does not exist, is a directory rather than a
    2680     *         regular file, or for some other reason why the file cannot be opened for reading.
    2681     * @since 2.3
    2682     */
    2683    public static String readFileToString(final File file, final Charset charsetName) throws IOException {
    2684        return IOUtils.toString(() -> Files.newInputStream(file.toPath()), Charsets.toCharset(charsetName));
    2685    }
    2686
    2687    /**
    2688     * Reads the contents of a file into a String. The file is always closed.
    2689     *
    2690     * @param file     the file to read, must not be {@code null}
    2691     * @param charsetName the name of the requested charset, {@code null} means platform default
    2692     * @return the file contents, never {@code null}
    2693     * @throws NullPointerException if file is {@code null}.
    2694     * @throws IOException if an I/O error occurs, including when the file does not exist, is a directory rather than a
    2695     *         regular file, or for some other reason why the file cannot be opened for reading.
    2696     * @throws java.nio.charset.UnsupportedCharsetException if the named charset is unavailable.
    2697     * @since 2.3
    2698     */
    2699    public static String readFileToString(final File file, final String charsetName) throws IOException {
    2700        return readFileToString(file, Charsets.toCharset(charsetName));
    2701    }
    2702
    2703    /**
    2704     * Reads the contents of a file line by line to a List of Strings using the default encoding for the VM.
    2705     * The file is always closed.
    2706     *
    2707     * @param file the file to read, must not be {@code null}
    2708     * @return the list of Strings representing each line in the file, never {@code null}
    2709     * @throws NullPointerException if file is {@code null}.
    2710     * @throws IOException if an I/O error occurs, including when the file does not exist, is a directory rather than a
    2711     *         regular file, or for some other reason why the file cannot be opened for reading.
    2712     * @since 1.3
    2713     * @deprecated Use {@link #readLines(File, Charset)} instead (and specify the appropriate encoding)
    2714     */
    2715    @Deprecated
    2716    public static List<String> readLines(final File file) throws IOException {
    2717        return readLines(file, Charset.defaultCharset());
    2718    }
    2719
    2720    /**
    2721     * Reads the contents of a file line by line to a List of Strings.
    2722     * The file is always closed.
    2723     *
    2724     * @param file     the file to read, must not be {@code null}
    2725     * @param charset the charset to use, {@code null} means platform default
    2726     * @return the list of Strings representing each line in the file, never {@code null}
    2727     * @throws NullPointerException if file is {@code null}.
    2728     * @throws IOException if an I/O error occurs, including when the file does not exist, is a directory rather than a
    2729     *         regular file, or for some other reason why the file cannot be opened for reading.
    2730     * @since 2.3
    2731     */
    2732    public static List<String> readLines(final File file, final Charset charset) throws IOException {
    2733        return Files.readAllLines(file.toPath(), charset);
    2734    }
    2735
    2736    /**
    2737     * Reads the contents of a file line by line to a List of Strings. The file is always closed.
    2738     *
    2739     * @param file     the file to read, must not be {@code null}
    2740     * @param charsetName the name of the requested charset, {@code null} means platform default
    2741     * @return the list of Strings representing each line in the file, never {@code null}
    2742     * @throws NullPointerException if file is {@code null}.
    2743     * @throws IOException if an I/O error occurs, including when the file does not exist, is a directory rather than a
    2744     *         regular file, or for some other reason why the file cannot be opened for reading.
    2745     * @throws java.nio.charset.UnsupportedCharsetException if the named charset is unavailable.
    2746     * @since 1.1
    2747     */
    2748    public static List<String> readLines(final File file, final String charsetName) throws IOException {
    2749        return readLines(file, Charsets.toCharset(charsetName));
    2750    }
    2751
    2752    private static void requireAbsent(final File file, final String name) throws FileExistsException {
    2753        if (file.exists()) {
    2754            throw new FileExistsException(String.format("File element in parameter '%s' already exists: '%s'", name, file));
    2755        }
    2756    }
    2757
    2758    /**
    2759     * Throws IllegalArgumentException if the given files' canonical representations are equal.
    2760     *
    2761     * @param file1 The first file to compare.
    2762     * @param file2 The second file to compare.
    2763     * @throws IOException if an I/O error occurs.
    2764     * @throws IllegalArgumentException if the given files' canonical representations are equal.
    2765     */
    2766    private static void requireCanonicalPathsNotEquals(final File file1, final File file2) throws IOException {
    2767        final String canonicalPath = file1.getCanonicalPath();
    2768        if (canonicalPath.equals(file2.getCanonicalPath())) {
    2769            throw new IllegalArgumentException(String
    2770                .format("File canonical paths are equal: '%s' (file1='%s', file2='%s')", canonicalPath, file1, file2));
    2771        }
    2772    }
    2773
    2774    /**
    2775     * Requires that the given {@link File} exists and is a directory.
    2776     *
    2777     * @param directory The {@link File} to check.
    2778     * @param name The parameter name to use in the exception message in case of null input or if the file is not a directory.
    2779     * @throws NullPointerException if the given {@link File} is {@code null}.
    2780     * @throws FileNotFoundException if the given {@link File} does not exist
    2781     * @throws IllegalArgumentException if the given {@link File} exists but is not a directory.
    2782     */
    2783    private static void requireDirectoryExists(final File directory, final String name) throws FileNotFoundException {
    2784        Objects.requireNonNull(directory, name);
    2785        if (!directory.isDirectory()) {
    2786            if (directory.exists()) {
    2787                throw new IllegalArgumentException("Parameter '" + name + "' is not a directory: '" + directory + "'");
    2788            }
    2789            throw new FileNotFoundException("Directory '" + directory + "' does not exist.");
    2790        }
    2791    }
    2792
    2793    /**
    2794     * Requires that the given {@link File} is a directory if it exists.
    2795     *
    2796     * @param directory The {@link File} to check.
    2797     * @param name The parameter name to use in the exception message in case of null input.
    2798     * @throws NullPointerException if the given {@link File} is {@code null}.
    2799     * @throws IllegalArgumentException if the given {@link File} exists but is not a directory.
    2800     */
    2801    private static void requireDirectoryIfExists(final File directory, final String name) throws FileNotFoundException {
    2802        Objects.requireNonNull(directory, name);
    2803        if (directory.exists() && !directory.isDirectory()) {
    2804            throw new IllegalArgumentException("Parameter '" + name + "' is not a directory: '" + directory + "'");
    2805        }
    2806    }
    2807
    2808    /**
    2809     * Sets file lastModifiedTime, lastAccessTime and creationTime to match source file
    2810     *
    2811     * @param sourceFile The source file to query.
    2812     * @param targetFile The target file or directory to set.
    2813     * @return {@code true} if and only if the operation succeeded;
    2814     *          {@code false} otherwise
    2815     * @throws NullPointerException if sourceFile is {@code null}.
    2816     * @throws NullPointerException if targetFile is {@code null}.
    2817     */
    2818    private static boolean setTimes(final File sourceFile, final File targetFile) {
    2819        Objects.requireNonNull(sourceFile, "sourceFile");
    2820        Objects.requireNonNull(targetFile, "targetFile");
    2821        try {
    2822            // Set creation, modified, last accessed to match source file
    2823            final BasicFileAttributes srcAttr = Files.readAttributes(sourceFile.toPath(), BasicFileAttributes.class);
    2824            final BasicFileAttributeView destAttrView = Files.getFileAttributeView(targetFile.toPath(), BasicFileAttributeView.class);
    2825            // null guards are not needed; BasicFileAttributes.setTimes(...) is null safe
    2826            destAttrView.setTimes(srcAttr.lastModifiedTime(), srcAttr.lastAccessTime(), srcAttr.creationTime());
    2827            return true;
    2828        } catch (final IOException ignored) {
    2829            // Fallback: Only set modified time to match source file
    2830            return targetFile.setLastModified(sourceFile.lastModified());
    2831        }
    2832
    2833        // TODO: (Help!) Determine historically why setLastModified(File, File) needed PathUtils.setLastModifiedTime() if
    2834        //  sourceFile.isFile() was true, but needed setLastModifiedTime(File, long) if sourceFile.isFile() was false
    2835    }
    2836
    2837    /**
    2838     * Returns the size of the specified file or directory. If the provided
    2839     * {@link File} is a regular file, then the file's length is returned.
    2840     * If the argument is a directory, then the size of the directory is
    2841     * calculated recursively. If a directory or subdirectory is security
    2842     * restricted, its size will not be included.
    2843     * <p>
    2844     * Note that overflow is not detected, and the return value may be negative if
    2845     * overflow occurs. See {@link #sizeOfAsBigInteger(File)} for an alternative
    2846     * method that does not overflow.
    2847     * </p>
    2848     *
    2849     * @param file the regular file or directory to return the size
    2850     *             of (must not be {@code null}).
    2851     *
    2852     * @return the length of the file, or recursive size of the directory,
    2853     * provided (in bytes).
    2854     *
    2855     * @throws NullPointerException     if the file is {@code null}.
    2856     * @throws IllegalArgumentException if the file does not exist.
    2857     * @throws UncheckedIOException if an IO error occurs.
    2858     * @since 2.0
    2859     */
    2860    public static long sizeOf(final File file) {
    2861        return Uncheck.get(() -> PathUtils.sizeOf(file.toPath()));
    2862    }
    2863
    2864    /**
    2865     * Returns the size of the specified file or directory. If the provided
    2866     * {@link File} is a regular file, then the file's length is returned.
    2867     * If the argument is a directory, then the size of the directory is
    2868     * calculated recursively. If a directory or subdirectory is security
    2869     * restricted, its size will not be included.
    2870     *
    2871     * @param file the regular file or directory to return the size
    2872     *             of (must not be {@code null}).
    2873     *
    2874     * @return the length of the file, or recursive size of the directory,
    2875     * provided (in bytes).
    2876     *
    2877     * @throws NullPointerException     if the file is {@code null}.
    2878     * @throws IllegalArgumentException if the file does not exist.
    2879     * @throws UncheckedIOException if an IO error occurs.
    2880     * @since 2.4
    2881     */
    2882    public static BigInteger sizeOfAsBigInteger(final File file) {
    2883        return Uncheck.get(() -> PathUtils.sizeOfAsBigInteger(file.toPath()));
    2884    }
    2885
    2886    /**
    2887     * Counts the size of a directory recursively (sum of the length of all files).
    2888     * <p>
    2889     * Note that overflow is not detected, and the return value may be negative if
    2890     * overflow occurs. See {@link #sizeOfDirectoryAsBigInteger(File)} for an alternative
    2891     * method that does not overflow.
    2892     * </p>
    2893     *
    2894     * @param directory directory to inspect, must not be {@code null}.
    2895     * @return size of directory in bytes, 0 if directory is security restricted, a negative number when the real total
    2896     * is greater than {@link Long#MAX_VALUE}.
    2897     * @throws IllegalArgumentException if the given {@link File} exists but is not a directory
    2898     * @throws NullPointerException if the directory is {@code null}.
    2899     * @throws UncheckedIOException if an IO error occurs.
    2900     */
    2901    public static long sizeOfDirectory(final File directory) {
    2902        try {
    2903            requireDirectoryExists(directory, "directory");
    2904        } catch (final FileNotFoundException e) {
    2905            throw new UncheckedIOException(e);
    2906        }
    2907        return Uncheck.get(() -> PathUtils.sizeOfDirectory(directory.toPath()));
    2908    }
    2909
    2910    /**
    2911     * Counts the size of a directory recursively (sum of the length of all files).
    2912     *
    2913     * @param directory directory to inspect, must not be {@code null}.
    2914     * @return size of directory in bytes, 0 if directory is security restricted.
    2915     * @throws IllegalArgumentException if the given {@link File} exists but is not a directory
    2916     * @throws NullPointerException if the directory is {@code null}.
    2917     * @throws UncheckedIOException if an IO error occurs.
    2918     * @since 2.4
    2919     */
    2920    public static BigInteger sizeOfDirectoryAsBigInteger(final File directory) {
    2921        try {
    2922            requireDirectoryExists(directory, "directory");
    2923        } catch (final FileNotFoundException e) {
    2924            throw new UncheckedIOException(e);
    2925        }
    2926        return Uncheck.get(() -> PathUtils.sizeOfDirectoryAsBigInteger(directory.toPath()));
    2927    }
    2928
    2929    /**
    2930     * Streams over the files in a given directory (and optionally its subdirectories) which match an array of extensions.
    2931     * <p>
    2932     * The returned {@link Stream} may wrap one or more {@link DirectoryStream}s. When you require timely disposal of file system resources, use a
    2933     * {@code try}-with-resources block to ensure invocation of the stream's {@link Stream#close()} method after the stream operations are completed. Calling a
    2934     * closed stream causes a {@link IllegalStateException}.
    2935     * </p>
    2936     *
    2937     * @param directory  the directory to search in
    2938     * @param recursive  if true all subdirectories are searched as well
    2939     * @param extensions an array of extensions, for example, {"java","xml"}. If this parameter is {@code null}, all files are returned.
    2940     * @return a Stream of {@link File} for matching files.
    2941     * @throws IOException if an I/O error is thrown when accessing the starting file.
    2942     * @since 2.9.0
    2943     */
    2944    public static Stream<File> streamFiles(final File directory, final boolean recursive, final String... extensions) throws IOException {
    2945        // @formatter:off
    2946        final IOFileFilter filter = extensions == null
    2947            ? FileFileFilter.INSTANCE
    2948            : FileFileFilter.INSTANCE.and(new SuffixFileFilter(toSuffixes(extensions)));
    2949        // @formatter:on
    2950        return PathUtils.walk(directory.toPath(), filter, toMaxDepth(recursive), false, FileVisitOption.FOLLOW_LINKS).map(Path::toFile);
    2951    }
    2952
    2953    /**
    2954     * Converts from a {@link URL} to a {@link File}.
    2955     * <p>
    2956     * Syntax such as {@code file:///my%20docs/file.txt} will be
    2957     * correctly decoded to {@code /my docs/file.txt}.
    2958     * UTF-8 is used to decode percent-encoded octets to characters.
    2959     * Additionally, malformed percent-encoded octets are handled leniently by
    2960     * passing them through literally.
    2961     * </p>
    2962     *
    2963     * @param url the file URL to convert, {@code null} returns {@code null}
    2964     * @return the equivalent {@link File} object, or {@code null}
    2965     * if the URL's protocol is not {@code file}
    2966     */
    2967    public static File toFile(final URL url) {
    2968        if (url == null || !"file".equalsIgnoreCase(url.getProtocol())) {
    2969            return null;
    2970        }
    2971        final String fileName = url.getFile().replace('/', File.separatorChar);
    2972        return new File(decodeUrl(fileName));
    2973    }
    2974
    2975    /**
    2976     * Converts each of an array of {@link URL} to a {@link File}.
    2977     * <p>
    2978     * Returns an array of the same size as the input.
    2979     * If the input is {@code null}, an empty array is returned.
    2980     * If the input contains {@code null}, the output array contains {@code null} at the same
    2981     * index.
    2982     * </p>
    2983     * <p>
    2984     * This method will decode the URL.
    2985     * Syntax such as {@code file:///my%20docs/file.txt} will be
    2986     * correctly decoded to {@code /my docs/file.txt}.
    2987     * </p>
    2988     *
    2989     * @param urls the file URLs to convert, {@code null} returns empty array
    2990     * @return a non-{@code null} array of Files matching the input, with a {@code null} item
    2991     * if there was a {@code null} at that index in the input array
    2992     * @throws IllegalArgumentException if any file is not a URL file
    2993     * @throws IllegalArgumentException if any file is incorrectly encoded
    2994     * @since 1.1
    2995     */
    2996    public static File[] toFiles(final URL... urls) {
    2997        if (IOUtils.length(urls) == 0) {
    2998            return EMPTY_FILE_ARRAY;
    2999        }
    3000        final File[] files = new File[urls.length];
    3001        for (int i = 0; i < urls.length; i++) {
    3002            final URL url = urls[i];
    3003            if (url != null) {
    3004                if (!"file".equalsIgnoreCase(url.getProtocol())) {
    3005                    throw new IllegalArgumentException("Can only convert file URL to a File: " + url);
    3006                }
    3007                files[i] = toFile(url);
    3008            }
    3009        }
    3010        return files;
    3011    }
    3012
    3013    /**
    3014     * Consumes all of the given stream.
    3015     * <p>
    3016     * When called from a FileTreeWalker, the walker <em>closes</em> the stream because {@link FileTreeWalker#next()} calls {@code top.stream().close()}.
    3017     * </p>
    3018     *
    3019     * @param stream The stream to consume.
    3020     * @return a new List.
    3021     */
    3022    private static List<File> toList(final Stream<File> stream) {
    3023        return stream.collect(Collectors.toList());
    3024    }
    3025
    3026    /**
    3027     * Converts whether or not to recurse into a recursion max depth.
    3028     *
    3029     * @param recursive whether or not to recurse
    3030     * @return the recursion depth
    3031     */
    3032    private static int toMaxDepth(final boolean recursive) {
    3033        return recursive ? Integer.MAX_VALUE : 1;
    3034    }
    3035
    3036    /**
    3037     * Converts an array of file extensions to suffixes.
    3038     *
    3039     * @param extensions an array of extensions. Format: {"java", "xml"}
    3040     * @return an array of suffixes. Format: {".java", ".xml"}
    3041     * @throws NullPointerException if the parameter is null
    3042     */
    3043    private static String[] toSuffixes(final String... extensions) {
    3044        return Stream.of(Objects.requireNonNull(extensions, "extensions")).map(e -> "." + e).toArray(String[]::new);
    3045    }
    3046
    3047    /**
    3048     * Implements behavior similar to the UNIX "touch" utility. Creates a new file with size 0, or, if the file exists, just
    3049     * updates the file's modified time. This method throws an IOException if the last modified date
    3050     * of the file cannot be set. It creates parent directories if they do not exist.
    3051     *
    3052     * @param file the File to touch.
    3053     * @throws NullPointerException if the parameter is {@code null}.
    3054     * @throws IOException if setting the last-modified time failed or an I/O problem occurs.
    3055     */
    3056    public static void touch(final File file) throws IOException {
    3057        PathUtils.touch(Objects.requireNonNull(file, "file").toPath());
    3058    }
    3059
    3060    /**
    3061     * Converts each element of an array of {@link File} to a {@link URL}.
    3062     * <p>
    3063     * Returns an array of the same size as the input.
    3064     * </p>
    3065     *
    3066     * @param files the files to convert, must not be {@code null}
    3067     * @return an array of URLs matching the input
    3068     * @throws IOException          if a file cannot be converted
    3069     * @throws NullPointerException if any argument is null
    3070     */
    3071    public static URL[] toURLs(final File... files) throws IOException {
    3072        Objects.requireNonNull(files, "files");
    3073        final URL[] urls = new URL[files.length];
    3074        for (int i = 0; i < urls.length; i++) {
    3075            urls[i] = files[i].toURI().toURL();
    3076        }
    3077        return urls;
    3078    }
    3079
    3080    /**
    3081     * Validates the given arguments.
    3082     * <ul>
    3083     * <li>Throws {@link NullPointerException} if {@code source} is null</li>
    3084     * <li>Throws {@link NullPointerException} if {@code destination} is null</li>
    3085     * <li>Throws {@link FileNotFoundException} if {@code source} does not exist</li>
    3086     * </ul>
    3087     *
    3088     * @param source      the file or directory to be moved.
    3089     * @param destination the destination file or directory.
    3090     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
    3091     * @throws FileNotFoundException if the source file does not exist.
    3092     */
    3093    private static void validateMoveParameters(final File source, final File destination) throws FileNotFoundException {
    3094        Objects.requireNonNull(source, "source");
    3095        Objects.requireNonNull(destination, "destination");
    3096        if (!source.exists()) {
    3097            throw new FileNotFoundException("Source '" + source + "' does not exist");
    3098        }
    3099    }
    3100
    3101    /**
    3102     * Waits for the file system to propagate a file creation, with a timeout.
    3103     * <p>
    3104     * This method repeatedly tests {@link Files#exists(Path, LinkOption...)} until it returns
    3105     * true up to the maximum time specified in seconds.
    3106     * </p>
    3107     *
    3108     * @param file    the file to check, must not be {@code null}
    3109     * @param seconds the maximum time in seconds to wait
    3110     * @return true if file exists
    3111     * @throws NullPointerException if the file is {@code null}
    3112     */
    3113    public static boolean waitFor(final File file, final int seconds) {
    3114        Objects.requireNonNull(file, "file");
    3115        return PathUtils.waitFor(file.toPath(), Duration.ofSeconds(seconds), PathUtils.EMPTY_LINK_OPTION_ARRAY);
    3116    }
    3117
    3118    /**
    3119     * Writes a CharSequence to a file creating the file if it does not exist using the default encoding for the VM.
    3120     *
    3121     * @param file the file to write
    3122     * @param data the content to write to the file
    3123     * @throws IOException in case of an I/O error
    3124     * @since 2.0
    3125     * @deprecated Use {@link #write(File, CharSequence, Charset)} instead (and specify the appropriate encoding)
    3126     */
    3127    @Deprecated
    3128    public static void write(final File file, final CharSequence data) throws IOException {
    3129        write(file, data, Charset.defaultCharset(), false);
    3130    }
    3131
    3132    /**
    3133     * Writes a CharSequence to a file creating the file if it does not exist using the default encoding for the VM.
    3134     *
    3135     * @param file   the file to write
    3136     * @param data   the content to write to the file
    3137     * @param append if {@code true}, then the data will be added to the
    3138     *               end of the file rather than overwriting
    3139     * @throws IOException in case of an I/O error
    3140     * @since 2.1
    3141     * @deprecated Use {@link #write(File, CharSequence, Charset, boolean)} instead (and specify the appropriate encoding)
    3142     */
    3143    @Deprecated
    3144    public static void write(final File file, final CharSequence data, final boolean append) throws IOException {
    3145        write(file, data, Charset.defaultCharset(), append);
    3146    }
    3147
    3148    /**
    3149     * Writes a CharSequence to a file creating the file if it does not exist.
    3150     *
    3151     * @param file     the file to write
    3152     * @param data     the content to write to the file
    3153     * @param charset the name of the requested charset, {@code null} means platform default
    3154     * @throws IOException in case of an I/O error
    3155     * @since 2.3
    3156     */
    3157    public static void write(final File file, final CharSequence data, final Charset charset) throws IOException {
    3158        write(file, data, charset, false);
    3159    }
    3160
    3161    /**
    3162     * Writes a CharSequence to a file creating the file if it does not exist.
    3163     *
    3164     * @param file     the file to write
    3165     * @param data     the content to write to the file
    3166     * @param charset the charset to use, {@code null} means platform default
    3167     * @param append   if {@code true}, then the data will be added to the
    3168     *                 end of the file rather than overwriting
    3169     * @throws IOException in case of an I/O error
    3170     * @since 2.3
    3171     */
    3172    public static void write(final File file, final CharSequence data, final Charset charset, final boolean append) throws IOException {
    3173        writeStringToFile(file, Objects.toString(data, null), charset, append);
    3174    }
    3175
    3176    /**
    3177     * Writes a CharSequence to a file creating the file if it does not exist.
    3178     *
    3179     * @param file     the file to write
    3180     * @param data     the content to write to the file
    3181     * @param charsetName the name of the requested charset, {@code null} means platform default
    3182     * @throws IOException                          in case of an I/O error
    3183     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
    3184     * @since 2.0
    3185     */
    3186    public static void write(final File file, final CharSequence data, final String charsetName) throws IOException {
    3187        write(file, data, charsetName, false);
    3188    }
    3189
    3190    /**
    3191     * Writes a CharSequence to a file creating the file if it does not exist.
    3192     *
    3193     * @param file     the file to write
    3194     * @param data     the content to write to the file
    3195     * @param charsetName the name of the requested charset, {@code null} means platform default
    3196     * @param append   if {@code true}, then the data will be added to the
    3197     *                 end of the file rather than overwriting
    3198     * @throws IOException                 in case of an I/O error
    3199     * @throws java.nio.charset.UnsupportedCharsetException if the encoding is not supported by the VM
    3200     * @since 2.1
    3201     */
    3202    public static void write(final File file, final CharSequence data, final String charsetName, final boolean append) throws IOException {
    3203        write(file, data, Charsets.toCharset(charsetName), append);
    3204    }
    3205
    3206    // Must be called with a directory
    3207
    3208    /**
    3209     * Writes a byte array to a file creating the file if it does not exist.
    3210     * The parent directories of the file will be created if they do not exist.
    3211     *
    3212     * @param file the file to write to
    3213     * @param data the content to write to the file
    3214     * @throws IOException in case of an I/O error
    3215     * @since 1.1
    3216     */
    3217    public static void writeByteArrayToFile(final File file, final byte[] data) throws IOException {
    3218        writeByteArrayToFile(file, data, false);
    3219    }
    3220
    3221    /**
    3222     * Writes a byte array to a file creating the file if it does not exist.
    3223     *
    3224     * @param file   the file to write to
    3225     * @param data   the content to write to the file
    3226     * @param append if {@code true}, then bytes will be added to the
    3227     *               end of the file rather than overwriting
    3228     * @throws IOException in case of an I/O error
    3229     * @since 2.1
    3230     */
    3231    public static void writeByteArrayToFile(final File file, final byte[] data, final boolean append) throws IOException {
    3232        writeByteArrayToFile(file, data, 0, data.length, append);
    3233    }
    3234
    3235    /**
    3236     * Writes {@code len} bytes from the specified byte array starting
    3237     * at offset {@code off} to a file, creating the file if it does
    3238     * not exist.
    3239     *
    3240     * @param file the file to write to
    3241     * @param data the content to write to the file
    3242     * @param off  the start offset in the data
    3243     * @param len  the number of bytes to write
    3244     * @throws IOException in case of an I/O error
    3245     * @since 2.5
    3246     */
    3247    public static void writeByteArrayToFile(final File file, final byte[] data, final int off, final int len) throws IOException {
    3248        writeByteArrayToFile(file, data, off, len, false);
    3249    }
    3250
    3251    /**
    3252     * Writes {@code len} bytes from the specified byte array starting
    3253     * at offset {@code off} to a file, creating the file if it does
    3254     * not exist.
    3255     *
    3256     * @param file   the file to write to
    3257     * @param data   the content to write to the file
    3258     * @param off    the start offset in the data
    3259     * @param len    the number of bytes to write
    3260     * @param append if {@code true}, then bytes will be added to the
    3261     *               end of the file rather than overwriting
    3262     * @throws IOException in case of an I/O error
    3263     * @since 2.5
    3264     */
    3265    public static void writeByteArrayToFile(final File file, final byte[] data, final int off, final int len, final boolean append) throws IOException {
    3266        try (OutputStream out = newOutputStream(file, append)) {
    3267            out.write(data, off, len);
    3268        }
    3269    }
    3270
    3271    /**
    3272     * Writes the {@code toString()} value of each item in a collection to
    3273     * the specified {@link File} line by line.
    3274     * The default VM encoding and the default line ending will be used.
    3275     *
    3276     * @param file  the file to write to
    3277     * @param lines the lines to write, {@code null} entries produce blank lines
    3278     * @throws IOException in case of an I/O error
    3279     * @since 1.3
    3280     */
    3281    public static void writeLines(final File file, final Collection<?> lines) throws IOException {
    3282        writeLines(file, null, lines, null, false);
    3283    }
    3284
    3285    /**
    3286     * Writes the {@code toString()} value of each item in a collection to
    3287     * the specified {@link File} line by line.
    3288     * The default VM encoding and the default line ending will be used.
    3289     *
    3290     * @param file   the file to write to
    3291     * @param lines  the lines to write, {@code null} entries produce blank lines
    3292     * @param append if {@code true}, then the lines will be added to the
    3293     *               end of the file rather than overwriting
    3294     * @throws IOException in case of an I/O error
    3295     * @since 2.1
    3296     */
    3297    public static void writeLines(final File file, final Collection<?> lines, final boolean append) throws IOException {
    3298        writeLines(file, null, lines, null, append);
    3299    }
    3300
    3301    /**
    3302     * Writes the {@code toString()} value of each item in a collection to
    3303     * the specified {@link File} line by line.
    3304     * The default VM encoding and the specified line ending will be used.
    3305     *
    3306     * @param file       the file to write to
    3307     * @param lines      the lines to write, {@code null} entries produce blank lines
    3308     * @param lineEnding the line separator to use, {@code null} is system default
    3309     * @throws IOException in case of an I/O error
    3310     * @since 1.3
    3311     */
    3312    public static void writeLines(final File file, final Collection<?> lines, final String lineEnding) throws IOException {
    3313        writeLines(file, null, lines, lineEnding, false);
    3314    }
    3315
    3316    /**
    3317     * Writes the {@code toString()} value of each item in a collection to
    3318     * the specified {@link File} line by line.
    3319     * The default VM encoding and the specified line ending will be used.
    3320     *
    3321     * @param file       the file to write to
    3322     * @param lines      the lines to write, {@code null} entries produce blank lines
    3323     * @param lineEnding the line separator to use, {@code null} is system default
    3324     * @param append     if {@code true}, then the lines will be added to the
    3325     *                   end of the file rather than overwriting
    3326     * @throws IOException in case of an I/O error
    3327     * @since 2.1
    3328     */
    3329    public static void writeLines(final File file, final Collection<?> lines, final String lineEnding, final boolean append) throws IOException {
    3330        writeLines(file, null, lines, lineEnding, append);
    3331    }
    3332
    3333    /**
    3334     * Writes the {@code toString()} value of each item in a collection to
    3335     * the specified {@link File} line by line.
    3336     * The specified character encoding and the default line ending will be used.
    3337     * The parent directories of the file will be created if they do not exist.
    3338     *
    3339     * @param file     the file to write to
    3340     * @param charsetName the name of the requested charset, {@code null} means platform default
    3341     * @param lines    the lines to write, {@code null} entries produce blank lines
    3342     * @throws IOException                          in case of an I/O error
    3343     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
    3344     * @since 1.1
    3345     */
    3346    public static void writeLines(final File file, final String charsetName, final Collection<?> lines) throws IOException {
    3347        writeLines(file, charsetName, lines, null, false);
    3348    }
    3349
    3350    /**
    3351     * Writes the {@code toString()} value of each item in a collection to
    3352     * the specified {@link File} line by line, optionally appending.
    3353     * The specified character encoding and the default line ending will be used.
    3354     *
    3355     * @param file     the file to write to
    3356     * @param charsetName the name of the requested charset, {@code null} means platform default
    3357     * @param lines    the lines to write, {@code null} entries produce blank lines
    3358     * @param append   if {@code true}, then the lines will be added to the
    3359     *                 end of the file rather than overwriting
    3360     * @throws IOException                          in case of an I/O error
    3361     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
    3362     * @since 2.1
    3363     */
    3364    public static void writeLines(final File file, final String charsetName, final Collection<?> lines, final boolean append) throws IOException {
    3365        writeLines(file, charsetName, lines, null, append);
    3366    }
    3367
    3368    /**
    3369     * Writes the {@code toString()} value of each item in a collection to
    3370     * the specified {@link File} line by line.
    3371     * The specified character encoding and the line ending will be used.
    3372     * The parent directories of the file will be created if they do not exist.
    3373     *
    3374     * @param file       the file to write to
    3375     * @param charsetName   the name of the requested charset, {@code null} means platform default
    3376     * @param lines      the lines to write, {@code null} entries produce blank lines
    3377     * @param lineEnding the line separator to use, {@code null} is system default
    3378     * @throws IOException                          in case of an I/O error
    3379     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
    3380     * @since 1.1
    3381     */
    3382    public static void writeLines(final File file, final String charsetName, final Collection<?> lines, final String lineEnding) throws IOException {
    3383        writeLines(file, charsetName, lines, lineEnding, false);
    3384    }
    3385
    3386    /**
    3387     * Writes the {@code toString()} value of each item in a collection to
    3388     * the specified {@link File} line by line.
    3389     * The specified character encoding and the line ending will be used.
    3390     *
    3391     * @param file       the file to write to
    3392     * @param charsetName   the name of the requested charset, {@code null} means platform default
    3393     * @param lines      the lines to write, {@code null} entries produce blank lines
    3394     * @param lineEnding the line separator to use, {@code null} is system default
    3395     * @param append     if {@code true}, then the lines will be added to the
    3396     *                   end of the file rather than overwriting
    3397     * @throws IOException                          in case of an I/O error
    3398     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
    3399     * @since 2.1
    3400     */
    3401    public static void writeLines(final File file, final String charsetName, final Collection<?> lines, final String lineEnding, final boolean append)
    3402        throws IOException {
    3403        try (OutputStream out = new BufferedOutputStream(newOutputStream(file, append))) {
    3404            IOUtils.writeLines(lines, lineEnding, out, charsetName);
    3405        }
    3406    }
    3407
    3408    /**
    3409     * Writes a String to a file creating the file if it does not exist using the default encoding for the VM.
    3410     *
    3411     * @param file the file to write
    3412     * @param data the content to write to the file
    3413     * @throws IOException in case of an I/O error
    3414     * @deprecated Use {@link #writeStringToFile(File, String, Charset)} instead (and specify the appropriate encoding)
    3415     */
    3416    @Deprecated
    3417    public static void writeStringToFile(final File file, final String data) throws IOException {
    3418        writeStringToFile(file, data, Charset.defaultCharset(), false);
    3419    }
    3420
    3421    /**
    3422     * Writes a String to a file creating the file if it does not exist using the default encoding for the VM.
    3423     *
    3424     * @param file   the file to write
    3425     * @param data   the content to write to the file
    3426     * @param append if {@code true}, then the String will be added to the
    3427     *               end of the file rather than overwriting
    3428     * @throws IOException in case of an I/O error
    3429     * @since 2.1
    3430     * @deprecated Use {@link #writeStringToFile(File, String, Charset, boolean)} instead (and specify the appropriate encoding)
    3431     */
    3432    @Deprecated
    3433    public static void writeStringToFile(final File file, final String data, final boolean append) throws IOException {
    3434        writeStringToFile(file, data, Charset.defaultCharset(), append);
    3435    }
    3436
    3437    /**
    3438     * Writes a String to a file creating the file if it does not exist.
    3439     * The parent directories of the file will be created if they do not exist.
    3440     *
    3441     * @param file     the file to write
    3442     * @param data     the content to write to the file
    3443     * @param charset the charset to use, {@code null} means platform default
    3444     * @throws IOException                          in case of an I/O error
    3445     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
    3446     * @since 2.4
    3447     */
    3448    public static void writeStringToFile(final File file, final String data, final Charset charset) throws IOException {
    3449        writeStringToFile(file, data, charset, false);
    3450    }
    3451
    3452    /**
    3453     * Writes a String to a file, creating the file if it does not exist.
    3454     * The parent directories of the file are created if they do not exist.
    3455     *
    3456     * @param file     the file to write
    3457     * @param data     the content to write to the file
    3458     * @param charset the charset to use, {@code null} means platform default
    3459     * @param append   if {@code true}, then the String will be added to the
    3460     *                 end of the file rather than overwriting
    3461     * @throws IOException in case of an I/O error
    3462     * @since 2.3
    3463     */
    3464    public static void writeStringToFile(final File file, final String data, final Charset charset, final boolean append) throws IOException {
    3465        try (OutputStream out = newOutputStream(file, append)) {
    3466            IOUtils.write(data, out, charset);
    3467        }
    3468    }
    3469
    3470    /**
    3471     * Writes a String to a file, creating the file if it does not exist.
    3472     * The parent directories of the file are created if they do not exist.
    3473     *
    3474     * @param file     the file to write
    3475     * @param data     the content to write to the file
    3476     * @param charsetName the name of the requested charset, {@code null} means platform default
    3477     * @throws IOException                          in case of an I/O error
    3478     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
    3479     */
    3480    public static void writeStringToFile(final File file, final String data, final String charsetName) throws IOException {
    3481        writeStringToFile(file, data, charsetName, false);
    3482    }
    3483
    3484    /**
    3485     * Writes a String to a file, creating the file if it does not exist.
    3486     * The parent directories of the file are created if they do not exist.
    3487     *
    3488     * @param file     the file to write
    3489     * @param data     the content to write to the file
    3490     * @param charsetName the name of the requested charset, {@code null} means platform default
    3491     * @param append   if {@code true}, then the String will be added to the
    3492     *                 end of the file rather than overwriting
    3493     * @throws IOException                 in case of an I/O error
    3494     * @throws java.nio.charset.UnsupportedCharsetException if the encoding is not supported by the VM
    3495     * @since 2.1
    3496     */
    3497    public static void writeStringToFile(final File file, final String data, final String charsetName, final boolean append) throws IOException {
    3498        writeStringToFile(file, data, Charsets.toCharset(charsetName), append);
    3499    }
    3500
    3501    /**
    3502     * Instances should NOT be constructed in standard programming.
    3503     *
    3504     * @deprecated TODO Make private in 3.0.
    3505     */
    3506    @Deprecated
    3507    public FileUtils() { //NOSONAR
    3508        // empty
    3509    }
    3510
    3511}