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