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