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