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