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