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