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