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
097 * <li>reading from a file
098 * <li>make a directory including parent directories
099 * <li>copying files and directories
100 * <li>deleting files and directories
101 * <li>converting to and from a URL
102 * <li>listing files and directories by filter and extension
103 * <li>comparing file content
104 * <li>file last changed date
105 * <li>calculating a checksum
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.
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     * Getsv the path to the system temporary directory.
1554     *
1555     * WARNING: this method relies on the Java system property 'java.io.tmpdir'
1556     * which may or may not have a trailing file separator.
1557     * This can affect code that uses String processing to manipulate pathnames rather
1558     * than the standard libary methods in classes such as {@link File}
1559     *
1560     * @return the path to the system temporary directory as a String
1561     * @since 2.0
1562     */
1563    public static String getTempDirectoryPath() {
1564        return System.getProperty("java.io.tmpdir");
1565    }
1566
1567    /**
1568     * Gets a {@link File} representing the user's home directory.
1569     *
1570     * @return the user's home directory.
1571     * @since 2.0
1572     */
1573    public static File getUserDirectory() {
1574        return new File(getUserDirectoryPath());
1575    }
1576
1577    /**
1578     * Gets the path to the user's home directory.
1579     *
1580     * @return the path to the user's home directory.
1581     * @since 2.0
1582     */
1583    public static String getUserDirectoryPath() {
1584        return System.getProperty("user.home");
1585    }
1586
1587    /**
1588     * Tests whether the specified {@link File} is a directory or not. Implemented as a
1589     * null-safe delegate to {@link Files#isDirectory(Path path, LinkOption... options)}.
1590     *
1591     * @param   file the path to the file.
1592     * @param   options options indicating how symbolic links are handled
1593     * @return  {@code true} if the file is a directory; {@code false} if
1594     *          the path is null, the file does not exist, is not a directory, or it cannot
1595     *          be determined if the file is a directory or not.
1596     * @throws SecurityException     In the case of the default provider, and a security manager is installed, the
1597     *                               {@link SecurityManager#checkRead(String) checkRead} method is invoked to check read
1598     *                               access to the directory.
1599     * @since 2.9.0
1600     */
1601    public static boolean isDirectory(final File file, final LinkOption... options) {
1602        return file != null && Files.isDirectory(file.toPath(), options);
1603    }
1604
1605    /**
1606     * Tests whether the directory is empty.
1607     *
1608     * @param directory the directory to query.
1609     * @return whether the directory is empty.
1610     * @throws IOException if an I/O error occurs.
1611     * @throws NotDirectoryException if the file could not otherwise be opened because it is not a directory
1612     *                               <em>(optional specific exception)</em>.
1613     * @since 2.9.0
1614     */
1615    public static boolean isEmptyDirectory(final File directory) throws IOException {
1616        return PathUtils.isEmptyDirectory(directory.toPath());
1617    }
1618
1619    /**
1620     * Tests if the specified {@link File} is newer than the specified {@link ChronoLocalDate}
1621     * at the end of day.
1622     *
1623     * <p>
1624     * Note: The input date is assumed to be in the system default time-zone with the time
1625     * part set to the current time. To use a non-default time-zone use the method
1626     * {@link #isFileNewer(File, ChronoLocalDateTime, ZoneId)
1627     * isFileNewer(file, chronoLocalDate.atTime(LocalTime.now(zoneId)), zoneId)} where
1628     * {@code zoneId} is a valid {@link ZoneId}.
1629     * </p>
1630     *
1631     * @param file            the {@link File} of which the modification date must be compared.
1632     * @param chronoLocalDate the date reference.
1633     * @return true if the {@link File} exists and has been modified after the given
1634     * {@link ChronoLocalDate} at the current time.
1635     * @throws UncheckedIOException if an I/O error occurs
1636     * @throws NullPointerException if the file or local date is {@code null}.
1637     * @since 2.8.0
1638     */
1639    public static boolean isFileNewer(final File file, final ChronoLocalDate chronoLocalDate) {
1640        return isFileNewer(file, chronoLocalDate, LocalTime.MAX);
1641    }
1642
1643    /**
1644     * Tests if the specified {@link File} is newer than the specified {@link ChronoLocalDate}
1645     * at the specified time.
1646     *
1647     * <p>
1648     * Note: The input date and time are assumed to be in the system default time-zone. To use a
1649     * non-default time-zone use the method {@link #isFileNewer(File, ChronoLocalDateTime, ZoneId)
1650     * isFileNewer(file, chronoLocalDate.atTime(localTime), zoneId)} where {@code zoneId} is a valid
1651     * {@link ZoneId}.
1652     * </p>
1653     *
1654     * @param file            the {@link File} of which the modification date must be compared.
1655     * @param chronoLocalDate the date reference.
1656     * @param localTime       the time reference.
1657     * @return true if the {@link File} exists and has been modified after the given
1658     * {@link ChronoLocalDate} at the given time.
1659     * @throws UncheckedIOException if an I/O error occurs
1660     * @throws NullPointerException if the file, local date or zone ID is {@code null}.
1661     * @since 2.8.0
1662     */
1663    public static boolean isFileNewer(final File file, final ChronoLocalDate chronoLocalDate, final LocalTime localTime) {
1664        Objects.requireNonNull(chronoLocalDate, "chronoLocalDate");
1665        Objects.requireNonNull(localTime, "localTime");
1666        return isFileNewer(file, chronoLocalDate.atTime(localTime));
1667    }
1668
1669    /**
1670     * Tests if the specified {@link File} is newer than the specified {@link ChronoLocalDate} at the specified
1671     * {@link OffsetTime}.
1672     *
1673     * @param file the {@link File} of which the modification date must be compared
1674     * @param chronoLocalDate the date reference
1675     * @param offsetTime the time reference
1676     * @return true if the {@link File} exists and has been modified after the given {@link ChronoLocalDate} at the given
1677     *         {@link OffsetTime}.
1678     * @throws UncheckedIOException if an I/O error occurs
1679     * @throws NullPointerException if the file, local date or zone ID is {@code null}
1680     * @since 2.12.0
1681     */
1682    public static boolean isFileNewer(final File file, final ChronoLocalDate chronoLocalDate, final OffsetTime offsetTime) {
1683        Objects.requireNonNull(chronoLocalDate, "chronoLocalDate");
1684        Objects.requireNonNull(offsetTime, "offsetTime");
1685        return isFileNewer(file, chronoLocalDate.atTime(offsetTime.toLocalTime()));
1686    }
1687
1688    /**
1689     * Tests if the specified {@link File} is newer than the specified {@link ChronoLocalDateTime}
1690     * at the system-default time zone.
1691     *
1692     * <p>
1693     * Note: The input date and time is assumed to be in the system default time-zone. To use a
1694     * non-default time-zone use the method {@link #isFileNewer(File, ChronoLocalDateTime, ZoneId)
1695     * isFileNewer(file, chronoLocalDateTime, zoneId)} where {@code zoneId} is a valid
1696     * {@link ZoneId}.
1697     * </p>
1698     *
1699     * @param file                the {@link File} of which the modification date must be compared.
1700     * @param chronoLocalDateTime the date reference.
1701     * @return true if the {@link File} exists and has been modified after the given
1702     * {@link ChronoLocalDateTime} at the system-default time zone.
1703     * @throws UncheckedIOException if an I/O error occurs
1704     * @throws NullPointerException if the file or local date time is {@code null}.
1705     * @since 2.8.0
1706     */
1707    public static boolean isFileNewer(final File file, final ChronoLocalDateTime<?> chronoLocalDateTime) {
1708        return isFileNewer(file, chronoLocalDateTime, ZoneId.systemDefault());
1709    }
1710
1711    /**
1712     * Tests if the specified {@link File} is newer than the specified {@link ChronoLocalDateTime}
1713     * at the specified {@link ZoneId}.
1714     *
1715     * @param file                the {@link File} of which the modification date must be compared.
1716     * @param chronoLocalDateTime the date reference.
1717     * @param zoneId              the time zone.
1718     * @return true if the {@link File} exists and has been modified after the given
1719     * {@link ChronoLocalDateTime} at the given {@link ZoneId}.
1720     * @throws UncheckedIOException if an I/O error occurs
1721     * @throws NullPointerException if the file, local date time or zone ID is {@code null}.
1722     * @since 2.8.0
1723     */
1724    public static boolean isFileNewer(final File file, final ChronoLocalDateTime<?> chronoLocalDateTime, final ZoneId zoneId) {
1725        Objects.requireNonNull(chronoLocalDateTime, "chronoLocalDateTime");
1726        Objects.requireNonNull(zoneId, "zoneId");
1727        return isFileNewer(file, chronoLocalDateTime.atZone(zoneId));
1728    }
1729
1730    /**
1731     * Tests if the specified {@link File} is newer than the specified {@link ChronoZonedDateTime}.
1732     *
1733     * @param file                the {@link File} of which the modification date must be compared.
1734     * @param chronoZonedDateTime the date reference.
1735     * @return true if the {@link File} exists and has been modified after the given
1736     * {@link ChronoZonedDateTime}.
1737     * @throws NullPointerException if the file or zoned date time is {@code null}.
1738     * @throws UncheckedIOException if an I/O error occurs
1739     * @since 2.8.0
1740     */
1741    public static boolean isFileNewer(final File file, final ChronoZonedDateTime<?> chronoZonedDateTime) {
1742        Objects.requireNonNull(file, PROTOCOL_FILE);
1743        Objects.requireNonNull(chronoZonedDateTime, "chronoZonedDateTime");
1744        return Uncheck.getAsBoolean(() -> PathUtils.isNewer(file.toPath(), chronoZonedDateTime));
1745    }
1746
1747    /**
1748     * Tests if the specified {@link File} is newer than the specified {@link Date}.
1749     *
1750     * @param file the {@link File} of which the modification date must be compared.
1751     * @param date the date reference.
1752     * @return true if the {@link File} exists and has been modified
1753     * after the given {@link Date}.
1754     * @throws UncheckedIOException if an I/O error occurs
1755     * @throws NullPointerException if the file or date is {@code null}.
1756     */
1757    public static boolean isFileNewer(final File file, final Date date) {
1758        Objects.requireNonNull(date, "date");
1759        return isFileNewer(file, date.getTime());
1760    }
1761
1762    /**
1763     * Tests if the specified {@link File} is newer than the reference {@link File}.
1764     *
1765     * @param file      the {@link File} of which the modification date must be compared.
1766     * @param reference the {@link File} of which the modification date is used.
1767     * @return true if the {@link File} exists and has been modified more
1768     * recently than the reference {@link File}.
1769     * @throws NullPointerException if the file or reference file is {@code null}.
1770     * @throws UncheckedIOException if the reference file doesn't exist.
1771     */
1772    public static boolean isFileNewer(final File file, final File reference) {
1773        return Uncheck.getAsBoolean(() -> PathUtils.isNewer(file.toPath(), reference.toPath()));
1774    }
1775
1776    /**
1777     * Tests if the specified {@link File} is newer than the specified {@link FileTime}.
1778     *
1779     * @param file the {@link File} of which the modification date must be compared.
1780     * @param fileTime the file time reference.
1781     * @return true if the {@link File} exists and has been modified after the given {@link FileTime}.
1782     * @throws IOException if an I/O error occurs.
1783     * @throws NullPointerException if the file or local date is {@code null}.
1784     * @since 2.12.0
1785     */
1786    public static boolean isFileNewer(final File file, final FileTime fileTime) throws IOException {
1787        Objects.requireNonNull(file, PROTOCOL_FILE);
1788        return PathUtils.isNewer(file.toPath(), fileTime);
1789    }
1790
1791    /**
1792     * Tests if the specified {@link File} is newer than the specified {@link Instant}.
1793     *
1794     * @param file the {@link File} of which the modification date must be compared.
1795     * @param instant the date reference.
1796     * @return true if the {@link File} exists and has been modified after the given {@link Instant}.
1797     * @throws NullPointerException if the file or instant is {@code null}.
1798     * @throws UncheckedIOException if an I/O error occurs
1799     * @since 2.8.0
1800     */
1801    public static boolean isFileNewer(final File file, final Instant instant) {
1802        Objects.requireNonNull(instant, "instant");
1803        return Uncheck.getAsBoolean(() -> PathUtils.isNewer(file.toPath(), instant));
1804    }
1805
1806    /**
1807     * Tests if the specified {@link File} is newer than the specified time reference.
1808     *
1809     * @param file       the {@link File} of which the modification date must be compared.
1810     * @param timeMillis the time reference measured in milliseconds since the
1811     *                   epoch (00:00:00 GMT, January 1, 1970).
1812     * @return true if the {@link File} exists and has been modified after the given time reference.
1813     * @throws UncheckedIOException if an I/O error occurs
1814     * @throws NullPointerException if the file is {@code null}.
1815     */
1816    public static boolean isFileNewer(final File file, final long timeMillis) {
1817        Objects.requireNonNull(file, PROTOCOL_FILE);
1818        return Uncheck.getAsBoolean(() -> PathUtils.isNewer(file.toPath(), timeMillis));
1819    }
1820
1821    /**
1822     * Tests if the specified {@link File} is newer than the specified {@link OffsetDateTime}.
1823     *
1824     * @param file the {@link File} of which the modification date must be compared
1825     * @param offsetDateTime the date reference
1826     * @return true if the {@link File} exists and has been modified before the given {@link OffsetDateTime}.
1827     * @throws UncheckedIOException if an I/O error occurs
1828     * @throws NullPointerException if the file or zoned date time is {@code null}
1829     * @since 2.12.0
1830     */
1831    public static boolean isFileNewer(final File file, final OffsetDateTime offsetDateTime) {
1832        Objects.requireNonNull(offsetDateTime, "offsetDateTime");
1833        return isFileNewer(file, offsetDateTime.toInstant());
1834    }
1835
1836    /**
1837     * Tests if the specified {@link File} is older than the specified {@link ChronoLocalDate}
1838     * at the end of day.
1839     *
1840     * <p>
1841     * Note: The input date is assumed to be in the system default time-zone with the time
1842     * part set to the current time. To use a non-default time-zone use the method
1843     * {@link #isFileOlder(File, ChronoLocalDateTime, ZoneId)
1844     * isFileOlder(file, chronoLocalDate.atTime(LocalTime.now(zoneId)), zoneId)} where
1845     * {@code zoneId} is a valid {@link ZoneId}.
1846     * </p>
1847     *
1848     * @param file            the {@link File} of which the modification date must be compared.
1849     * @param chronoLocalDate the date reference.
1850     * @return true if the {@link File} exists and has been modified before the given
1851     * {@link ChronoLocalDate} at the current time.
1852     * @throws NullPointerException if the file or local date is {@code null}.
1853     * @throws UncheckedIOException if an I/O error occurs
1854     * @see ZoneId#systemDefault()
1855     * @see LocalTime#now()
1856     * @since 2.8.0
1857     */
1858    public static boolean isFileOlder(final File file, final ChronoLocalDate chronoLocalDate) {
1859        return isFileOlder(file, chronoLocalDate, LocalTime.MAX);
1860    }
1861
1862    /**
1863     * Tests if the specified {@link File} is older than the specified {@link ChronoLocalDate}
1864     * at the specified {@link LocalTime}.
1865     *
1866     * <p>
1867     * Note: The input date and time are assumed to be in the system default time-zone. To use a
1868     * non-default time-zone use the method {@link #isFileOlder(File, ChronoLocalDateTime, ZoneId)
1869     * isFileOlder(file, chronoLocalDate.atTime(localTime), zoneId)} where {@code zoneId} is a valid
1870     * {@link ZoneId}.
1871     * </p>
1872     *
1873     * @param file            the {@link File} of which the modification date must be compared.
1874     * @param chronoLocalDate the date reference.
1875     * @param localTime       the time reference.
1876     * @return true if the {@link File} exists and has been modified before the
1877     * given {@link ChronoLocalDate} at the specified time.
1878     * @throws UncheckedIOException if an I/O error occurs
1879     * @throws NullPointerException if the file, local date or local time is {@code null}.
1880     * @see ZoneId#systemDefault()
1881     * @since 2.8.0
1882     */
1883    public static boolean isFileOlder(final File file, final ChronoLocalDate chronoLocalDate, final LocalTime localTime) {
1884        Objects.requireNonNull(chronoLocalDate, "chronoLocalDate");
1885        Objects.requireNonNull(localTime, "localTime");
1886        return isFileOlder(file, chronoLocalDate.atTime(localTime));
1887    }
1888
1889    /**
1890     * Tests if the specified {@link File} is older than the specified {@link ChronoLocalDate} at the specified
1891     * {@link OffsetTime}.
1892     *
1893     * @param file the {@link File} of which the modification date must be compared
1894     * @param chronoLocalDate the date reference
1895     * @param offsetTime the time reference
1896     * @return true if the {@link File} exists and has been modified after the given {@link ChronoLocalDate} at the given
1897     *         {@link OffsetTime}.
1898     * @throws NullPointerException if the file, local date or zone ID is {@code null}
1899     * @throws UncheckedIOException if an I/O error occurs
1900     * @since 2.12.0
1901     */
1902    public static boolean isFileOlder(final File file, final ChronoLocalDate chronoLocalDate, final OffsetTime offsetTime) {
1903        Objects.requireNonNull(chronoLocalDate, "chronoLocalDate");
1904        Objects.requireNonNull(offsetTime, "offsetTime");
1905        return isFileOlder(file, chronoLocalDate.atTime(offsetTime.toLocalTime()));
1906    }
1907
1908    /**
1909     * Tests if the specified {@link File} is older than the specified {@link ChronoLocalDateTime}
1910     * at the system-default time zone.
1911     *
1912     * <p>
1913     * Note: The input date and time is assumed to be in the system default time-zone. To use a
1914     * non-default time-zone use the method {@link #isFileOlder(File, ChronoLocalDateTime, ZoneId)
1915     * isFileOlder(file, chronoLocalDateTime, zoneId)} where {@code zoneId} is a valid
1916     * {@link ZoneId}.
1917     * </p>
1918     *
1919     * @param file                the {@link File} of which the modification date must be compared.
1920     * @param chronoLocalDateTime the date reference.
1921     * @return true if the {@link File} exists and has been modified before the given
1922     * {@link ChronoLocalDateTime} at the system-default time zone.
1923     * @throws NullPointerException if the file or local date time is {@code null}.
1924     * @throws UncheckedIOException if an I/O error occurs
1925     * @see ZoneId#systemDefault()
1926     * @since 2.8.0
1927     */
1928    public static boolean isFileOlder(final File file, final ChronoLocalDateTime<?> chronoLocalDateTime) {
1929        return isFileOlder(file, chronoLocalDateTime, ZoneId.systemDefault());
1930    }
1931
1932    /**
1933     * Tests if the specified {@link File} is older than the specified {@link ChronoLocalDateTime}
1934     * at the specified {@link ZoneId}.
1935     *
1936     * @param file          the {@link File} of which the modification date must be compared.
1937     * @param chronoLocalDateTime the date reference.
1938     * @param zoneId        the time zone.
1939     * @return true if the {@link File} exists and has been modified before the given
1940     * {@link ChronoLocalDateTime} at the given {@link ZoneId}.
1941     * @throws NullPointerException if the file, local date time or zone ID is {@code null}.
1942     * @throws UncheckedIOException if an I/O error occurs
1943     * @since 2.8.0
1944     */
1945    public static boolean isFileOlder(final File file, final ChronoLocalDateTime<?> chronoLocalDateTime, final ZoneId zoneId) {
1946        Objects.requireNonNull(chronoLocalDateTime, "chronoLocalDateTime");
1947        Objects.requireNonNull(zoneId, "zoneId");
1948        return isFileOlder(file, chronoLocalDateTime.atZone(zoneId));
1949    }
1950
1951    /**
1952     * Tests if the specified {@link File} is older than the specified {@link ChronoZonedDateTime}.
1953     *
1954     * @param file                the {@link File} of which the modification date must be compared.
1955     * @param chronoZonedDateTime the date reference.
1956     * @return true if the {@link File} exists and has been modified before the given
1957     * {@link ChronoZonedDateTime}.
1958     * @throws NullPointerException if the file or zoned date time is {@code null}.
1959     * @throws UncheckedIOException if an I/O error occurs
1960     * @since 2.8.0
1961     */
1962    public static boolean isFileOlder(final File file, final ChronoZonedDateTime<?> chronoZonedDateTime) {
1963        Objects.requireNonNull(chronoZonedDateTime, "chronoZonedDateTime");
1964        return isFileOlder(file, chronoZonedDateTime.toInstant());
1965    }
1966
1967    /**
1968     * Tests if the specified {@link File} is older than the specified {@link Date}.
1969     *
1970     * @param file the {@link File} of which the modification date must be compared.
1971     * @param date the date reference.
1972     * @return true if the {@link File} exists and has been modified before the given {@link Date}.
1973     * @throws NullPointerException if the file or date is {@code null}.
1974     * @throws UncheckedIOException if an I/O error occurs
1975     */
1976    public static boolean isFileOlder(final File file, final Date date) {
1977        Objects.requireNonNull(date, "date");
1978        return isFileOlder(file, date.getTime());
1979    }
1980
1981    /**
1982     * Tests if the specified {@link File} is older than the reference {@link File}.
1983     *
1984     * @param file      the {@link File} of which the modification date must be compared.
1985     * @param reference the {@link File} of which the modification date is used.
1986     * @return true if the {@link File} exists and has been modified before the reference {@link File}.
1987     * @throws NullPointerException if the file or reference file is {@code null}.
1988     * @throws FileNotFoundException if the reference file doesn't exist.
1989     * @throws UncheckedIOException if an I/O error occurs
1990     */
1991    public static boolean isFileOlder(final File file, final File reference) throws FileNotFoundException {
1992        return Uncheck.getAsBoolean(() -> PathUtils.isOlder(file.toPath(), reference.toPath()));
1993    }
1994
1995    /**
1996     * Tests if the specified {@link File} is older than the specified {@link FileTime}.
1997     *
1998     * @param file the {@link File} of which the modification date must be compared.
1999     * @param fileTime the file time reference.
2000     * @return true if the {@link File} exists and has been modified before the given {@link FileTime}.
2001     * @throws IOException if an I/O error occurs.
2002     * @throws NullPointerException if the file or local date is {@code null}.
2003     * @since 2.12.0
2004     */
2005    public static boolean isFileOlder(final File file, final FileTime fileTime) throws IOException {
2006        Objects.requireNonNull(file, PROTOCOL_FILE);
2007        return PathUtils.isOlder(file.toPath(), fileTime);
2008    }
2009
2010    /**
2011     * Tests if the specified {@link File} is older than the specified {@link Instant}.
2012     *
2013     * @param file    the {@link File} of which the modification date must be compared.
2014     * @param instant the date reference.
2015     * @return true if the {@link File} exists and has been modified before the given {@link Instant}.
2016     * @throws NullPointerException if the file or instant is {@code null}.
2017     * @since 2.8.0
2018     */
2019    public static boolean isFileOlder(final File file, final Instant instant) {
2020        Objects.requireNonNull(instant, "instant");
2021        return Uncheck.getAsBoolean(() -> PathUtils.isOlder(file.toPath(), instant));
2022    }
2023
2024    /**
2025     * Tests if the specified {@link File} is older than the specified time reference.
2026     *
2027     * @param file       the {@link File} of which the modification date must be compared.
2028     * @param timeMillis the time reference measured in milliseconds since the
2029     *                   epoch (00:00:00 GMT, January 1, 1970).
2030     * @return true if the {@link File} exists and has been modified before the given time reference.
2031     * @throws NullPointerException if the file is {@code null}.
2032     * @throws UncheckedIOException if an I/O error occurs
2033     */
2034    public static boolean isFileOlder(final File file, final long timeMillis) {
2035        Objects.requireNonNull(file, PROTOCOL_FILE);
2036        return Uncheck.getAsBoolean(() -> PathUtils.isOlder(file.toPath(), timeMillis));
2037    }
2038
2039    /**
2040     * Tests if the specified {@link File} is older than the specified {@link OffsetDateTime}.
2041     *
2042     * @param file the {@link File} of which the modification date must be compared
2043     * @param offsetDateTime the date reference
2044     * @return true if the {@link File} exists and has been modified before the given {@link OffsetDateTime}.
2045     * @throws NullPointerException if the file or zoned date time is {@code null}
2046     * @since 2.12.0
2047     */
2048    public static boolean isFileOlder(final File file, final OffsetDateTime offsetDateTime) {
2049        Objects.requireNonNull(offsetDateTime, "offsetDateTime");
2050        return isFileOlder(file, offsetDateTime.toInstant());
2051    }
2052
2053    /**
2054     * Tests whether the given URL is a file URL.
2055     *
2056     * @param url The URL to test.
2057     * @return Whether the given URL is a file URL.
2058     */
2059    private static boolean isFileProtocol(final URL url) {
2060        return PROTOCOL_FILE.equalsIgnoreCase(url.getProtocol());
2061    }
2062
2063    /**
2064     * Tests whether the specified {@link File} is a regular file or not. Implemented as a
2065     * null-safe delegate to {@link Files#isRegularFile(Path path, LinkOption... options)}.
2066     *
2067     * @param   file the path to the file.
2068     * @param   options options indicating how symbolic links are handled
2069     * @return  {@code true} if the file is a regular file; {@code false} if
2070     *          the path is null, the file does not exist, is not a regular file, or it cannot
2071     *          be determined if the file is a regular file or not.
2072     * @throws SecurityException     In the case of the default provider, and a security manager is installed, the
2073     *                               {@link SecurityManager#checkRead(String) checkRead} method is invoked to check read
2074     *                               access to the directory.
2075     * @since 2.9.0
2076     */
2077    public static boolean isRegularFile(final File file, final LinkOption... options) {
2078        return file != null && Files.isRegularFile(file.toPath(), options);
2079    }
2080
2081    /**
2082     * Tests whether the specified file is a symbolic link rather than an actual file.
2083     * <p>
2084     * This method delegates to {@link Files#isSymbolicLink(Path path)}
2085     * </p>
2086     *
2087     * @param file the file to test, may be null.
2088     * @return true if the file is a symbolic link, see {@link Files#isSymbolicLink(Path path)}.
2089     * @since 2.0
2090     * @see Files#isSymbolicLink(Path)
2091     */
2092    public static boolean isSymlink(final File file) {
2093        return file != null && Files.isSymbolicLink(file.toPath());
2094    }
2095
2096    /**
2097     * Iterates over the files in given directory (and optionally
2098     * its subdirectories).
2099     * <p>
2100     * The resulting iterator MUST be consumed in its entirety in order to close its underlying stream.
2101     * </p>
2102     * <p>
2103     * All files found are filtered by an IOFileFilter.
2104     * </p>
2105     *
2106     * @param directory  The directory to search.
2107     * @param fileFilter filter to apply when finding files.
2108     * @param dirFilter  optional filter to apply when finding subdirectories.
2109     *                   If this parameter is {@code null}, subdirectories will not be included in the
2110     *                   search. Use TrueFileFilter.INSTANCE to match all directories.
2111     * @return an iterator of {@link File} for the matching files.
2112     * @see org.apache.commons.io.filefilter.FileFilterUtils
2113     * @see org.apache.commons.io.filefilter.NameFileFilter
2114     * @since 1.2
2115     */
2116    public static Iterator<File> iterateFiles(final File directory, final IOFileFilter fileFilter, final IOFileFilter dirFilter) {
2117        return listFiles(directory, fileFilter, dirFilter).iterator();
2118    }
2119
2120    /**
2121     * Iterates over the files in a given directory (and optionally
2122     * its subdirectories) which match an array of extensions.
2123     * <p>
2124     * The resulting iterator MUST be consumed in its entirety in order to close its underlying stream.
2125     * </p>
2126     *
2127     * @param directory  The directory to search.
2128     * @param extensions an array of extensions, for example, <code>{"java", "xml"}</code>. If this
2129     *                   parameter is {@code null}, all files are returned.
2130     * @param recursive  if true all subdirectories are searched as well.
2131     * @return an iterator of {@link File} with the matching files.
2132     * @since 1.2
2133     */
2134    public static Iterator<File> iterateFiles(final File directory, final String[] extensions, final boolean recursive) {
2135        return StreamIterator.iterator(Uncheck.get(() -> streamFiles(directory, recursive, extensions)));
2136    }
2137
2138    /**
2139     * Iterates over the files in given directory (and optionally
2140     * its subdirectories).
2141     * <p>
2142     * The resulting iterator MUST be consumed in its entirety in order to close its underlying stream.
2143     * </p>
2144     * <p>
2145     * All files found are filtered by an IOFileFilter.
2146     * </p>
2147     * <p>
2148     * The resulting iterator includes the subdirectories themselves.
2149     * </p>
2150     *
2151     * @param directory  The directory to search.
2152     * @param fileFilter filter to apply when finding files.
2153     * @param dirFilter  optional filter to apply when finding subdirectories.
2154     *                   If this parameter is {@code null}, subdirectories will not be included in the
2155     *                   search. Use TrueFileFilter.INSTANCE to match all directories.
2156     * @return an iterator of {@link File} for the matching files.
2157     * @see org.apache.commons.io.filefilter.FileFilterUtils
2158     * @see org.apache.commons.io.filefilter.NameFileFilter
2159     * @since 2.2
2160     */
2161    public static Iterator<File> iterateFilesAndDirs(final File directory, final IOFileFilter fileFilter, final IOFileFilter dirFilter) {
2162        return listFilesAndDirs(directory, fileFilter, dirFilter).iterator();
2163    }
2164
2165    /**
2166     * Returns the last modification time in milliseconds via
2167     * {@link java.nio.file.Files#getLastModifiedTime(Path, LinkOption...)}.
2168     * <p>
2169     * For the best precision, use {@link #lastModifiedFileTime(File)}.
2170     * </p>
2171     * <p>
2172     * Use this method to avoid issues with {@link File#lastModified()} like
2173     * <a href="https://bugs.openjdk.java.net/browse/JDK-8177809">JDK-8177809</a> where {@link File#lastModified()} is
2174     * losing milliseconds (always ends in 000). This bug exists in OpenJDK 8 and 9, and is fixed in 10.
2175     * </p>
2176     *
2177     * @param file The File to query.
2178     * @return See {@link java.nio.file.attribute.FileTime#toMillis()}.
2179     * @throws IOException if an I/O error occurs.
2180     * @since 2.9.0
2181     */
2182    public static long lastModified(final File file) throws IOException {
2183        // https://bugs.openjdk.java.net/browse/JDK-8177809
2184        // File.lastModified() is losing milliseconds (always ends in 000)
2185        // This bug is in OpenJDK 8 and 9, and fixed in 10.
2186        return lastModifiedFileTime(file).toMillis();
2187    }
2188
2189    /**
2190     * Returns the last modification {@link FileTime} via
2191     * {@link java.nio.file.Files#getLastModifiedTime(Path, LinkOption...)}.
2192     * <p>
2193     * Use this method to avoid issues with {@link File#lastModified()} like
2194     * <a href="https://bugs.openjdk.java.net/browse/JDK-8177809">JDK-8177809</a> where {@link File#lastModified()} is
2195     * losing milliseconds (always ends in 000). This bug exists in OpenJDK 8 and 9, and is fixed in 10.
2196     * </p>
2197     *
2198     * @param file The File to query.
2199     * @return See {@link java.nio.file.Files#getLastModifiedTime(Path, LinkOption...)}.
2200     * @throws IOException if an I/O error occurs.
2201     * @since 2.12.0
2202     */
2203    public static FileTime lastModifiedFileTime(final File file) throws IOException {
2204        // https://bugs.openjdk.java.net/browse/JDK-8177809
2205        // File.lastModified() is losing milliseconds (always ends in 000)
2206        // This bug is in OpenJDK 8 and 9, and fixed in 10.
2207        return Files.getLastModifiedTime(Objects.requireNonNull(file, PROTOCOL_FILE).toPath());
2208    }
2209
2210    /**
2211     * Returns the last modification time in milliseconds via
2212     * {@link java.nio.file.Files#getLastModifiedTime(Path, LinkOption...)}.
2213     * <p>
2214     * For the best precision, use {@link #lastModifiedFileTime(File)}.
2215     * </p>
2216     * <p>
2217     * Use this method to avoid issues with {@link File#lastModified()} like
2218     * <a href="https://bugs.openjdk.java.net/browse/JDK-8177809">JDK-8177809</a> where {@link File#lastModified()} is
2219     * losing milliseconds (always ends in 000). This bug exists in OpenJDK 8 and 9, and is fixed in 10.
2220     * </p>
2221     *
2222     * @param file The File to query.
2223     * @return See {@link java.nio.file.attribute.FileTime#toMillis()}.
2224     * @throws UncheckedIOException if an I/O error occurs.
2225     * @since 2.9.0
2226     */
2227    public static long lastModifiedUnchecked(final File file) {
2228        // https://bugs.openjdk.java.net/browse/JDK-8177809
2229        // File.lastModified() is losing milliseconds (always ends in 000)
2230        // This bug is in OpenJDK 8 and 9, and fixed in 10.
2231        return Uncheck.apply(FileUtils::lastModified, file);
2232    }
2233
2234    /**
2235     * Returns an Iterator for the lines in a {@link File} using the default encoding for the VM.
2236     *
2237     * @param file the file to open for input, must not be {@code null}.
2238     * @return an Iterator of the lines in the file, never {@code null}.
2239     * @throws NullPointerException if file is {@code null}.
2240     * @throws FileNotFoundException if the file does not exist, is a directory rather than a regular file, or for some
2241     *         other reason cannot be opened for reading.
2242     * @throws IOException if an I/O error occurs.
2243     * @see #lineIterator(File, String)
2244     * @since 1.3
2245     */
2246    public static LineIterator lineIterator(final File file) throws IOException {
2247        return lineIterator(file, null);
2248    }
2249
2250    /**
2251     * Returns an Iterator for the lines in a {@link File}.
2252     * <p>
2253     * This method opens an {@link InputStream} for the file.
2254     * When you have finished with the iterator you should close the stream
2255     * to free internal resources. This can be done by using a try-with-resources block or calling the
2256     * {@link LineIterator#close()} method.
2257     * </p>
2258     * <p>
2259     * The recommended usage pattern is:
2260     * </p>
2261     * <pre>
2262     * LineIterator it = FileUtils.lineIterator(file, StandardCharsets.UTF_8.name());
2263     * try {
2264     *   while (it.hasNext()) {
2265     *     String line = it.nextLine();
2266     *     // do something with line
2267     *   }
2268     * } finally {
2269     *   LineIterator.closeQuietly(iterator);
2270     * }
2271     * </pre>
2272     * <p>
2273     * If an exception occurs during the creation of the iterator, the
2274     * underlying stream is closed.
2275     * </p>
2276     *
2277     * @param file     the file to open for input, must not be {@code null}.
2278     * @param charsetName the name of the requested charset, {@code null} means platform default.
2279     * @return a LineIterator for lines in the file, never {@code null}; MUST be closed by the caller.
2280     * @throws NullPointerException if file is {@code null}.
2281     * @throws FileNotFoundException if the file does not exist, is a directory rather than a regular file, or for some
2282     *         other reason cannot be opened for reading.
2283     * @throws IOException if an I/O error occurs.
2284     * @since 1.2
2285     */
2286    @SuppressWarnings("resource") // Caller closes the result LineIterator.
2287    public static LineIterator lineIterator(final File file, final String charsetName) throws IOException {
2288        InputStream inputStream = null;
2289        try {
2290            inputStream = Files.newInputStream(file.toPath());
2291            return IOUtils.lineIterator(inputStream, charsetName);
2292        } catch (final IOException | RuntimeException ex) {
2293            IOUtils.closeQuietly(inputStream, ex::addSuppressed);
2294            throw ex;
2295        }
2296    }
2297
2298    private static AccumulatorPathVisitor listAccumulate(final File directory, final IOFileFilter fileFilter, final IOFileFilter dirFilter,
2299            final FileVisitOption... options) throws IOException {
2300        final boolean isDirFilterSet = dirFilter != null;
2301        final FileEqualsFileFilter rootDirFilter = new FileEqualsFileFilter(directory);
2302        final PathFilter dirPathFilter = isDirFilterSet ? rootDirFilter.or(dirFilter) : rootDirFilter;
2303        // @formatter:off
2304        final AccumulatorPathVisitor visitor = AccumulatorPathVisitor.builder()
2305                .setPathCounters(Counters.noopPathCounters())
2306                .setFileFilter(fileFilter)
2307                .setDirectoryFilter(dirPathFilter)
2308                .setVisitFileFailedFunction((p, e) -> FileVisitResult.CONTINUE)
2309                .get();
2310        // @formatter:on
2311        final Set<FileVisitOption> optionSet = new HashSet<>();
2312        if (options != null) {
2313            Collections.addAll(optionSet, options);
2314        }
2315        Files.walkFileTree(directory.toPath(), optionSet, toMaxDepth(isDirFilterSet), visitor);
2316        return visitor;
2317    }
2318
2319    /**
2320     * Lists files in a directory, asserting that the supplied directory exists and is a directory.
2321     *
2322     * @param directory  The directory to list.
2323     * @param fileFilter Optional file filter, may be null.
2324     * @return The files in the directory, never {@code null}.
2325     * @throws NullPointerException     if the {@code directory} is {@code null}.
2326     * @throws IllegalArgumentException if the {@code directory} exists but is not a directory.
2327     * @throws IOException              if an I/O error occurs per {@link File#listFiles()} and {@link File#listFiles(FileFilter)}.
2328     * @throws SecurityException        If a security manager exists and its {@link SecurityManager#checkRead(String)} method denies read access to the
2329     *                                  directory.
2330     */
2331    private static File[] listFiles(final File directory, final FileFilter fileFilter) throws IOException {
2332        requireDirectoryExists(directory, "directory");
2333        final File[] files = directory.listFiles(fileFilter);
2334        if (files == null) {
2335            // null if the directory does not denote a directory, or if an I/O error occurs.
2336            throw new IOException("Unknown I/O error listing contents of directory: " + directory);
2337        }
2338        return files;
2339    }
2340
2341    /**
2342     * Finds files within a given directory (and optionally its
2343     * subdirectories). All files found are filtered by an IOFileFilter.
2344     * <p>
2345     * If your search should recurse into subdirectories you can pass in
2346     * an IOFileFilter for directories. You don't need to bind a
2347     * DirectoryFileFilter (via logical AND) to this filter. This method does
2348     * that for you.
2349     * </p>
2350     * <p>
2351     * An example: If you want to search through all directories called
2352     * "temp" you pass in {@code FileFilterUtils.NameFileFilter("temp")}.
2353     * </p>
2354     * <p>
2355     * Another common usage of this method is find files in a directory
2356     * tree but ignoring the directories generated CVS. You can simply pass
2357     * in {@code FileFilterUtils.makeCVSAware(null)}.
2358     * </p>
2359     *
2360     * @param directory  The directory to search.
2361     * @param fileFilter filter to apply when finding files. Must not be {@code null},
2362     *                   use {@link TrueFileFilter#INSTANCE} to match all files in selected directories.
2363     * @param dirFilter  optional filter to apply when finding subdirectories.
2364     *                   If this parameter is {@code null}, subdirectories will not be included in the
2365     *                   search. Use {@link TrueFileFilter#INSTANCE} to match all directories.
2366     * @return a collection of {@link File} with the matching files.
2367     * @see org.apache.commons.io.filefilter.FileFilterUtils
2368     * @see org.apache.commons.io.filefilter.NameFileFilter
2369     */
2370    public static Collection<File> listFiles(final File directory, final IOFileFilter fileFilter, final IOFileFilter dirFilter) {
2371        final AccumulatorPathVisitor visitor = Uncheck
2372            .apply(d -> listAccumulate(d, FileFileFilter.INSTANCE.and(fileFilter), dirFilter, FileVisitOption.FOLLOW_LINKS), directory);
2373        return toList(visitor.getFileList().stream().map(Path::toFile));
2374    }
2375
2376    /**
2377     * Lists Files in the given {@code directory}, adding each file to the given list.
2378     *
2379     * @param directory A File for an assumed directory, not null.
2380     * @param files The list to add found Files, not null.
2381     * @param recursive Whether or not to recurse into subdirectories.
2382     * @param filter How to filter files, not null.
2383     * @return The given list.
2384     */
2385    @SuppressWarnings("null")
2386    private static List<File> listFiles(final File directory, final List<File> files, final boolean recursive, final FilenameFilter filter) {
2387        final File[] listFiles = directory.listFiles();
2388        if (listFiles != null) {
2389            // Only allocate if you must.
2390            final List<File> dirs = recursive ? new ArrayList<>() : null;
2391            Arrays.stream(listFiles).forEach(f -> {
2392                if (recursive && f.isDirectory()) {
2393                    dirs.add(f);
2394                } else if (f.isFile() && filter.accept(directory, f.getName())) {
2395                    files.add(f);
2396                }
2397            });
2398            if (recursive) {
2399                dirs.forEach(d -> listFiles(d, files, true, filter));
2400            }
2401        }
2402        return files;
2403    }
2404
2405    /**
2406     * Lists files within a given directory (and optionally its subdirectories)
2407     * which match an array of extensions.
2408     *
2409     * @param directory  The directory to search.
2410     * @param extensions an array of extensions, for example, <code>{"java", "xml"}</code>. If this
2411     *                   parameter is {@code null}, all files are returned.
2412     * @param recursive  if true all subdirectories are searched as well.
2413     * @return a collection of {@link File} with the matching files.
2414     */
2415    public static Collection<File> listFiles(final File directory, final String[] extensions, final boolean recursive) {
2416        return listFiles(directory, new ArrayList<>(), recursive, extensions != null ? toSuffixFileFilter(extensions) : TrueFileFilter.INSTANCE);
2417    }
2418
2419    /**
2420     * Finds files within a given directory (and optionally its
2421     * subdirectories). All files found are filtered by an IOFileFilter.
2422     * <p>
2423     * The resulting collection includes the starting directory and
2424     * any subdirectories that match the directory filter.
2425     * </p>
2426     *
2427     * @param directory  The directory to search.
2428     * @param fileFilter filter to apply when finding files.
2429     * @param dirFilter  optional filter to apply when finding subdirectories.
2430     *                   If this parameter is {@code null}, subdirectories will not be included in the
2431     *                   search. Use TrueFileFilter.INSTANCE to match all directories.
2432     * @return a collection of {@link File} with the matching files.
2433     * @see org.apache.commons.io.FileUtils#listFiles
2434     * @see org.apache.commons.io.filefilter.FileFilterUtils
2435     * @see org.apache.commons.io.filefilter.NameFileFilter
2436     * @since 2.2
2437     */
2438    public static Collection<File> listFilesAndDirs(final File directory, final IOFileFilter fileFilter, final IOFileFilter dirFilter) {
2439        final AccumulatorPathVisitor visitor = Uncheck.apply(d -> listAccumulate(d, fileFilter, dirFilter, FileVisitOption.FOLLOW_LINKS),
2440            directory);
2441        final List<Path> list = visitor.getFileList();
2442        list.addAll(visitor.getDirList());
2443        return toList(list.stream().map(Path::toFile));
2444    }
2445
2446    /**
2447     * Calls {@link File#mkdirs()} and throws an {@link IOException} on failure.
2448     * <p>
2449     * Creates all directories for a File object, including any necessary but non-existent parent directories. If the {@code directory} already exists or is
2450     * null, nothing happens.
2451     * </p>
2452     *
2453     * @param directory the receiver for {@code mkdirs()}. If the {@code directory} already exists or is null, nothing happens.
2454     * @return the given directory.
2455     * @throws IOException       if the directory was not created along with all its parent directories.
2456     * @throws IOException       if the given file object is not a directory.
2457     * @throws SecurityException See {@link File#mkdirs()}.
2458     * @see File#mkdirs()
2459     */
2460    private static File mkdirs(final File directory) throws IOException {
2461        if (directory != null && !directory.mkdirs() && !directory.isDirectory()) {
2462            throw new IOException("Cannot create directory '" + directory + "'.");
2463        }
2464        return directory;
2465    }
2466
2467    /**
2468     * Moves a directory.
2469     * <p>
2470     * When the destination directory is on another file system, do a "copy and delete".
2471     * </p>
2472     *
2473     * @param srcDir the directory to be moved.
2474     * @param destDir the destination directory.
2475     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
2476     * @throws IllegalArgumentException if {@code srcDir} exists but is not a directory.
2477     * @throws FileNotFoundException if the source does not exist.
2478     * @throws IOException if an error occurs or setting the last-modified time didn't succeed.
2479     * @since 1.4
2480     */
2481    public static void moveDirectory(final File srcDir, final File destDir) throws IOException {
2482        Objects.requireNonNull(destDir, "destination");
2483        requireDirectoryExists(srcDir, "srcDir");
2484        requireAbsent(destDir, "destDir");
2485        if (!srcDir.renameTo(destDir)) {
2486            if (destDir.getCanonicalPath().startsWith(srcDir.getCanonicalPath() + File.separator)) {
2487                throw new IOException("Cannot move directory: " + srcDir + " to a subdirectory of itself: " + destDir);
2488            }
2489            copyDirectory(srcDir, destDir);
2490            deleteDirectory(srcDir);
2491            if (srcDir.exists()) {
2492                throw new IOException("Failed to delete original directory '" + srcDir +
2493                        "' after copy to '" + destDir + "'");
2494            }
2495        }
2496    }
2497
2498    /**
2499     * Moves a directory to another directory.
2500     * <p>
2501     * If {@code createDestDir} is true, creates all destination parent directories, including any necessary but non-existent parent directories.
2502     * </p>
2503     *
2504     * @param source the directory to be moved.
2505     * @param destDir the destination file.
2506     * @param createDestDir If {@code true} create the destination directory, otherwise if {@code false} throw an
2507     *        IOException.
2508     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
2509     * @throws IllegalArgumentException if the source or destination is invalid.
2510     * @throws FileNotFoundException if the source does not exist.
2511     * @throws IOException if the directory was not created along with all its parent directories, if enabled.
2512     * @throws IOException if an error occurs or setting the last-modified time didn't succeed.
2513     * @throws SecurityException See {@link File#mkdirs()}.
2514     * @since 1.4
2515     */
2516    public static void moveDirectoryToDirectory(final File source, final File destDir, final boolean createDestDir) throws IOException {
2517        validateMoveParameters(source, destDir);
2518        if (!destDir.isDirectory()) {
2519            if (destDir.exists()) {
2520                throw new IOException("Destination '" + destDir + "' is not a directory");
2521            }
2522            if (!createDestDir) {
2523                throw new FileNotFoundException("Destination directory '" + destDir + "' does not exist [createDestDir=" + false + "]");
2524            }
2525            mkdirs(destDir);
2526        }
2527        moveDirectory(source, new File(destDir, source.getName()));
2528    }
2529
2530    /**
2531     * Moves a file preserving attributes.
2532     * <p>
2533     * Shorthand for {@code moveFile(srcFile, destFile, StandardCopyOption.COPY_ATTRIBUTES)}.
2534     * </p>
2535     * <p>
2536     * When the destination file is on another file system, do a "copy and delete".
2537     * </p>
2538     *
2539     * @param srcFile the file to be moved.
2540     * @param destFile the destination file.
2541     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
2542     * @throws FileExistsException if the destination file exists.
2543     * @throws FileNotFoundException if the source file does not exist.
2544     * @throws IllegalArgumentException if {@code srcFile} is a directory.
2545     * @throws IOException if an error occurs.
2546     * @since 1.4
2547     */
2548    public static void moveFile(final File srcFile, final File destFile) throws IOException {
2549        moveFile(srcFile, destFile, StandardCopyOption.COPY_ATTRIBUTES);
2550    }
2551
2552    /**
2553     * Moves a file.
2554     * <p>
2555     * When the destination file is on another file system, do a "copy and delete".
2556     * </p>
2557     *
2558     * @param srcFile the file to be moved.
2559     * @param destFile the destination file.
2560     * @param copyOptions Copy options.
2561     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
2562     * @throws FileExistsException if the destination file exists.
2563     * @throws FileNotFoundException if the source file does not exist.
2564     * @throws IllegalArgumentException if {@code srcFile} is a directory.
2565     * @throws IOException if an error occurs or setting the last-modified time didn't succeed.
2566     * @since 2.9.0
2567     */
2568    public static void moveFile(final File srcFile, final File destFile, final CopyOption... copyOptions) throws IOException {
2569        Objects.requireNonNull(destFile, "destFile");
2570        checkFileExists(srcFile, "srcFile");
2571        requireAbsent(destFile, "destFile");
2572        final boolean rename = srcFile.renameTo(destFile);
2573        if (!rename) {
2574            // Don't interfere with file date on move, handled by StandardCopyOption.COPY_ATTRIBUTES
2575            copyFile(srcFile, destFile, false, copyOptions);
2576            if (!srcFile.delete()) {
2577                deleteQuietly(destFile);
2578                throw new IOException("Failed to delete original file '" + srcFile + "' after copy to '" + destFile + "'");
2579            }
2580        }
2581    }
2582
2583    /**
2584     * Moves a file into a directory.
2585     * <p>
2586     * If {@code createDestDir} is true, creates all destination parent directories, including any necessary but non-existent parent directories.
2587     * </p>
2588     *
2589     * @param srcFile the file to be moved.
2590     * @param destDir the directory to move the file into.
2591     * @param createDestDir if {@code true} create the destination directory. If {@code false} throw an
2592     *        IOException if the destination directory does not already exist.
2593     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
2594     * @throws FileExistsException if the destination file exists.
2595     * @throws FileNotFoundException if the source file does not exist.
2596     * @throws IOException if source or destination is invalid.
2597     * @throws IOException if the directory was not created along with all its parent directories, if enabled.
2598     * @throws IOException if an error occurs or setting the last-modified time didn't succeed.
2599     * @throws SecurityException See {@link File#mkdirs()}.
2600     * @throws IllegalArgumentException if {@code destDir} exists but is not a directory.
2601     * @since 1.4
2602     */
2603    public static void moveFileToDirectory(final File srcFile, final File destDir, final boolean createDestDir) throws IOException {
2604        validateMoveParameters(srcFile, destDir);
2605        if (!destDir.exists() && createDestDir) {
2606            mkdirs(destDir);
2607        }
2608        requireDirectoryExists(destDir, "destDir");
2609        moveFile(srcFile, new File(destDir, srcFile.getName()));
2610    }
2611
2612    /**
2613     * Moves a file or directory into a destination directory.
2614     * <p>
2615     * If {@code createDestDir} is true, creates all destination parent directories, including any necessary but non-existent parent directories.
2616     * </p>
2617     * <p>
2618     * When the destination is on another file system, do a "copy and delete".
2619     * </p>
2620     *
2621     * @param src           the file or directory to be moved.
2622     * @param destDir       the destination directory.
2623     * @param createDestDir if {@code true} create the destination directory. If {@code false} throw an
2624     *        IOException if the destination directory does not already exist.
2625     * @throws NullPointerException  if any of the given {@link File}s are {@code null}.
2626     * @throws FileExistsException   if the directory or file exists in the destination directory.
2627     * @throws FileNotFoundException if the source file does not exist.
2628     * @throws IOException           if source or destination is invalid.
2629     * @throws IOException           if an error occurs or setting the last-modified time didn't succeed.
2630     * @since 1.4
2631     */
2632    public static void moveToDirectory(final File src, final File destDir, final boolean createDestDir) throws IOException {
2633        validateMoveParameters(src, destDir);
2634        if (src.isDirectory()) {
2635            moveDirectoryToDirectory(src, destDir, createDestDir);
2636        } else {
2637            moveFileToDirectory(src, destDir, createDestDir);
2638        }
2639    }
2640
2641    /**
2642     * Creates a new OutputStream by opening or creating a file, returning an output stream that may be used to write bytes
2643     * to the file.
2644     *
2645     * @param append Whether or not to append.
2646     * @param file the File.
2647     * @return a new OutputStream.
2648     * @throws IOException if an I/O error occurs.
2649     * @see PathUtils#newOutputStream(Path, boolean)
2650     * @since 2.12.0
2651     */
2652    public static OutputStream newOutputStream(final File file, final boolean append) throws IOException {
2653        return PathUtils.newOutputStream(Objects.requireNonNull(file, PROTOCOL_FILE).toPath(), append);
2654    }
2655
2656    /**
2657     * Opens a {@link FileInputStream} for the specified file, providing better error messages than simply calling
2658     * {@code new FileInputStream(file)}.
2659     * <p>
2660     * At the end of the method either the stream will be successfully opened, or an exception will have been thrown.
2661     * </p>
2662     * <p>
2663     * An exception is thrown if the file does not exist. An exception is thrown if the file object exists but is a
2664     * directory. An exception is thrown if the file exists but cannot be read.
2665     * </p>
2666     *
2667     * @param file the file to open for input, must not be {@code null}.
2668     * @return a new {@link FileInputStream} for the specified file.
2669     * @throws NullPointerException if file is {@code null}.
2670     * @throws FileNotFoundException if the file does not exist, is a directory rather than a regular file, or for some
2671     *         other reason cannot be opened for reading.
2672     * @throws IOException See FileNotFoundException above, FileNotFoundException is a subclass of IOException.
2673     * @since 1.3
2674     */
2675    public static FileInputStream openInputStream(final File file) throws IOException {
2676        Objects.requireNonNull(file, PROTOCOL_FILE);
2677        return new FileInputStream(file);
2678    }
2679
2680    /**
2681     * Opens a {@link FileOutputStream} for the specified file, checking and
2682     * creating the parent directory if it does not exist.
2683     * <p>
2684     * At the end of the method either the stream will be successfully opened,
2685     * or an exception will have been thrown.
2686     * </p>
2687     * <p>
2688     * The parent directory will be created if it does not exist.
2689     * The file will be created if it does not exist.
2690     * An exception is thrown if the file object exists but is a directory.
2691     * An exception is thrown if the file exists but cannot be written to.
2692     * An exception is thrown if the parent directory cannot be created.
2693     * </p>
2694     *
2695     * @param file the file to open for output, must not be {@code null}.
2696     * @return a new {@link FileOutputStream} for the specified file
2697     * @throws NullPointerException if the file object is {@code null}.
2698     * @throws IllegalArgumentException if the file object is a directory
2699     * @throws IllegalArgumentException if the file is not writable.
2700     * @throws IOException if the directories could not be created.
2701     * @since 1.3
2702     */
2703    public static FileOutputStream openOutputStream(final File file) throws IOException {
2704        return openOutputStream(file, false);
2705    }
2706
2707    /**
2708     * Opens a {@link FileOutputStream} for the specified file, checking and
2709     * creating the parent directory if it does not exist.
2710     * <p>
2711     * At the end of the method either the stream will be successfully opened,
2712     * or an exception will have been thrown.
2713     * </p>
2714     * <p>
2715     * The parent directory will be created if it does not exist.
2716     * The file will be created if it does not exist.
2717     * An exception is thrown if the file object exists but is a directory.
2718     * An exception is thrown if the file exists but cannot be written to.
2719     * An exception is thrown if the parent directory cannot be created.
2720     * </p>
2721     *
2722     * @param file   the file to open for output, must not be {@code null}.
2723     * @param append if {@code true}, then bytes will be added to the
2724     *               end of the file rather than overwriting.
2725     * @return a new {@link FileOutputStream} for the specified file.
2726     * @throws NullPointerException if the file object is {@code null}.
2727     * @throws IllegalArgumentException if the file object is a directory.
2728     * @throws IOException if the directories could not be created, or the file is not writable.
2729     * @since 2.1
2730     */
2731    public static FileOutputStream openOutputStream(final File file, final boolean append) throws IOException {
2732        Objects.requireNonNull(file, PROTOCOL_FILE);
2733        if (file.exists()) {
2734            checkIsFile(file, PROTOCOL_FILE);
2735        } else {
2736            createParentDirectories(file);
2737        }
2738        return new FileOutputStream(file, append);
2739    }
2740
2741    /**
2742     * Reads the contents of a file into a byte array.
2743     * The file is always closed.
2744     *
2745     * @param file the file to read, must not be {@code null}.
2746     * @return the file contents, never {@code null}.
2747     * @throws NullPointerException if file is {@code null}.
2748     * @throws IOException if an I/O error occurs, including when the file does not exist, is a directory rather than a
2749     *         regular file, or for some other reason why the file cannot be opened for reading.
2750     * @since 1.1
2751     */
2752    public static byte[] readFileToByteArray(final File file) throws IOException {
2753        Objects.requireNonNull(file, PROTOCOL_FILE);
2754        return Files.readAllBytes(file.toPath());
2755    }
2756
2757    /**
2758     * Reads the contents of a file into a String using the virtual machine's {@linkplain Charset#defaultCharset() default charset}. The
2759     * file is always closed.
2760     *
2761     * @param file the file to read, must not be {@code null}.
2762     * @return the file contents, never {@code null}.
2763     * @throws NullPointerException if file is {@code null}.
2764     * @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
2765     *                              reason why the file cannot be opened for reading.
2766     * @since 1.3.1
2767     * @deprecated Use {@link #readFileToString(File, Charset)} instead (and specify the appropriate encoding).
2768     */
2769    @Deprecated
2770    public static String readFileToString(final File file) throws IOException {
2771        return readFileToString(file, Charset.defaultCharset());
2772    }
2773
2774    /**
2775     * Reads the contents of a file into a String.
2776     * The file is always closed.
2777     *
2778     * @param file     the file to read, must not be {@code null}.
2779     * @param charsetName the name of the requested charset, {@code null} means platform default.
2780     * @return the file contents, never {@code null}.
2781     * @throws NullPointerException if file is {@code null}.
2782     * @throws IOException if an I/O error occurs, including when the file does not exist, is a directory rather than a
2783     *         regular file, or for some other reason why the file cannot be opened for reading.
2784     * @since 2.3
2785     */
2786    public static String readFileToString(final File file, final Charset charsetName) throws IOException {
2787        return IOUtils.toString(() -> Files.newInputStream(file.toPath()), Charsets.toCharset(charsetName));
2788    }
2789
2790    /**
2791     * Reads the contents of a file into a String. The file is always closed.
2792     *
2793     * @param file     the file to read, must not be {@code null}.
2794     * @param charsetName the name of the requested charset, {@code null} means platform default.
2795     * @return the file contents, never {@code null}.
2796     * @throws NullPointerException if file is {@code null}.
2797     * @throws IOException if an I/O error occurs, including when the file does not exist, is a directory rather than a
2798     *         regular file, or for some other reason why the file cannot be opened for reading.
2799     * @throws java.nio.charset.UnsupportedCharsetException if the named charset is unavailable.
2800     * @since 2.3
2801     */
2802    public static String readFileToString(final File file, final String charsetName) throws IOException {
2803        return readFileToString(file, Charsets.toCharset(charsetName));
2804    }
2805
2806    /**
2807     * Reads the contents of a file line by line to a List of Strings using the virtual machine's {@linkplain Charset#defaultCharset() default charset}.
2808     * The file is always closed.
2809     *
2810     * @param file the file to read, must not be {@code null}.
2811     * @return the list of Strings representing each line in the file, never {@code null}.
2812     * @throws NullPointerException if file is {@code null}.
2813     * @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
2814     *                              reason why the file cannot be opened for reading.
2815     * @since 1.3
2816     * @deprecated Use {@link #readLines(File, Charset)} instead (and specify the appropriate encoding).
2817     */
2818    @Deprecated
2819    public static List<String> readLines(final File file) throws IOException {
2820        return readLines(file, Charset.defaultCharset());
2821    }
2822
2823    /**
2824     * Reads the contents of a file line by line to a List of Strings.
2825     * The file is always closed.
2826     *
2827     * @param file     the file to read, must not be {@code null}.
2828     * @param charset the charset to use, {@code null} means platform default.
2829     * @return the list of Strings representing each line in the file, never {@code null}.
2830     * @throws NullPointerException if file is {@code null}.
2831     * @throws IOException if an I/O error occurs, including when the file does not exist, is a directory rather than a
2832     *         regular file, or for some other reason why the file cannot be opened for reading.
2833     * @since 2.3
2834     */
2835    public static List<String> readLines(final File file, final Charset charset) throws IOException {
2836        return Files.readAllLines(file.toPath(), Charsets.toCharset(charset));
2837    }
2838
2839    /**
2840     * Reads the contents of a file line by line to a List of Strings. The file is always closed.
2841     *
2842     * @param file     the file to read, must not be {@code null}.
2843     * @param charsetName the name of the requested charset, {@code null} means platform default.
2844     * @return the list of Strings representing each line in the file, never {@code null}.
2845     * @throws NullPointerException if file is {@code null}.
2846     * @throws IOException if an I/O error occurs, including when the file does not exist, is a directory rather than a
2847     *         regular file, or for some other reason why the file cannot be opened for reading.
2848     * @throws java.nio.charset.UnsupportedCharsetException if the named charset is unavailable.
2849     * @since 1.1
2850     */
2851    public static List<String> readLines(final File file, final String charsetName) throws IOException {
2852        return readLines(file, Charsets.toCharset(charsetName));
2853    }
2854
2855    private static void requireAbsent(final File file, final String name) throws FileExistsException {
2856        if (file.exists()) {
2857            throw new FileExistsException(String.format("File element in parameter '%s' already exists: '%s'", name, file));
2858        }
2859    }
2860
2861    /**
2862     * Throws IllegalArgumentException if the given files' canonical representations are equal.
2863     *
2864     * @param file1 The first file to compare.
2865     * @param file2 The second file to compare.
2866     * @throws IOException if an I/O error occurs.
2867     * @throws IllegalArgumentException if the given files' canonical representations are equal.
2868     */
2869    private static void requireCanonicalPathsNotEquals(final File file1, final File file2) throws IOException {
2870        final String canonicalPath = file1.getCanonicalPath();
2871        if (canonicalPath.equals(file2.getCanonicalPath())) {
2872            throw new IllegalArgumentException(String
2873                .format("File canonical paths are equal: '%s' (file1='%s', file2='%s')", canonicalPath, file1, file2));
2874        }
2875    }
2876
2877    /**
2878     * Requires that the given {@link File} exists and is a directory.
2879     *
2880     * @param directory The {@link File} to check.
2881     * @param name The parameter name to use in the exception message in case of null input or if the file is not a directory.
2882     * @throws NullPointerException if the given {@link File} is {@code null}.
2883     * @throws FileNotFoundException if the given {@link File} does not exist.
2884     * @throws IllegalArgumentException if the given {@link File} exists but is not a directory.
2885     */
2886    private static void requireDirectoryExists(final File directory, final String name) throws FileNotFoundException {
2887        Objects.requireNonNull(directory, name);
2888        if (!directory.isDirectory()) {
2889            if (directory.exists()) {
2890                throw new IllegalArgumentException("Parameter '" + name + "' is not a directory: '" + directory + "'");
2891            }
2892            throw new FileNotFoundException("Directory '" + directory + "' does not exist.");
2893        }
2894    }
2895
2896    /**
2897     * Requires that the given {@link File} is a directory if it exists.
2898     *
2899     * @param directory The {@link File} to check.
2900     * @param name The parameter name to use in the exception message in case of null input.
2901     * @throws NullPointerException if the given {@link File} is {@code null}.
2902     * @throws IllegalArgumentException if the given {@link File} exists but is not a directory.
2903     */
2904    private static void requireDirectoryIfExists(final File directory, final String name) {
2905        Objects.requireNonNull(directory, name);
2906        if (directory.exists() && !directory.isDirectory()) {
2907            throw new IllegalArgumentException("Parameter '" + name + "' is not a directory: '" + directory + "'");
2908        }
2909    }
2910
2911    /**
2912     * Sets file lastModifiedTime, lastAccessTime and creationTime to match source file
2913     *
2914     * @param sourceFile The source file to query.
2915     * @param targetFile The target file or directory to set.
2916     * @return {@code true} if and only if the operation succeeded; {@code false} otherwise.
2917     * @throws NullPointerException if sourceFile is {@code null}.
2918     * @throws NullPointerException if targetFile is {@code null}.
2919     */
2920    private static boolean setTimes(final File sourceFile, final File targetFile) {
2921        Objects.requireNonNull(sourceFile, "sourceFile");
2922        Objects.requireNonNull(targetFile, "targetFile");
2923        try {
2924            // Set creation, modified, last accessed to match source file
2925            final BasicFileAttributes srcAttr = Files.readAttributes(sourceFile.toPath(), BasicFileAttributes.class);
2926            final BasicFileAttributeView destAttrView = Files.getFileAttributeView(targetFile.toPath(), BasicFileAttributeView.class);
2927            // null guards are not needed; BasicFileAttributes.setTimes(...) is null safe
2928            destAttrView.setTimes(srcAttr.lastModifiedTime(), srcAttr.lastAccessTime(), srcAttr.creationTime());
2929            return true;
2930        } catch (final IOException ignored) {
2931            // Fallback: Only set modified time to match source file
2932            return targetFile.setLastModified(sourceFile.lastModified());
2933        }
2934        // TODO: (Help!) Determine historically why setLastModified(File, File) needed PathUtils.setLastModifiedTime() if
2935        // sourceFile.isFile() was true, but needed setLastModifiedTime(File, long) if sourceFile.isFile() was false
2936    }
2937
2938    /**
2939     * Returns the size of the specified file or directory. If the provided
2940     * {@link File} is a regular file, then the file's length is returned.
2941     * If the argument is a directory, then the size of the directory is
2942     * calculated recursively. If a directory or subdirectory is security
2943     * restricted, its size will not be included.
2944     * <p>
2945     * Note that overflow is not detected, and the return value may be negative if
2946     * overflow occurs. See {@link #sizeOfAsBigInteger(File)} for an alternative
2947     * method that does not overflow.
2948     * </p>
2949     *
2950     * @param file the regular file or directory to return the size
2951     *             of (must not be {@code null}).
2952     *
2953     * @return the length of the file, or recursive size of the directory,
2954     * provided (in bytes).
2955     *
2956     * @throws NullPointerException     if the file is {@code null}.
2957     * @throws IllegalArgumentException if the file does not exist.
2958     * @throws UncheckedIOException if an IO error occurs.
2959     * @since 2.0
2960     */
2961    public static long sizeOf(final File file) {
2962        return Uncheck.getAsLong(() -> PathUtils.sizeOf(file.toPath()));
2963    }
2964
2965    /**
2966     * Returns the size of the specified file or directory. If the provided
2967     * {@link File} is a regular file, then the file's length is returned.
2968     * If the argument is a directory, then the size of the directory is
2969     * calculated recursively. If a directory or subdirectory is security
2970     * restricted, its size will not be included.
2971     *
2972     * @param file the regular file or directory to return the size
2973     *             of (must not be {@code null}).
2974     *
2975     * @return the length of the file, or recursive size of the directory,
2976     * provided (in bytes).
2977     *
2978     * @throws NullPointerException     if the file is {@code null}.
2979     * @throws IllegalArgumentException if the file does not exist.
2980     * @throws UncheckedIOException if an IO error occurs.
2981     * @since 2.4
2982     */
2983    public static BigInteger sizeOfAsBigInteger(final File file) {
2984        return Uncheck.get(() -> PathUtils.sizeOfAsBigInteger(file.toPath()));
2985    }
2986
2987    /**
2988     * Counts the size of a directory recursively (sum of the length of all files).
2989     * <p>
2990     * Note that overflow is not detected, and the return value may be negative if
2991     * overflow occurs. See {@link #sizeOfDirectoryAsBigInteger(File)} for an alternative
2992     * method that does not overflow.
2993     * </p>
2994     *
2995     * @param directory directory to inspect, must not be {@code null}.
2996     * @return size of directory in bytes, 0 if directory is security restricted, a negative number when the real total
2997     * is greater than {@link Long#MAX_VALUE}.
2998     * @throws IllegalArgumentException if the given {@link File} exists but is not a directory.
2999     * @throws NullPointerException if the directory is {@code null}.
3000     * @throws UncheckedIOException if an IO error occurs.
3001     */
3002    public static long sizeOfDirectory(final File directory) {
3003        try {
3004            requireDirectoryExists(directory, "directory");
3005        } catch (final FileNotFoundException e) {
3006            throw new UncheckedIOException(e);
3007        }
3008        return Uncheck.getAsLong(() -> PathUtils.sizeOfDirectory(directory.toPath()));
3009    }
3010
3011    /**
3012     * Counts the size of a directory recursively (sum of the length of all files).
3013     *
3014     * @param directory directory to inspect, must not be {@code null}.
3015     * @return size of directory in bytes, 0 if directory is security restricted.
3016     * @throws IllegalArgumentException if the given {@link File} exists but is not a directory.
3017     * @throws NullPointerException if the directory is {@code null}.
3018     * @throws UncheckedIOException if an IO error occurs.
3019     * @since 2.4
3020     */
3021    public static BigInteger sizeOfDirectoryAsBigInteger(final File directory) {
3022        try {
3023            requireDirectoryExists(directory, "directory");
3024        } catch (final FileNotFoundException e) {
3025            throw new UncheckedIOException(e);
3026        }
3027        return Uncheck.get(() -> PathUtils.sizeOfDirectoryAsBigInteger(directory.toPath()));
3028    }
3029
3030    /**
3031     * Streams over the files in a given directory (and optionally its subdirectories) which match an array of extensions.
3032     * <p>
3033     * The returned {@link Stream} may wrap one or more {@link DirectoryStream}s. When you require timely disposal of file system resources, use a
3034     * {@code try}-with-resources block to ensure invocation of the stream's {@link Stream#close()} method after the stream operations are completed. Calling a
3035     * closed stream causes a {@link IllegalStateException}.
3036     * </p>
3037     *
3038     * @param directory  The directory to search.
3039     * @param recursive  if true all subdirectories are searched as well.
3040     * @param extensions an array of extensions, for example, <code>{"java", "xml"}</code>. If this parameter is {@code null}, all files are returned.
3041     * @return a Stream of {@link File} for matching files.
3042     * @throws IOException if an I/O error is thrown when accessing the starting file.
3043     * @since 2.9.0
3044     */
3045    public static Stream<File> streamFiles(final File directory, final boolean recursive, final String... extensions) throws IOException {
3046        // @formatter:off
3047        final IOFileFilter filter = extensions == null
3048            ? FileFileFilter.INSTANCE
3049            : FileFileFilter.INSTANCE.and(toSuffixFileFilter(extensions));
3050        // @formatter:on
3051        return PathUtils.walk(directory.toPath(), filter, toMaxDepth(recursive), false, FileVisitOption.FOLLOW_LINKS).map(Path::toFile);
3052    }
3053
3054    /**
3055     * Converts from a {@link URL} to a {@link File}.
3056     * <p>
3057     * 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
3058     * to characters. Additionally, malformed percent-encoded octets are handled leniently by passing them through literally.
3059     * </p>
3060     *
3061     * @param url the file URL to convert, {@code null} returns {@code null}.
3062     * @return the equivalent {@link File} object, or {@code null} if the URL's protocol is not {@code file}.
3063     */
3064    public static File toFile(final URL url) {
3065        if (url == null || !isFileProtocol(url)) {
3066            return null;
3067        }
3068        final String fileName = url.getFile().replace('/', File.separatorChar);
3069        return new File(decodeUrl(fileName));
3070    }
3071
3072    /**
3073     * Converts each of an array of {@link URL} to a {@link File}.
3074     * <p>
3075     * 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
3076     * array contains {@code null} at the same index.
3077     * </p>
3078     * <p>
3079     * 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}.
3080     * </p>
3081     *
3082     * @param urls the file URLs to convert, {@code null} returns empty array.
3083     * @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.
3084     * @throws IllegalArgumentException if any file is not a URL file.
3085     * @throws IllegalArgumentException if any file is incorrectly encoded.
3086     * @since 1.1
3087     */
3088    public static File[] toFiles(final URL... urls) {
3089        if (IOUtils.length(urls) == 0) {
3090            return EMPTY_FILE_ARRAY;
3091        }
3092        final File[] files = new File[urls.length];
3093        for (int i = 0; i < urls.length; i++) {
3094            final URL url = urls[i];
3095            if (url != null) {
3096                if (!isFileProtocol(url)) {
3097                    throw new IllegalArgumentException("Can only convert file URL to a File: " + url);
3098                }
3099                files[i] = toFile(url);
3100            }
3101        }
3102        return files;
3103    }
3104
3105    /**
3106     * Consumes all of the given stream.
3107     * <p>
3108     * When called from a FileTreeWalker, the walker <em>closes</em> the stream because {@link FileTreeWalker#next()} calls {@code top.stream().close()}.
3109     * </p>
3110     *
3111     * @param stream The stream to consume.
3112     * @return a new List.
3113     */
3114    private static List<File> toList(final Stream<File> stream) {
3115        return stream.collect(Collectors.toList());
3116    }
3117
3118    /**
3119     * Converts whether or not to recurse into a recursion max depth.
3120     *
3121     * @param recursive whether or not to recurse.
3122     * @return the recursion depth.
3123     */
3124    private static int toMaxDepth(final boolean recursive) {
3125        return recursive ? Integer.MAX_VALUE : 1;
3126    }
3127
3128    /**
3129     * Converts an array of file extensions to suffixes.
3130     *
3131     * @param extensions an array of extensions, for example: {@code ["java", "xml"]}.
3132     * @return an array of suffixes, for example: {@code [".java", ".xml"]}.
3133     * @throws NullPointerException if the parameter is null.
3134     */
3135    private static String[] toSuffixes(final String... extensions) {
3136        return Stream.of(Objects.requireNonNull(extensions, "extensions")).map(s -> s.charAt(0) == '.' ? s : "." + s).toArray(String[]::new);
3137    }
3138
3139    private static SuffixFileFilter toSuffixFileFilter(final String... extensions) {
3140        return new SuffixFileFilter(toSuffixes(extensions));
3141    }
3142
3143    /**
3144     * Implements behavior similar to the Unix "touch" utility. Creates a new file with size 0, or, if the file exists, just
3145     * updates the file's modified time. This method throws an IOException if the last modified date
3146     * of the file cannot be set. It creates parent directories if they do not exist.
3147     *
3148     * @param file the File to touch.
3149     * @throws NullPointerException if the parameter is {@code null}.
3150     * @throws IOException if setting the last-modified time failed or an I/O problem occurs.
3151     */
3152    public static void touch(final File file) throws IOException {
3153        PathUtils.touch(Objects.requireNonNull(file, PROTOCOL_FILE).toPath());
3154    }
3155
3156    /**
3157     * Converts each element of an array of {@link File} to a {@link URL}.
3158     * <p>
3159     * Returns an array of the same size as the input.
3160     * </p>
3161     *
3162     * @param files the files to convert, must not be {@code null}.
3163     * @return an array of URLs matching the input.
3164     * @throws IOException          if a file cannot be converted.
3165     * @throws NullPointerException if any argument is null.
3166     */
3167    public static URL[] toURLs(final File... files) throws IOException {
3168        Objects.requireNonNull(files, "files");
3169        final URL[] urls = new URL[files.length];
3170        for (int i = 0; i < urls.length; i++) {
3171            urls[i] = files[i].toURI().toURL();
3172        }
3173        return urls;
3174    }
3175
3176    /**
3177     * Validates the given arguments.
3178     * <ul>
3179     * <li>Throws {@link NullPointerException} if {@code source} is null</li>
3180     * <li>Throws {@link NullPointerException} if {@code destination} is null</li>
3181     * <li>Throws {@link FileNotFoundException} if {@code source} does not exist</li>
3182     * </ul>
3183     *
3184     * @param source      the file or directory to be moved.
3185     * @param destination the destination file or directory.
3186     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
3187     * @throws FileNotFoundException if the source file does not exist.
3188     */
3189    private static void validateMoveParameters(final File source, final File destination) throws FileNotFoundException {
3190        Objects.requireNonNull(source, "source");
3191        Objects.requireNonNull(destination, "destination");
3192        if (!source.exists()) {
3193            throw new FileNotFoundException("Source '" + source + "' does not exist");
3194        }
3195    }
3196
3197    /**
3198     * Waits for the file system to detect a file's presence, with a timeout.
3199     * <p>
3200     * This method repeatedly tests {@link Files#exists(Path, LinkOption...)} until it returns
3201     * true up to the maximum time specified in seconds.
3202     * </p>
3203     *
3204     * @param file    the file to check, must not be {@code null}.
3205     * @param seconds the maximum time in seconds to wait.
3206     * @return true if file exists.
3207     * @throws NullPointerException if the file is {@code null}.
3208     */
3209    public static boolean waitFor(final File file, final int seconds) {
3210        Objects.requireNonNull(file, PROTOCOL_FILE);
3211        return PathUtils.waitFor(file.toPath(), Duration.ofSeconds(seconds), PathUtils.EMPTY_LINK_OPTION_ARRAY);
3212    }
3213
3214    /**
3215     * Writes a CharSequence to a file creating the file if it does not exist using the virtual machine's {@linkplain Charset#defaultCharset() default charset}.
3216     *
3217     * @param file the file to write.
3218     * @param data the content to write to the file.
3219     * @throws IOException in case of an I/O error.
3220     * @since 2.0
3221     * @deprecated Use {@link #write(File, CharSequence, Charset)} instead (and specify the appropriate encoding).
3222     */
3223    @Deprecated
3224    public static void write(final File file, final CharSequence data) throws IOException {
3225        write(file, data, Charset.defaultCharset(), false);
3226    }
3227
3228    /**
3229     * Writes a CharSequence to a file creating the file if it does not exist using the virtual machine's {@linkplain Charset#defaultCharset() default charset}.
3230     *
3231     * @param file   the file to write.
3232     * @param data   the content to write to the file.
3233     * @param append if {@code true}, then the data will be added to the end of the file rather than overwriting.
3234     * @throws IOException in case of an I/O error.
3235     * @since 2.1
3236     * @deprecated Use {@link #write(File, CharSequence, Charset, boolean)} instead (and specify the appropriate encoding).
3237     */
3238    @Deprecated
3239    public static void write(final File file, final CharSequence data, final boolean append) throws IOException {
3240        write(file, data, Charset.defaultCharset(), append);
3241    }
3242
3243    /**
3244     * Writes a CharSequence to a file creating the file if it does not exist.
3245     *
3246     * @param file     the file to write.
3247     * @param data     the content to write to the file.
3248     * @param charset the name of the requested charset, {@code null} means platform default.
3249     * @throws IOException in case of an I/O error.
3250     * @since 2.3
3251     */
3252    public static void write(final File file, final CharSequence data, final Charset charset) throws IOException {
3253        write(file, data, charset, false);
3254    }
3255
3256    /**
3257     * Writes a CharSequence to a file creating the file if it does not exist.
3258     *
3259     * @param file     the file to write.
3260     * @param data     the content to write to the file.
3261     * @param charset the charset to use, {@code null} means platform default.
3262     * @param append   if {@code true}, then the data will be added to the.
3263     *                 end of the file rather than overwriting.
3264     * @throws IOException in case of an I/O error.
3265     * @since 2.3
3266     */
3267    public static void write(final File file, final CharSequence data, final Charset charset, final boolean append) throws IOException {
3268        writeStringToFile(file, Objects.toString(data, null), charset, append);
3269    }
3270
3271    /**
3272     * Writes a CharSequence to a file creating the file if it does not exist.
3273     *
3274     * @param file     the file to write
3275     * @param data     the content to write to the file
3276     * @param charsetName the name of the requested charset, {@code null} means platform default
3277     * @throws IOException                          in case of an I/O error
3278     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
3279     * @since 2.0
3280     */
3281    public static void write(final File file, final CharSequence data, final String charsetName) throws IOException {
3282        write(file, data, charsetName, false);
3283    }
3284
3285    /**
3286     * Writes a CharSequence to a file creating the file if it does not exist.
3287     *
3288     * @param file     the file to write
3289     * @param data     the content to write to the file
3290     * @param charsetName the name of the requested charset, {@code null} means platform default
3291     * @param append   if {@code true}, then the data will be added to the
3292     *                 end of the file rather than overwriting
3293     * @throws IOException                 in case of an I/O error
3294     * @throws java.nio.charset.UnsupportedCharsetException if the encoding is not supported by the VM
3295     * @since 2.1
3296     */
3297    public static void write(final File file, final CharSequence data, final String charsetName, final boolean append) throws IOException {
3298        write(file, data, Charsets.toCharset(charsetName), append);
3299    }
3300
3301    // Must be called with a directory
3302
3303    /**
3304     * Writes a byte array to a file creating the file if it does not exist.
3305     * The parent directories of the file will be created if they do not exist.
3306     *
3307     * @param file the file to write to
3308     * @param data the content to write to the file
3309     * @throws IOException in case of an I/O error
3310     * @since 1.1
3311     */
3312    public static void writeByteArrayToFile(final File file, final byte[] data) throws IOException {
3313        writeByteArrayToFile(file, data, false);
3314    }
3315
3316    /**
3317     * Writes a byte array to a file creating the file if it does not exist.
3318     *
3319     * @param file   the file to write to
3320     * @param data   the content to write to the file
3321     * @param append if {@code true}, then bytes will be added to the
3322     *               end of the file rather than overwriting
3323     * @throws IOException in case of an I/O error
3324     * @since 2.1
3325     */
3326    public static void writeByteArrayToFile(final File file, final byte[] data, final boolean append) throws IOException {
3327        writeByteArrayToFile(file, data, 0, data.length, append);
3328    }
3329
3330    /**
3331     * Writes {@code len} bytes from the specified byte array starting
3332     * at offset {@code off} to a file, creating the file if it does
3333     * not exist.
3334     *
3335     * @param file the file to write to
3336     * @param data the content to write to the file
3337     * @param off  the start offset in the data
3338     * @param len  the number of bytes to write
3339     * @throws IOException in case of an I/O error
3340     * @since 2.5
3341     */
3342    public static void writeByteArrayToFile(final File file, final byte[] data, final int off, final int len) throws IOException {
3343        writeByteArrayToFile(file, data, off, len, false);
3344    }
3345
3346    /**
3347     * Writes {@code len} bytes from the specified byte array starting
3348     * at offset {@code off} to a file, creating the file if it does
3349     * not exist.
3350     *
3351     * @param file   the file to write to
3352     * @param data   the content to write to the file
3353     * @param off    the start offset in the data
3354     * @param len    the number of bytes to write
3355     * @param append if {@code true}, then bytes will be added to the
3356     *               end of the file rather than overwriting
3357     * @throws IOException in case of an I/O error
3358     * @since 2.5
3359     */
3360    public static void writeByteArrayToFile(final File file, final byte[] data, final int off, final int len, final boolean append) throws IOException {
3361        try (OutputStream out = newOutputStream(file, append)) {
3362            out.write(data, off, len);
3363        }
3364    }
3365
3366    /**
3367     * Writes the {@code toString()} value of each item in a collection to
3368     * the specified {@link File} line by line.
3369     * The default VM encoding and the default line ending will be used.
3370     *
3371     * @param file  the file to write to
3372     * @param lines the lines to write, {@code null} entries produce blank lines
3373     * @throws IOException in case of an I/O error
3374     * @since 1.3
3375     */
3376    public static void writeLines(final File file, final Collection<?> lines) throws IOException {
3377        writeLines(file, null, lines, null, false);
3378    }
3379
3380    /**
3381     * Writes the {@code toString()} value of each item in a collection to
3382     * the specified {@link File} line by line.
3383     * The default VM encoding and the default line ending will be used.
3384     *
3385     * @param file   the file to write to
3386     * @param lines  the lines to write, {@code null} entries produce blank lines
3387     * @param append if {@code true}, then the lines will be added to the
3388     *               end of the file rather than overwriting
3389     * @throws IOException in case of an I/O error
3390     * @since 2.1
3391     */
3392    public static void writeLines(final File file, final Collection<?> lines, final boolean append) throws IOException {
3393        writeLines(file, null, lines, null, append);
3394    }
3395
3396    /**
3397     * Writes the {@code toString()} value of each item in a collection to
3398     * the specified {@link File} line by line.
3399     * The default VM encoding and the specified line ending will be used.
3400     *
3401     * @param file       the file to write to
3402     * @param lines      the lines to write, {@code null} entries produce blank lines
3403     * @param lineEnding the line separator to use, {@code null} is system default
3404     * @throws IOException in case of an I/O error
3405     * @since 1.3
3406     */
3407    public static void writeLines(final File file, final Collection<?> lines, final String lineEnding) throws IOException {
3408        writeLines(file, null, lines, lineEnding, false);
3409    }
3410
3411    /**
3412     * Writes the {@code toString()} value of each item in a collection to
3413     * the specified {@link File} line by line.
3414     * The default VM encoding and the specified line ending will be used.
3415     *
3416     * @param file       the file to write to
3417     * @param lines      the lines to write, {@code null} entries produce blank lines
3418     * @param lineEnding the line separator to use, {@code null} is system default
3419     * @param append     if {@code true}, then the lines will be added to the
3420     *                   end of the file rather than overwriting
3421     * @throws IOException in case of an I/O error
3422     * @since 2.1
3423     */
3424    public static void writeLines(final File file, final Collection<?> lines, final String lineEnding, final boolean append) throws IOException {
3425        writeLines(file, null, lines, lineEnding, append);
3426    }
3427
3428    /**
3429     * Writes the {@code toString()} value of each item in a collection to
3430     * the specified {@link File} line by line.
3431     * The specified character encoding and the default line ending will be used.
3432     * The parent directories of the file will be created if they do not exist.
3433     *
3434     * @param file     the file to write to
3435     * @param charsetName the name of the requested charset, {@code null} means platform default
3436     * @param lines    the lines to write, {@code null} entries produce blank lines
3437     * @throws IOException                          in case of an I/O error
3438     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
3439     * @since 1.1
3440     */
3441    public static void writeLines(final File file, final String charsetName, final Collection<?> lines) throws IOException {
3442        writeLines(file, charsetName, lines, null, false);
3443    }
3444
3445    /**
3446     * Writes the {@code toString()} value of each item in a collection to
3447     * the specified {@link File} line by line, optionally appending.
3448     * The specified character encoding and the default line ending will be used.
3449     *
3450     * @param file     the file to write to
3451     * @param charsetName the name of the requested charset, {@code null} means platform default
3452     * @param lines    the lines to write, {@code null} entries produce blank lines
3453     * @param append   if {@code true}, then the lines will be added to the
3454     *                 end of the file rather than overwriting
3455     * @throws IOException                          in case of an I/O error
3456     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
3457     * @since 2.1
3458     */
3459    public static void writeLines(final File file, final String charsetName, final Collection<?> lines, final boolean append) throws IOException {
3460        writeLines(file, charsetName, lines, null, append);
3461    }
3462
3463    /**
3464     * Writes the {@code toString()} value of each item in a collection to
3465     * the specified {@link File} line by line.
3466     * The specified character encoding and the line ending will be used.
3467     * The parent directories of the file will be created if they do not exist.
3468     *
3469     * @param file       the file to write to
3470     * @param charsetName   the name of the requested charset, {@code null} means platform default
3471     * @param lines      the lines to write, {@code null} entries produce blank lines
3472     * @param lineEnding the line separator to use, {@code null} is system default
3473     * @throws IOException                          in case of an I/O error
3474     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
3475     * @since 1.1
3476     */
3477    public static void writeLines(final File file, final String charsetName, final Collection<?> lines, final String lineEnding) throws IOException {
3478        writeLines(file, charsetName, lines, lineEnding, false);
3479    }
3480
3481    /**
3482     * Writes the {@code toString()} value of each item in a collection to
3483     * the specified {@link File} line by line.
3484     * The specified character encoding and the line ending will be used.
3485     *
3486     * @param file       the file to write to
3487     * @param charsetName   the name of the requested charset, {@code null} means platform default
3488     * @param lines      the lines to write, {@code null} entries produce blank lines
3489     * @param lineEnding the line separator to use, {@code null} is system default
3490     * @param append     if {@code true}, then the lines will be added to the
3491     *                   end of the file rather than overwriting
3492     * @throws IOException                          in case of an I/O error
3493     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
3494     * @since 2.1
3495     */
3496    public static void writeLines(final File file, final String charsetName, final Collection<?> lines, final String lineEnding, final boolean append)
3497        throws IOException {
3498        try (OutputStream out = new BufferedOutputStream(newOutputStream(file, append))) {
3499            IOUtils.writeLines(lines, lineEnding, out, charsetName);
3500        }
3501    }
3502
3503    /**
3504     * Writes a String to a file creating the file if it does not exist using the virtual machine's {@linkplain Charset#defaultCharset() default charset}.
3505     *
3506     * @param file the file to write
3507     * @param data the content to write to the file
3508     * @throws IOException in case of an I/O error
3509     * @deprecated Use {@link #writeStringToFile(File, String, Charset)} instead (and specify the appropriate encoding)
3510     */
3511    @Deprecated
3512    public static void writeStringToFile(final File file, final String data) throws IOException {
3513        writeStringToFile(file, data, Charset.defaultCharset(), false);
3514    }
3515
3516    /**
3517     * Writes a String to a file creating the file if it does not exist using the virtual machine's {@linkplain Charset#defaultCharset() default charset}.
3518     *
3519     * @param file   the file to write
3520     * @param data   the content to write to the file
3521     * @param append if {@code true}, then the String will be added to the end of the file rather than overwriting
3522     * @throws IOException in case of an I/O error
3523     * @since 2.1
3524     * @deprecated Use {@link #writeStringToFile(File, String, Charset, boolean)} instead (and specify the appropriate encoding)
3525     */
3526    @Deprecated
3527    public static void writeStringToFile(final File file, final String data, final boolean append) throws IOException {
3528        writeStringToFile(file, data, Charset.defaultCharset(), append);
3529    }
3530
3531    /**
3532     * Writes a String to a file creating the file if it does not exist.
3533     * The parent directories of the file will be created if they do not exist.
3534     *
3535     * @param file     the file to write
3536     * @param data     the content to write to the file
3537     * @param charset the charset to use, {@code null} means platform default
3538     * @throws IOException                          in case of an I/O error
3539     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
3540     * @since 2.4
3541     */
3542    public static void writeStringToFile(final File file, final String data, final Charset charset) throws IOException {
3543        writeStringToFile(file, data, charset, false);
3544    }
3545
3546    /**
3547     * Writes a String to a file, creating the file if it does not exist.
3548     * The parent directories of the file are created if they do not exist.
3549     *
3550     * @param file     the file to write
3551     * @param data     the content to write to the file
3552     * @param charset the charset to use, {@code null} means platform default
3553     * @param append   if {@code true}, then the String will be added to the
3554     *                 end of the file rather than overwriting
3555     * @throws IOException in case of an I/O error
3556     * @since 2.3
3557     */
3558    public static void writeStringToFile(final File file, final String data, final Charset charset, final boolean append) throws IOException {
3559        try (OutputStream out = newOutputStream(file, append)) {
3560            IOUtils.write(data, out, charset);
3561        }
3562    }
3563
3564    /**
3565     * Writes a String to a file, creating the file if it does not exist.
3566     * The parent directories of the file are created if they do not exist.
3567     *
3568     * @param file     the file to write
3569     * @param data     the content to write to the file
3570     * @param charsetName the name of the requested charset, {@code null} means platform default
3571     * @throws IOException                          in case of an I/O error
3572     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
3573     */
3574    public static void writeStringToFile(final File file, final String data, final String charsetName) throws IOException {
3575        writeStringToFile(file, data, charsetName, false);
3576    }
3577
3578    /**
3579     * Writes a String to a file, creating the file if it does not exist.
3580     * The parent directories of the file are created if they do not exist.
3581     *
3582     * @param file     the file to write
3583     * @param data     the content to write to the file
3584     * @param charsetName the name of the requested charset, {@code null} means platform default
3585     * @param append   if {@code true}, then the String will be added to the
3586     *                 end of the file rather than overwriting
3587     * @throws IOException                 in case of an I/O error
3588     * @throws java.nio.charset.UnsupportedCharsetException if the encoding is not supported by the VM
3589     * @since 2.1
3590     */
3591    public static void writeStringToFile(final File file, final String data, final String charsetName, final boolean append) throws IOException {
3592        writeStringToFile(file, data, Charsets.toCharset(charsetName), append);
3593    }
3594
3595    /**
3596     * Instances should NOT be constructed in standard programming.
3597     *
3598     * @deprecated TODO Make private in 3.0.
3599     */
3600    @Deprecated
3601    public FileUtils() { //NOSONAR
3602        // empty
3603    }
3604
3605}