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.getAsBoolean(() -> 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.getAsBoolean(() -> 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.getAsBoolean(() -> 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.getAsBoolean(() -> 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.getAsBoolean(() -> 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.getAsBoolean(() -> 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.getAsBoolean(() -> 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        // @formatter:off
2286        final AccumulatorPathVisitor visitor = AccumulatorPathVisitor.builder()
2287                .setPathCounters(Counters.noopPathCounters())
2288                .setFileFilter(fileFilter)
2289                .setDirectoryFilter(dirPathFilter)
2290                .setVisitFileFailedFunction((p, e) -> FileVisitResult.CONTINUE)
2291                .get();
2292        // @formatter:on
2293        final Set<FileVisitOption> optionSet = new HashSet<>();
2294        if (options != null) {
2295            Collections.addAll(optionSet, options);
2296        }
2297        Files.walkFileTree(directory.toPath(), optionSet, toMaxDepth(isDirFilterSet), visitor);
2298        return visitor;
2299    }
2300
2301    /**
2302     * Lists files in a directory, asserting that the supplied directory exists and is a directory.
2303     *
2304     * @param directory  The directory to list.
2305     * @param fileFilter Optional file filter, may be null.
2306     * @return The files in the directory, never {@code null}.
2307     * @throws NullPointerException     if the {@code directory} is {@code null}.
2308     * @throws IllegalArgumentException if the {@code directory} exists but is not a directory.
2309     * @throws IOException              if an I/O error occurs per {@link File#listFiles()} and {@link File#listFiles(FileFilter)}.
2310     * @throws SecurityException        If a security manager exists and its {@link SecurityManager#checkRead(String)} method denies read access to the
2311     *                                  directory.
2312     */
2313    private static File[] listFiles(final File directory, final FileFilter fileFilter) throws IOException {
2314        requireDirectoryExists(directory, "directory");
2315        final File[] files = directory.listFiles(fileFilter);
2316        if (files == null) {
2317            // null if the directory does not denote a directory, or if an I/O error occurs.
2318            throw new IOException("Unknown I/O error listing contents of directory: " + directory);
2319        }
2320        return files;
2321    }
2322
2323    /**
2324     * Finds files within a given directory (and optionally its
2325     * subdirectories). All files found are filtered by an IOFileFilter.
2326     * <p>
2327     * If your search should recurse into subdirectories you can pass in
2328     * an IOFileFilter for directories. You don't need to bind a
2329     * DirectoryFileFilter (via logical AND) to this filter. This method does
2330     * that for you.
2331     * </p>
2332     * <p>
2333     * An example: If you want to search through all directories called
2334     * "temp" you pass in {@code FileFilterUtils.NameFileFilter("temp")}
2335     * </p>
2336     * <p>
2337     * Another common usage of this method is find files in a directory
2338     * tree but ignoring the directories generated CVS. You can simply pass
2339     * in {@code FileFilterUtils.makeCVSAware(null)}.
2340     * </p>
2341     *
2342     * @param directory  the directory to search in
2343     * @param fileFilter filter to apply when finding files. Must not be {@code null},
2344     *                   use {@link TrueFileFilter#INSTANCE} to match all files in selected directories.
2345     * @param dirFilter  optional filter to apply when finding subdirectories.
2346     *                   If this parameter is {@code null}, subdirectories will not be included in the
2347     *                   search. Use {@link TrueFileFilter#INSTANCE} to match all directories.
2348     * @return a collection of {@link File} with the matching files
2349     * @see org.apache.commons.io.filefilter.FileFilterUtils
2350     * @see org.apache.commons.io.filefilter.NameFileFilter
2351     */
2352    public static Collection<File> listFiles(final File directory, final IOFileFilter fileFilter, final IOFileFilter dirFilter) {
2353        final AccumulatorPathVisitor visitor = Uncheck
2354            .apply(d -> listAccumulate(d, FileFileFilter.INSTANCE.and(fileFilter), dirFilter, FileVisitOption.FOLLOW_LINKS), directory);
2355        return toList(visitor.getFileList().stream().map(Path::toFile));
2356    }
2357
2358    /**
2359     * Lists Files in the given {@code directory}, adding each file to the given list.
2360     *
2361     * @param directory A File for an assumed directory, not null.
2362     * @param files The list to add found Files, not null.
2363     * @param recursive Whether or not to recurse into subdirectories.
2364     * @param filter How to filter files, not null.
2365     */
2366    @SuppressWarnings("null")
2367    private static void listFiles(final File directory, final List<File> files, final boolean recursive, final FilenameFilter filter) {
2368        final File[] listFiles = directory.listFiles();
2369        if (listFiles != null) {
2370            // Only allocate if you must.
2371            final List<File> dirs = recursive ? new ArrayList<>() : null;
2372            Arrays.stream(listFiles).forEach(f -> {
2373                if (recursive && f.isDirectory()) {
2374                    dirs.add(f);
2375                } else if (f.isFile() && filter.accept(directory, f.getName())) {
2376                    files.add(f);
2377                }
2378            });
2379            if (recursive) {
2380                dirs.forEach(d -> listFiles(d, files, true, filter));
2381            }
2382        }
2383    }
2384
2385    /**
2386     * Lists files within a given directory (and optionally its subdirectories)
2387     * which match an array of extensions.
2388     *
2389     * @param directory  the directory to search in
2390     * @param extensions an array of extensions, for example, {"java","xml"}. If this
2391     *                   parameter is {@code null}, all files are returned.
2392     * @param recursive  if true all subdirectories are searched as well
2393     * @return a collection of {@link File} with the matching files
2394     */
2395    public static Collection<File> listFiles(final File directory, final String[] extensions, final boolean recursive) {
2396        // IO-856: Don't use NIO to path walk, allocate as little as possible while traversing.
2397        final List<File> files = new ArrayList<>();
2398        final FilenameFilter filter = extensions != null ? toSuffixFileFilter(extensions) : TrueFileFilter.INSTANCE;
2399        listFiles(directory, files, recursive, filter);
2400        return files;
2401    }
2402
2403    /**
2404     * Finds files within a given directory (and optionally its
2405     * subdirectories). All files found are filtered by an IOFileFilter.
2406     * <p>
2407     * The resulting collection includes the starting directory and
2408     * any subdirectories that match the directory filter.
2409     * </p>
2410     *
2411     * @param directory  the directory to search in
2412     * @param fileFilter filter to apply when finding files.
2413     * @param dirFilter  optional filter to apply when finding subdirectories.
2414     *                   If this parameter is {@code null}, subdirectories will not be included in the
2415     *                   search. Use TrueFileFilter.INSTANCE to match all directories.
2416     * @return a collection of {@link File} with the matching files
2417     * @see org.apache.commons.io.FileUtils#listFiles
2418     * @see org.apache.commons.io.filefilter.FileFilterUtils
2419     * @see org.apache.commons.io.filefilter.NameFileFilter
2420     * @since 2.2
2421     */
2422    public static Collection<File> listFilesAndDirs(final File directory, final IOFileFilter fileFilter, final IOFileFilter dirFilter) {
2423        final AccumulatorPathVisitor visitor = Uncheck.apply(d -> listAccumulate(d, fileFilter, dirFilter, FileVisitOption.FOLLOW_LINKS),
2424            directory);
2425        final List<Path> list = visitor.getFileList();
2426        list.addAll(visitor.getDirList());
2427        return toList(list.stream().map(Path::toFile));
2428    }
2429
2430    /**
2431     * Calls {@link File#mkdirs()} and throws an {@link IOException} on failure.
2432     * <p>
2433     * Creates all directories for a File object, including any necessary but non-existent parent directories. If the {@code directory} already exists or is
2434     * null, nothing happens.
2435     * </p>
2436     *
2437     * @param directory the receiver for {@code mkdirs()}. If the {@code directory} already exists or is null, nothing happens.
2438     * @return the given directory.
2439     * @throws IOException       if the directory was not created along with all its parent directories.
2440     * @throws IOException       if the given file object is not a directory.
2441     * @throws SecurityException See {@link File#mkdirs()}.
2442     * @see File#mkdirs()
2443     */
2444    private static File mkdirs(final File directory) throws IOException {
2445        if (directory != null && !directory.mkdirs() && !directory.isDirectory()) {
2446            throw new IOException("Cannot create directory '" + directory + "'.");
2447        }
2448        return directory;
2449    }
2450
2451    /**
2452     * Moves a directory.
2453     * <p>
2454     * When the destination directory is on another file system, do a "copy and delete".
2455     * </p>
2456     *
2457     * @param srcDir the directory to be moved.
2458     * @param destDir the destination directory.
2459     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
2460     * @throws IllegalArgumentException if {@code srcDir} exists but is not a directory
2461     * @throws FileNotFoundException if the source does not exist.
2462     * @throws IOException if an error occurs or setting the last-modified time didn't succeed.
2463     * @since 1.4
2464     */
2465    public static void moveDirectory(final File srcDir, final File destDir) throws IOException {
2466        Objects.requireNonNull(destDir, "destination");
2467        requireDirectoryExists(srcDir, "srcDir");
2468        requireAbsent(destDir, "destDir");
2469        if (!srcDir.renameTo(destDir)) {
2470            if (destDir.getCanonicalPath().startsWith(srcDir.getCanonicalPath() + File.separator)) {
2471                throw new IOException("Cannot move directory: " + srcDir + " to a subdirectory of itself: " + destDir);
2472            }
2473            copyDirectory(srcDir, destDir);
2474            deleteDirectory(srcDir);
2475            if (srcDir.exists()) {
2476                throw new IOException("Failed to delete original directory '" + srcDir +
2477                        "' after copy to '" + destDir + "'");
2478            }
2479        }
2480    }
2481
2482    /**
2483     * Moves a directory to another directory.
2484     * <p>
2485     * If {@code createDestDir} is true, creates all destination parent directories, including any necessary but non-existent parent directories.
2486     * </p>
2487     *
2488     * @param source the directory to be moved.
2489     * @param destDir the destination file.
2490     * @param createDestDir If {@code true} create the destination directory, otherwise if {@code false} throw an
2491     *        IOException.
2492     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
2493     * @throws IllegalArgumentException if the source or destination is invalid.
2494     * @throws FileNotFoundException if the source does not exist.
2495     * @throws IOException if the directory was not created along with all its parent directories, if enabled.
2496     * @throws IOException if an error occurs or setting the last-modified time didn't succeed.
2497     * @throws SecurityException See {@link File#mkdirs()}.
2498     * @since 1.4
2499     */
2500    public static void moveDirectoryToDirectory(final File source, final File destDir, final boolean createDestDir) throws IOException {
2501        validateMoveParameters(source, destDir);
2502        if (!destDir.isDirectory()) {
2503            if (destDir.exists()) {
2504                throw new IOException("Destination '" + destDir + "' is not a directory");
2505            }
2506            if (!createDestDir) {
2507                throw new FileNotFoundException("Destination directory '" + destDir + "' does not exist [createDestDir=" + false + "]");
2508            }
2509            mkdirs(destDir);
2510        }
2511        moveDirectory(source, new File(destDir, source.getName()));
2512    }
2513
2514    /**
2515     * Moves a file preserving attributes.
2516     * <p>
2517     * Shorthand for {@code moveFile(srcFile, destFile, StandardCopyOption.COPY_ATTRIBUTES)}.
2518     * </p>
2519     * <p>
2520     * When the destination file is on another file system, do a "copy and delete".
2521     * </p>
2522     *
2523     * @param srcFile the file to be moved.
2524     * @param destFile the destination file.
2525     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
2526     * @throws FileExistsException if the destination file exists.
2527     * @throws FileNotFoundException if the source file does not exist.
2528     * @throws IllegalArgumentException if {@code srcFile} is a directory
2529     * @throws IOException if an error occurs.
2530     * @since 1.4
2531     */
2532    public static void moveFile(final File srcFile, final File destFile) throws IOException {
2533        moveFile(srcFile, destFile, StandardCopyOption.COPY_ATTRIBUTES);
2534    }
2535
2536    /**
2537     * Moves a file.
2538     * <p>
2539     * When the destination file is on another file system, do a "copy and delete".
2540     * </p>
2541     *
2542     * @param srcFile the file to be moved.
2543     * @param destFile the destination file.
2544     * @param copyOptions Copy options.
2545     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
2546     * @throws FileExistsException if the destination file exists.
2547     * @throws FileNotFoundException if the source file does not exist.
2548     * @throws IllegalArgumentException if {@code srcFile} is a directory
2549     * @throws IOException if an error occurs or setting the last-modified time didn't succeed.
2550     * @since 2.9.0
2551     */
2552    public static void moveFile(final File srcFile, final File destFile, final CopyOption... copyOptions) throws IOException {
2553        Objects.requireNonNull(destFile, "destination");
2554        checkFileExists(srcFile, "srcFile");
2555        requireAbsent(destFile, "destFile");
2556        final boolean rename = srcFile.renameTo(destFile);
2557        if (!rename) {
2558            // Don't interfere with file date on move, handled by StandardCopyOption.COPY_ATTRIBUTES
2559            copyFile(srcFile, destFile, false, copyOptions);
2560            if (!srcFile.delete()) {
2561                deleteQuietly(destFile);
2562                throw new IOException("Failed to delete original file '" + srcFile + "' after copy to '" + destFile + "'");
2563            }
2564        }
2565    }
2566
2567    /**
2568     * Moves a file into a directory.
2569     * <p>
2570     * If {@code createDestDir} is true, creates all destination parent directories, including any necessary but non-existent parent directories.
2571     * </p>
2572     *
2573     * @param srcFile the file to be moved.
2574     * @param destDir the directory to move the file into
2575     * @param createDestDir if {@code true} create the destination directory. If {@code false} throw an
2576     *        IOException if the destination directory does not already exist.
2577     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
2578     * @throws FileExistsException if the destination file exists.
2579     * @throws FileNotFoundException if the source file does not exist.
2580     * @throws IOException if source or destination is invalid.
2581     * @throws IOException if the directory was not created along with all its parent directories, if enabled.
2582     * @throws IOException if an error occurs or setting the last-modified time didn't succeed.
2583     * @throws SecurityException See {@link File#mkdirs()}.
2584     * @throws IllegalArgumentException if {@code destDir} exists but is not a directory
2585     * @since 1.4
2586     */
2587    public static void moveFileToDirectory(final File srcFile, final File destDir, final boolean createDestDir) throws IOException {
2588        validateMoveParameters(srcFile, destDir);
2589        if (!destDir.exists() && createDestDir) {
2590            mkdirs(destDir);
2591        }
2592        requireDirectoryExists(destDir, "destDir");
2593        moveFile(srcFile, new File(destDir, srcFile.getName()));
2594    }
2595
2596    /**
2597     * Moves a file or directory into a destination directory.
2598     * <p>
2599     * If {@code createDestDir} is true, creates all destination parent directories, including any necessary but non-existent parent directories.
2600     * </p>
2601     * <p>
2602     * When the destination is on another file system, do a "copy and delete".
2603     * </p>
2604     *
2605     * @param src           the file or directory to be moved.
2606     * @param destDir       the destination directory.
2607     * @param createDestDir if {@code true} create the destination directory. If {@code false} throw an
2608     *        IOException if the destination directory does not already exist.
2609     * @throws NullPointerException  if any of the given {@link File}s are {@code null}.
2610     * @throws FileExistsException   if the directory or file exists in the destination directory.
2611     * @throws FileNotFoundException if the source file does not exist.
2612     * @throws IOException           if source or destination is invalid.
2613     * @throws IOException           if an error occurs or setting the last-modified time didn't succeed.
2614     * @since 1.4
2615     */
2616    public static void moveToDirectory(final File src, final File destDir, final boolean createDestDir) throws IOException {
2617        validateMoveParameters(src, destDir);
2618        if (src.isDirectory()) {
2619            moveDirectoryToDirectory(src, destDir, createDestDir);
2620        } else {
2621            moveFileToDirectory(src, destDir, createDestDir);
2622        }
2623    }
2624
2625    /**
2626     * Creates a new OutputStream by opening or creating a file, returning an output stream that may be used to write bytes
2627     * to the file.
2628     *
2629     * @param append Whether or not to append.
2630     * @param file the File.
2631     * @return a new OutputStream.
2632     * @throws IOException if an I/O error occurs.
2633     * @see PathUtils#newOutputStream(Path, boolean)
2634     * @since 2.12.0
2635     */
2636    public static OutputStream newOutputStream(final File file, final boolean append) throws IOException {
2637        return PathUtils.newOutputStream(Objects.requireNonNull(file, PROTOCOL_FILE).toPath(), append);
2638    }
2639
2640    /**
2641     * Opens a {@link FileInputStream} for the specified file, providing better error messages than simply calling
2642     * {@code new FileInputStream(file)}.
2643     * <p>
2644     * At the end of the method either the stream will be successfully opened, or an exception will have been thrown.
2645     * </p>
2646     * <p>
2647     * An exception is thrown if the file does not exist. An exception is thrown if the file object exists but is a
2648     * directory. An exception is thrown if the file exists but cannot be read.
2649     * </p>
2650     *
2651     * @param file the file to open for input, must not be {@code null}
2652     * @return a new {@link FileInputStream} for the specified file
2653     * @throws NullPointerException if file is {@code null}.
2654     * @throws FileNotFoundException if the file does not exist, is a directory rather than a regular file, or for some
2655     *         other reason cannot be opened for reading.
2656     * @throws IOException See FileNotFoundException above, FileNotFoundException is a subclass of IOException.
2657     * @since 1.3
2658     */
2659    public static FileInputStream openInputStream(final File file) throws IOException {
2660        Objects.requireNonNull(file, PROTOCOL_FILE);
2661        return new FileInputStream(file);
2662    }
2663
2664    /**
2665     * Opens a {@link FileOutputStream} for the specified file, checking and
2666     * creating the parent directory if it does not exist.
2667     * <p>
2668     * At the end of the method either the stream will be successfully opened,
2669     * or an exception will have been thrown.
2670     * </p>
2671     * <p>
2672     * The parent directory will be created if it does not exist.
2673     * The file will be created if it does not exist.
2674     * An exception is thrown if the file object exists but is a directory.
2675     * An exception is thrown if the file exists but cannot be written to.
2676     * An exception is thrown if the parent directory cannot be created.
2677     * </p>
2678     *
2679     * @param file the file to open for output, must not be {@code null}
2680     * @return a new {@link FileOutputStream} for the specified file
2681     * @throws NullPointerException if the file object is {@code null}.
2682     * @throws IllegalArgumentException if the file object is a directory
2683     * @throws IllegalArgumentException if the file is not writable.
2684     * @throws IOException if the directories could not be created.
2685     * @since 1.3
2686     */
2687    public static FileOutputStream openOutputStream(final File file) throws IOException {
2688        return openOutputStream(file, false);
2689    }
2690
2691    /**
2692     * Opens a {@link FileOutputStream} for the specified file, checking and
2693     * creating the parent directory if it does not exist.
2694     * <p>
2695     * At the end of the method either the stream will be successfully opened,
2696     * or an exception will have been thrown.
2697     * </p>
2698     * <p>
2699     * The parent directory will be created if it does not exist.
2700     * The file will be created if it does not exist.
2701     * An exception is thrown if the file object exists but is a directory.
2702     * An exception is thrown if the file exists but cannot be written to.
2703     * An exception is thrown if the parent directory cannot be created.
2704     * </p>
2705     *
2706     * @param file   the file to open for output, must not be {@code null}
2707     * @param append if {@code true}, then bytes will be added to the
2708     *               end of the file rather than overwriting
2709     * @return a new {@link FileOutputStream} for the specified file
2710     * @throws NullPointerException if the file object is {@code null}.
2711     * @throws IllegalArgumentException if the file object is a directory
2712     * @throws IOException if the directories could not be created, or the file is not writable
2713     * @since 2.1
2714     */
2715    public static FileOutputStream openOutputStream(final File file, final boolean append) throws IOException {
2716        Objects.requireNonNull(file, PROTOCOL_FILE);
2717        if (file.exists()) {
2718            checkIsFile(file, PROTOCOL_FILE);
2719        } else {
2720            createParentDirectories(file);
2721        }
2722        return new FileOutputStream(file, append);
2723    }
2724
2725    /**
2726     * Reads the contents of a file into a byte array.
2727     * The file is always closed.
2728     *
2729     * @param file the file to read, must not be {@code null}
2730     * @return the file contents, never {@code null}
2731     * @throws NullPointerException if file is {@code null}.
2732     * @throws IOException if an I/O error occurs, including when the file does not exist, is a directory rather than a
2733     *         regular file, or for some other reason why the file cannot be opened for reading.
2734     * @since 1.1
2735     */
2736    public static byte[] readFileToByteArray(final File file) throws IOException {
2737        Objects.requireNonNull(file, PROTOCOL_FILE);
2738        return Files.readAllBytes(file.toPath());
2739    }
2740
2741    /**
2742     * Reads the contents of a file into a String using the virtual machine's {@link Charset#defaultCharset() default charset}. The
2743     * file is always closed.
2744     *
2745     * @param file the file to read, must not be {@code null}
2746     * @return the file contents, never {@code null}
2747     * @throws NullPointerException if file is {@code null}.
2748     * @throws IOException          if an I/O error occurs, including when the file does not exist, is a directory rather than a regular file, or for some other
2749     *                              reason why the file cannot be opened for reading.
2750     * @since 1.3.1
2751     * @deprecated Use {@link #readFileToString(File, Charset)} instead (and specify the appropriate encoding)
2752     */
2753    @Deprecated
2754    public static String readFileToString(final File file) throws IOException {
2755        return readFileToString(file, Charset.defaultCharset());
2756    }
2757
2758    /**
2759     * Reads the contents of a file into a String.
2760     * The file is always closed.
2761     *
2762     * @param file     the file to read, must not be {@code null}
2763     * @param charsetName the name of the requested charset, {@code null} means platform default
2764     * @return the file contents, never {@code null}
2765     * @throws NullPointerException if file is {@code null}.
2766     * @throws IOException if an I/O error occurs, including when the file does not exist, is a directory rather than a
2767     *         regular file, or for some other reason why the file cannot be opened for reading.
2768     * @since 2.3
2769     */
2770    public static String readFileToString(final File file, final Charset charsetName) throws IOException {
2771        return IOUtils.toString(() -> Files.newInputStream(file.toPath()), Charsets.toCharset(charsetName));
2772    }
2773
2774    /**
2775     * Reads the contents of a file into a String. The file is always closed.
2776     *
2777     * @param file     the file to read, must not be {@code null}
2778     * @param charsetName the name of the requested charset, {@code null} means platform default
2779     * @return the file contents, never {@code null}
2780     * @throws NullPointerException if file is {@code null}.
2781     * @throws IOException if an I/O error occurs, including when the file does not exist, is a directory rather than a
2782     *         regular file, or for some other reason why the file cannot be opened for reading.
2783     * @throws java.nio.charset.UnsupportedCharsetException if the named charset is unavailable.
2784     * @since 2.3
2785     */
2786    public static String readFileToString(final File file, final String charsetName) throws IOException {
2787        return readFileToString(file, Charsets.toCharset(charsetName));
2788    }
2789
2790    /**
2791     * Reads the contents of a file line by line to a List of Strings using the virtual machine's {@link Charset#defaultCharset() default charset}.
2792     * The file is always closed.
2793     *
2794     * @param file the file to read, must not be {@code null}
2795     * @return the list of Strings representing each line in the file, never {@code null}
2796     * @throws NullPointerException if file is {@code null}.
2797     * @throws IOException          if an I/O error occurs, including when the file does not exist, is a directory rather than a regular file, or for some other
2798     *                              reason why the file cannot be opened for reading.
2799     * @since 1.3
2800     * @deprecated Use {@link #readLines(File, Charset)} instead (and specify the appropriate encoding)
2801     */
2802    @Deprecated
2803    public static List<String> readLines(final File file) throws IOException {
2804        return readLines(file, Charset.defaultCharset());
2805    }
2806
2807    /**
2808     * Reads the contents of a file line by line to a List of Strings.
2809     * The file is always closed.
2810     *
2811     * @param file     the file to read, must not be {@code null}
2812     * @param charset the charset to use, {@code null} means platform default
2813     * @return the list of Strings representing each line in the file, never {@code null}
2814     * @throws NullPointerException if file is {@code null}.
2815     * @throws IOException if an I/O error occurs, including when the file does not exist, is a directory rather than a
2816     *         regular file, or for some other reason why the file cannot be opened for reading.
2817     * @since 2.3
2818     */
2819    public static List<String> readLines(final File file, final Charset charset) throws IOException {
2820        return Files.readAllLines(file.toPath(), charset);
2821    }
2822
2823    /**
2824     * Reads the contents of a file line by line to a List of Strings. The file is always closed.
2825     *
2826     * @param file     the file to read, must not be {@code null}
2827     * @param charsetName the name of the requested charset, {@code null} means platform default
2828     * @return the list of Strings representing each line in the file, never {@code null}
2829     * @throws NullPointerException if file is {@code null}.
2830     * @throws IOException if an I/O error occurs, including when the file does not exist, is a directory rather than a
2831     *         regular file, or for some other reason why the file cannot be opened for reading.
2832     * @throws java.nio.charset.UnsupportedCharsetException if the named charset is unavailable.
2833     * @since 1.1
2834     */
2835    public static List<String> readLines(final File file, final String charsetName) throws IOException {
2836        return readLines(file, Charsets.toCharset(charsetName));
2837    }
2838
2839    private static void requireAbsent(final File file, final String name) throws FileExistsException {
2840        if (file.exists()) {
2841            throw new FileExistsException(String.format("File element in parameter '%s' already exists: '%s'", name, file));
2842        }
2843    }
2844
2845    /**
2846     * Throws IllegalArgumentException if the given files' canonical representations are equal.
2847     *
2848     * @param file1 The first file to compare.
2849     * @param file2 The second file to compare.
2850     * @throws IOException if an I/O error occurs.
2851     * @throws IllegalArgumentException if the given files' canonical representations are equal.
2852     */
2853    private static void requireCanonicalPathsNotEquals(final File file1, final File file2) throws IOException {
2854        final String canonicalPath = file1.getCanonicalPath();
2855        if (canonicalPath.equals(file2.getCanonicalPath())) {
2856            throw new IllegalArgumentException(String
2857                .format("File canonical paths are equal: '%s' (file1='%s', file2='%s')", canonicalPath, file1, file2));
2858        }
2859    }
2860
2861    /**
2862     * Requires that the given {@link File} exists and is a directory.
2863     *
2864     * @param directory The {@link File} to check.
2865     * @param name The parameter name to use in the exception message in case of null input or if the file is not a directory.
2866     * @throws NullPointerException if the given {@link File} is {@code null}.
2867     * @throws FileNotFoundException if the given {@link File} does not exist
2868     * @throws IllegalArgumentException if the given {@link File} exists but is not a directory.
2869     */
2870    private static void requireDirectoryExists(final File directory, final String name) throws FileNotFoundException {
2871        Objects.requireNonNull(directory, name);
2872        if (!directory.isDirectory()) {
2873            if (directory.exists()) {
2874                throw new IllegalArgumentException("Parameter '" + name + "' is not a directory: '" + directory + "'");
2875            }
2876            throw new FileNotFoundException("Directory '" + directory + "' does not exist.");
2877        }
2878    }
2879
2880    /**
2881     * Requires that the given {@link File} is a directory if it exists.
2882     *
2883     * @param directory The {@link File} to check.
2884     * @param name The parameter name to use in the exception message in case of null input.
2885     * @throws NullPointerException if the given {@link File} is {@code null}.
2886     * @throws IllegalArgumentException if the given {@link File} exists but is not a directory.
2887     */
2888    private static void requireDirectoryIfExists(final File directory, final String name) {
2889        Objects.requireNonNull(directory, name);
2890        if (directory.exists() && !directory.isDirectory()) {
2891            throw new IllegalArgumentException("Parameter '" + name + "' is not a directory: '" + directory + "'");
2892        }
2893    }
2894
2895    /**
2896     * Sets file lastModifiedTime, lastAccessTime and creationTime to match source file
2897     *
2898     * @param sourceFile The source file to query.
2899     * @param targetFile The target file or directory to set.
2900     * @return {@code true} if and only if the operation succeeded;
2901     *          {@code false} otherwise
2902     * @throws NullPointerException if sourceFile is {@code null}.
2903     * @throws NullPointerException if targetFile is {@code null}.
2904     */
2905    private static boolean setTimes(final File sourceFile, final File targetFile) {
2906        Objects.requireNonNull(sourceFile, "sourceFile");
2907        Objects.requireNonNull(targetFile, "targetFile");
2908        try {
2909            // Set creation, modified, last accessed to match source file
2910            final BasicFileAttributes srcAttr = Files.readAttributes(sourceFile.toPath(), BasicFileAttributes.class);
2911            final BasicFileAttributeView destAttrView = Files.getFileAttributeView(targetFile.toPath(), BasicFileAttributeView.class);
2912            // null guards are not needed; BasicFileAttributes.setTimes(...) is null safe
2913            destAttrView.setTimes(srcAttr.lastModifiedTime(), srcAttr.lastAccessTime(), srcAttr.creationTime());
2914            return true;
2915        } catch (final IOException ignored) {
2916            // Fallback: Only set modified time to match source file
2917            return targetFile.setLastModified(sourceFile.lastModified());
2918        }
2919
2920        // TODO: (Help!) Determine historically why setLastModified(File, File) needed PathUtils.setLastModifiedTime() if
2921        //  sourceFile.isFile() was true, but needed setLastModifiedTime(File, long) if sourceFile.isFile() was false
2922    }
2923
2924    /**
2925     * Returns the size of the specified file or directory. If the provided
2926     * {@link File} is a regular file, then the file's length is returned.
2927     * If the argument is a directory, then the size of the directory is
2928     * calculated recursively. If a directory or subdirectory is security
2929     * restricted, its size will not be included.
2930     * <p>
2931     * Note that overflow is not detected, and the return value may be negative if
2932     * overflow occurs. See {@link #sizeOfAsBigInteger(File)} for an alternative
2933     * method that does not overflow.
2934     * </p>
2935     *
2936     * @param file the regular file or directory to return the size
2937     *             of (must not be {@code null}).
2938     *
2939     * @return the length of the file, or recursive size of the directory,
2940     * provided (in bytes).
2941     *
2942     * @throws NullPointerException     if the file is {@code null}.
2943     * @throws IllegalArgumentException if the file does not exist.
2944     * @throws UncheckedIOException if an IO error occurs.
2945     * @since 2.0
2946     */
2947    public static long sizeOf(final File file) {
2948        return Uncheck.getAsLong(() -> PathUtils.sizeOf(file.toPath()));
2949    }
2950
2951    /**
2952     * Returns the size of the specified file or directory. If the provided
2953     * {@link File} is a regular file, then the file's length is returned.
2954     * If the argument is a directory, then the size of the directory is
2955     * calculated recursively. If a directory or subdirectory is security
2956     * restricted, its size will not be included.
2957     *
2958     * @param file the regular file or directory to return the size
2959     *             of (must not be {@code null}).
2960     *
2961     * @return the length of the file, or recursive size of the directory,
2962     * provided (in bytes).
2963     *
2964     * @throws NullPointerException     if the file is {@code null}.
2965     * @throws IllegalArgumentException if the file does not exist.
2966     * @throws UncheckedIOException if an IO error occurs.
2967     * @since 2.4
2968     */
2969    public static BigInteger sizeOfAsBigInteger(final File file) {
2970        return Uncheck.get(() -> PathUtils.sizeOfAsBigInteger(file.toPath()));
2971    }
2972
2973    /**
2974     * Counts the size of a directory recursively (sum of the length of all files).
2975     * <p>
2976     * Note that overflow is not detected, and the return value may be negative if
2977     * overflow occurs. See {@link #sizeOfDirectoryAsBigInteger(File)} for an alternative
2978     * method that does not overflow.
2979     * </p>
2980     *
2981     * @param directory directory to inspect, must not be {@code null}.
2982     * @return size of directory in bytes, 0 if directory is security restricted, a negative number when the real total
2983     * is greater than {@link Long#MAX_VALUE}.
2984     * @throws IllegalArgumentException if the given {@link File} exists but is not a directory
2985     * @throws NullPointerException if the directory is {@code null}.
2986     * @throws UncheckedIOException if an IO error occurs.
2987     */
2988    public static long sizeOfDirectory(final File directory) {
2989        try {
2990            requireDirectoryExists(directory, "directory");
2991        } catch (final FileNotFoundException e) {
2992            throw new UncheckedIOException(e);
2993        }
2994        return Uncheck.getAsLong(() -> PathUtils.sizeOfDirectory(directory.toPath()));
2995    }
2996
2997    /**
2998     * Counts the size of a directory recursively (sum of the length of all files).
2999     *
3000     * @param directory directory to inspect, must not be {@code null}.
3001     * @return size of directory in bytes, 0 if directory is security restricted.
3002     * @throws IllegalArgumentException if the given {@link File} exists but is not a directory
3003     * @throws NullPointerException if the directory is {@code null}.
3004     * @throws UncheckedIOException if an IO error occurs.
3005     * @since 2.4
3006     */
3007    public static BigInteger sizeOfDirectoryAsBigInteger(final File directory) {
3008        try {
3009            requireDirectoryExists(directory, "directory");
3010        } catch (final FileNotFoundException e) {
3011            throw new UncheckedIOException(e);
3012        }
3013        return Uncheck.get(() -> PathUtils.sizeOfDirectoryAsBigInteger(directory.toPath()));
3014    }
3015
3016    /**
3017     * Streams over the files in a given directory (and optionally its subdirectories) which match an array of extensions.
3018     * <p>
3019     * The returned {@link Stream} may wrap one or more {@link DirectoryStream}s. When you require timely disposal of file system resources, use a
3020     * {@code try}-with-resources block to ensure invocation of the stream's {@link Stream#close()} method after the stream operations are completed. Calling a
3021     * closed stream causes a {@link IllegalStateException}.
3022     * </p>
3023     *
3024     * @param directory  the directory to search in
3025     * @param recursive  if true all subdirectories are searched as well
3026     * @param extensions an array of extensions, for example, {"java","xml"}. If this parameter is {@code null}, all files are returned.
3027     * @return a Stream of {@link File} for matching files.
3028     * @throws IOException if an I/O error is thrown when accessing the starting file.
3029     * @since 2.9.0
3030     */
3031    public static Stream<File> streamFiles(final File directory, final boolean recursive, final String... extensions) throws IOException {
3032        // @formatter:off
3033        final IOFileFilter filter = extensions == null
3034            ? FileFileFilter.INSTANCE
3035            : FileFileFilter.INSTANCE.and(toSuffixFileFilter(extensions));
3036        // @formatter:on
3037        return PathUtils.walk(directory.toPath(), filter, toMaxDepth(recursive), false, FileVisitOption.FOLLOW_LINKS).map(Path::toFile);
3038    }
3039
3040    /**
3041     * Converts from a {@link URL} to a {@link File}.
3042     * <p>
3043     * Syntax such as {@code file:///my%20docs/file.txt} will be
3044     * correctly decoded to {@code /my docs/file.txt}.
3045     * UTF-8 is used to decode percent-encoded octets to characters.
3046     * Additionally, malformed percent-encoded octets are handled leniently by
3047     * passing them through literally.
3048     * </p>
3049     *
3050     * @param url the file URL to convert, {@code null} returns {@code null}
3051     * @return the equivalent {@link File} object, or {@code null}
3052     * if the URL's protocol is not {@code file}
3053     */
3054    public static File toFile(final URL url) {
3055        if (url == null || !isFileProtocol(url)) {
3056            return null;
3057        }
3058        final String fileName = url.getFile().replace('/', File.separatorChar);
3059        return new File(decodeUrl(fileName));
3060    }
3061
3062    /**
3063     * Converts each of an array of {@link URL} to a {@link File}.
3064     * <p>
3065     * Returns an array of the same size as the input.
3066     * If the input is {@code null}, an empty array is returned.
3067     * If the input contains {@code null}, the output array contains {@code null} at the same
3068     * index.
3069     * </p>
3070     * <p>
3071     * This method will decode the URL.
3072     * Syntax such as {@code file:///my%20docs/file.txt} will be
3073     * correctly decoded to {@code /my docs/file.txt}.
3074     * </p>
3075     *
3076     * @param urls the file URLs to convert, {@code null} returns empty array
3077     * @return a non-{@code null} array of Files matching the input, with a {@code null} item
3078     * if there was a {@code null} at that index in the input array
3079     * @throws IllegalArgumentException if any file is not a URL file
3080     * @throws IllegalArgumentException if any file is incorrectly encoded
3081     * @since 1.1
3082     */
3083    public static File[] toFiles(final URL... urls) {
3084        if (IOUtils.length(urls) == 0) {
3085            return EMPTY_FILE_ARRAY;
3086        }
3087        final File[] files = new File[urls.length];
3088        for (int i = 0; i < urls.length; i++) {
3089            final URL url = urls[i];
3090            if (url != null) {
3091                if (!isFileProtocol(url)) {
3092                    throw new IllegalArgumentException("Can only convert file URL to a File: " + url);
3093                }
3094                files[i] = toFile(url);
3095            }
3096        }
3097        return files;
3098    }
3099
3100    /**
3101     * Consumes all of the given stream.
3102     * <p>
3103     * When called from a FileTreeWalker, the walker <em>closes</em> the stream because {@link FileTreeWalker#next()} calls {@code top.stream().close()}.
3104     * </p>
3105     *
3106     * @param stream The stream to consume.
3107     * @return a new List.
3108     */
3109    private static List<File> toList(final Stream<File> stream) {
3110        return stream.collect(Collectors.toList());
3111    }
3112
3113    /**
3114     * Converts whether or not to recurse into a recursion max depth.
3115     *
3116     * @param recursive whether or not to recurse
3117     * @return the recursion depth
3118     */
3119    private static int toMaxDepth(final boolean recursive) {
3120        return recursive ? Integer.MAX_VALUE : 1;
3121    }
3122
3123    /**
3124     * Converts an array of file extensions to suffixes.
3125     *
3126     * @param extensions an array of extensions. Format: {"java", "xml"}
3127     * @return an array of suffixes. Format: {".java", ".xml"}
3128     * @throws NullPointerException if the parameter is null
3129     */
3130    private static String[] toSuffixes(final String... extensions) {
3131        return Stream.of(Objects.requireNonNull(extensions, "extensions")).map(s -> s.charAt(0) == '.' ? s : "." + s).toArray(String[]::new);
3132    }
3133
3134    private static SuffixFileFilter toSuffixFileFilter(final String... extensions) {
3135        return new SuffixFileFilter(toSuffixes(extensions));
3136    }
3137
3138    /**
3139     * Implements behavior similar to the Unix "touch" utility. Creates a new file with size 0, or, if the file exists, just
3140     * updates the file's modified time. This method throws an IOException if the last modified date
3141     * of the file cannot be set. It creates parent directories if they do not exist.
3142     *
3143     * @param file the File to touch.
3144     * @throws NullPointerException if the parameter is {@code null}.
3145     * @throws IOException if setting the last-modified time failed or an I/O problem occurs.
3146     */
3147    public static void touch(final File file) throws IOException {
3148        PathUtils.touch(Objects.requireNonNull(file, PROTOCOL_FILE).toPath());
3149    }
3150
3151    /**
3152     * Converts each element of an array of {@link File} to a {@link URL}.
3153     * <p>
3154     * Returns an array of the same size as the input.
3155     * </p>
3156     *
3157     * @param files the files to convert, must not be {@code null}
3158     * @return an array of URLs matching the input
3159     * @throws IOException          if a file cannot be converted
3160     * @throws NullPointerException if any argument is null
3161     */
3162    public static URL[] toURLs(final File... files) throws IOException {
3163        Objects.requireNonNull(files, "files");
3164        final URL[] urls = new URL[files.length];
3165        for (int i = 0; i < urls.length; i++) {
3166            urls[i] = files[i].toURI().toURL();
3167        }
3168        return urls;
3169    }
3170
3171    /**
3172     * Validates the given arguments.
3173     * <ul>
3174     * <li>Throws {@link NullPointerException} if {@code source} is null</li>
3175     * <li>Throws {@link NullPointerException} if {@code destination} is null</li>
3176     * <li>Throws {@link FileNotFoundException} if {@code source} does not exist</li>
3177     * </ul>
3178     *
3179     * @param source      the file or directory to be moved.
3180     * @param destination the destination file or directory.
3181     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
3182     * @throws FileNotFoundException if the source file does not exist.
3183     */
3184    private static void validateMoveParameters(final File source, final File destination) throws FileNotFoundException {
3185        Objects.requireNonNull(source, "source");
3186        Objects.requireNonNull(destination, "destination");
3187        if (!source.exists()) {
3188            throw new FileNotFoundException("Source '" + source + "' does not exist");
3189        }
3190    }
3191
3192    /**
3193     * Waits for the file system to detect a file's presence, with a timeout.
3194     * <p>
3195     * This method repeatedly tests {@link Files#exists(Path, LinkOption...)} until it returns
3196     * true up to the maximum time specified in seconds.
3197     * </p>
3198     *
3199     * @param file    the file to check, must not be {@code null}
3200     * @param seconds the maximum time in seconds to wait
3201     * @return true if file exists
3202     * @throws NullPointerException if the file is {@code null}
3203     */
3204    public static boolean waitFor(final File file, final int seconds) {
3205        Objects.requireNonNull(file, PROTOCOL_FILE);
3206        return PathUtils.waitFor(file.toPath(), Duration.ofSeconds(seconds), PathUtils.EMPTY_LINK_OPTION_ARRAY);
3207    }
3208
3209    /**
3210     * Writes a CharSequence to a file creating the file if it does not exist using the virtual machine's {@link Charset#defaultCharset() default charset}.
3211     *
3212     * @param file the file to write
3213     * @param data the content to write to the file
3214     * @throws IOException in case of an I/O error
3215     * @since 2.0
3216     * @deprecated Use {@link #write(File, CharSequence, Charset)} instead (and specify the appropriate encoding)
3217     */
3218    @Deprecated
3219    public static void write(final File file, final CharSequence data) throws IOException {
3220        write(file, data, Charset.defaultCharset(), false);
3221    }
3222
3223    /**
3224     * Writes a CharSequence to a file creating the file if it does not exist using the virtual machine's {@link Charset#defaultCharset() default charset}.
3225     *
3226     * @param file   the file to write
3227     * @param data   the content to write to the file
3228     * @param append if {@code true}, then the data will be added to the end of the file rather than overwriting
3229     * @throws IOException in case of an I/O error
3230     * @since 2.1
3231     * @deprecated Use {@link #write(File, CharSequence, Charset, boolean)} instead (and specify the appropriate encoding)
3232     */
3233    @Deprecated
3234    public static void write(final File file, final CharSequence data, final boolean append) throws IOException {
3235        write(file, data, Charset.defaultCharset(), append);
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 name of the requested charset, {@code null} means platform default
3244     * @throws IOException in case of an I/O error
3245     * @since 2.3
3246     */
3247    public static void write(final File file, final CharSequence data, final Charset charset) throws IOException {
3248        write(file, data, charset, false);
3249    }
3250
3251    /**
3252     * Writes a CharSequence to a file creating the file if it does not exist.
3253     *
3254     * @param file     the file to write
3255     * @param data     the content to write to the file
3256     * @param charset the charset to use, {@code null} means platform default
3257     * @param append   if {@code true}, then the data will be added to the
3258     *                 end of the file rather than overwriting
3259     * @throws IOException in case of an I/O error
3260     * @since 2.3
3261     */
3262    public static void write(final File file, final CharSequence data, final Charset charset, final boolean append) throws IOException {
3263        writeStringToFile(file, Objects.toString(data, null), charset, append);
3264    }
3265
3266    /**
3267     * Writes a CharSequence to a file creating the file if it does not exist.
3268     *
3269     * @param file     the file to write
3270     * @param data     the content to write to the file
3271     * @param charsetName the name of the requested charset, {@code null} means platform default
3272     * @throws IOException                          in case of an I/O error
3273     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
3274     * @since 2.0
3275     */
3276    public static void write(final File file, final CharSequence data, final String charsetName) throws IOException {
3277        write(file, data, charsetName, false);
3278    }
3279
3280    /**
3281     * Writes a CharSequence to a file creating the file if it does not exist.
3282     *
3283     * @param file     the file to write
3284     * @param data     the content to write to the file
3285     * @param charsetName the name of the requested charset, {@code null} means platform default
3286     * @param append   if {@code true}, then the data will be added to the
3287     *                 end of the file rather than overwriting
3288     * @throws IOException                 in case of an I/O error
3289     * @throws java.nio.charset.UnsupportedCharsetException if the encoding is not supported by the VM
3290     * @since 2.1
3291     */
3292    public static void write(final File file, final CharSequence data, final String charsetName, final boolean append) throws IOException {
3293        write(file, data, Charsets.toCharset(charsetName), append);
3294    }
3295
3296    // Must be called with a directory
3297
3298    /**
3299     * Writes a byte array to a file creating the file if it does not exist.
3300     * The parent directories of the file will be created if they do not exist.
3301     *
3302     * @param file the file to write to
3303     * @param data the content to write to the file
3304     * @throws IOException in case of an I/O error
3305     * @since 1.1
3306     */
3307    public static void writeByteArrayToFile(final File file, final byte[] data) throws IOException {
3308        writeByteArrayToFile(file, data, false);
3309    }
3310
3311    /**
3312     * Writes a byte array to a file creating the file if it does not exist.
3313     *
3314     * @param file   the file to write to
3315     * @param data   the content to write to the file
3316     * @param append if {@code true}, then bytes will be added to the
3317     *               end of the file rather than overwriting
3318     * @throws IOException in case of an I/O error
3319     * @since 2.1
3320     */
3321    public static void writeByteArrayToFile(final File file, final byte[] data, final boolean append) throws IOException {
3322        writeByteArrayToFile(file, data, 0, data.length, append);
3323    }
3324
3325    /**
3326     * Writes {@code len} bytes from the specified byte array starting
3327     * at offset {@code off} to a file, creating the file if it does
3328     * not exist.
3329     *
3330     * @param file the file to write to
3331     * @param data the content to write to the file
3332     * @param off  the start offset in the data
3333     * @param len  the number of bytes to write
3334     * @throws IOException in case of an I/O error
3335     * @since 2.5
3336     */
3337    public static void writeByteArrayToFile(final File file, final byte[] data, final int off, final int len) throws IOException {
3338        writeByteArrayToFile(file, data, off, len, false);
3339    }
3340
3341    /**
3342     * Writes {@code len} bytes from the specified byte array starting
3343     * at offset {@code off} to a file, creating the file if it does
3344     * not exist.
3345     *
3346     * @param file   the file to write to
3347     * @param data   the content to write to the file
3348     * @param off    the start offset in the data
3349     * @param len    the number of bytes to write
3350     * @param append if {@code true}, then bytes will be added to the
3351     *               end of the file rather than overwriting
3352     * @throws IOException in case of an I/O error
3353     * @since 2.5
3354     */
3355    public static void writeByteArrayToFile(final File file, final byte[] data, final int off, final int len, final boolean append) throws IOException {
3356        try (OutputStream out = newOutputStream(file, append)) {
3357            out.write(data, off, len);
3358        }
3359    }
3360
3361    /**
3362     * Writes the {@code toString()} value of each item in a collection to
3363     * the specified {@link File} line by line.
3364     * The default VM encoding and the default line ending will be used.
3365     *
3366     * @param file  the file to write to
3367     * @param lines the lines to write, {@code null} entries produce blank lines
3368     * @throws IOException in case of an I/O error
3369     * @since 1.3
3370     */
3371    public static void writeLines(final File file, final Collection<?> lines) throws IOException {
3372        writeLines(file, null, lines, null, false);
3373    }
3374
3375    /**
3376     * Writes the {@code toString()} value of each item in a collection to
3377     * the specified {@link File} line by line.
3378     * The default VM encoding and the default line ending will be used.
3379     *
3380     * @param file   the file to write to
3381     * @param lines  the lines to write, {@code null} entries produce blank lines
3382     * @param append if {@code true}, then the lines will be added to the
3383     *               end of the file rather than overwriting
3384     * @throws IOException in case of an I/O error
3385     * @since 2.1
3386     */
3387    public static void writeLines(final File file, final Collection<?> lines, final boolean append) throws IOException {
3388        writeLines(file, null, lines, null, append);
3389    }
3390
3391    /**
3392     * Writes the {@code toString()} value of each item in a collection to
3393     * the specified {@link File} line by line.
3394     * The default VM encoding and the specified line ending will be used.
3395     *
3396     * @param file       the file to write to
3397     * @param lines      the lines to write, {@code null} entries produce blank lines
3398     * @param lineEnding the line separator to use, {@code null} is system default
3399     * @throws IOException in case of an I/O error
3400     * @since 1.3
3401     */
3402    public static void writeLines(final File file, final Collection<?> lines, final String lineEnding) throws IOException {
3403        writeLines(file, null, lines, lineEnding, false);
3404    }
3405
3406    /**
3407     * Writes the {@code toString()} value of each item in a collection to
3408     * the specified {@link File} line by line.
3409     * The default VM encoding and the specified line ending will be used.
3410     *
3411     * @param file       the file to write to
3412     * @param lines      the lines to write, {@code null} entries produce blank lines
3413     * @param lineEnding the line separator to use, {@code null} is system default
3414     * @param append     if {@code true}, then the lines will be added to the
3415     *                   end of the file rather than overwriting
3416     * @throws IOException in case of an I/O error
3417     * @since 2.1
3418     */
3419    public static void writeLines(final File file, final Collection<?> lines, final String lineEnding, final boolean append) throws IOException {
3420        writeLines(file, null, lines, lineEnding, append);
3421    }
3422
3423    /**
3424     * Writes the {@code toString()} value of each item in a collection to
3425     * the specified {@link File} line by line.
3426     * The specified character encoding and the default line ending will be used.
3427     * The parent directories of the file will be created if they do not exist.
3428     *
3429     * @param file     the file to write to
3430     * @param charsetName the name of the requested charset, {@code null} means platform default
3431     * @param lines    the lines to write, {@code null} entries produce blank lines
3432     * @throws IOException                          in case of an I/O error
3433     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
3434     * @since 1.1
3435     */
3436    public static void writeLines(final File file, final String charsetName, final Collection<?> lines) throws IOException {
3437        writeLines(file, charsetName, lines, null, false);
3438    }
3439
3440    /**
3441     * Writes the {@code toString()} value of each item in a collection to
3442     * the specified {@link File} line by line, optionally appending.
3443     * The specified character encoding and the default line ending will be used.
3444     *
3445     * @param file     the file to write to
3446     * @param charsetName the name of the requested charset, {@code null} means platform default
3447     * @param lines    the lines to write, {@code null} entries produce blank lines
3448     * @param append   if {@code true}, then the lines will be added to the
3449     *                 end of the file rather than overwriting
3450     * @throws IOException                          in case of an I/O error
3451     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
3452     * @since 2.1
3453     */
3454    public static void writeLines(final File file, final String charsetName, final Collection<?> lines, final boolean append) throws IOException {
3455        writeLines(file, charsetName, lines, null, append);
3456    }
3457
3458    /**
3459     * Writes the {@code toString()} value of each item in a collection to
3460     * the specified {@link File} line by line.
3461     * The specified character encoding and the line ending will be used.
3462     * The parent directories of the file will be created if they do not exist.
3463     *
3464     * @param file       the file to write to
3465     * @param charsetName   the name of the requested charset, {@code null} means platform default
3466     * @param lines      the lines to write, {@code null} entries produce blank lines
3467     * @param lineEnding the line separator to use, {@code null} is system default
3468     * @throws IOException                          in case of an I/O error
3469     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
3470     * @since 1.1
3471     */
3472    public static void writeLines(final File file, final String charsetName, final Collection<?> lines, final String lineEnding) throws IOException {
3473        writeLines(file, charsetName, lines, lineEnding, false);
3474    }
3475
3476    /**
3477     * Writes the {@code toString()} value of each item in a collection to
3478     * the specified {@link File} line by line.
3479     * The specified character encoding and the line ending will be used.
3480     *
3481     * @param file       the file to write to
3482     * @param charsetName   the name of the requested charset, {@code null} means platform default
3483     * @param lines      the lines to write, {@code null} entries produce blank lines
3484     * @param lineEnding the line separator to use, {@code null} is system default
3485     * @param append     if {@code true}, then the lines will be added to the
3486     *                   end of the file rather than overwriting
3487     * @throws IOException                          in case of an I/O error
3488     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
3489     * @since 2.1
3490     */
3491    public static void writeLines(final File file, final String charsetName, final Collection<?> lines, final String lineEnding, final boolean append)
3492        throws IOException {
3493        try (OutputStream out = new BufferedOutputStream(newOutputStream(file, append))) {
3494            IOUtils.writeLines(lines, lineEnding, out, charsetName);
3495        }
3496    }
3497
3498    /**
3499     * Writes a String to a file creating the file if it does not exist using the virtual machine's {@link Charset#defaultCharset() default charset}.
3500     *
3501     * @param file the file to write
3502     * @param data the content to write to the file
3503     * @throws IOException in case of an I/O error
3504     * @deprecated Use {@link #writeStringToFile(File, String, Charset)} instead (and specify the appropriate encoding)
3505     */
3506    @Deprecated
3507    public static void writeStringToFile(final File file, final String data) throws IOException {
3508        writeStringToFile(file, data, Charset.defaultCharset(), false);
3509    }
3510
3511    /**
3512     * Writes a String to a file creating the file if it does not exist using the virtual machine's {@link Charset#defaultCharset() default charset}.
3513     *
3514     * @param file   the file to write
3515     * @param data   the content to write to the file
3516     * @param append if {@code true}, then the String will be added to the end of the file rather than overwriting
3517     * @throws IOException in case of an I/O error
3518     * @since 2.1
3519     * @deprecated Use {@link #writeStringToFile(File, String, Charset, boolean)} instead (and specify the appropriate encoding)
3520     */
3521    @Deprecated
3522    public static void writeStringToFile(final File file, final String data, final boolean append) throws IOException {
3523        writeStringToFile(file, data, Charset.defaultCharset(), append);
3524    }
3525
3526    /**
3527     * Writes a String to a file creating the file if it does not exist.
3528     * The parent directories of the file will be created if they do not exist.
3529     *
3530     * @param file     the file to write
3531     * @param data     the content to write to the file
3532     * @param charset the charset to use, {@code null} means platform default
3533     * @throws IOException                          in case of an I/O error
3534     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
3535     * @since 2.4
3536     */
3537    public static void writeStringToFile(final File file, final String data, final Charset charset) throws IOException {
3538        writeStringToFile(file, data, charset, false);
3539    }
3540
3541    /**
3542     * Writes a String to a file, creating the file if it does not exist.
3543     * The parent directories of the file are created if they do not exist.
3544     *
3545     * @param file     the file to write
3546     * @param data     the content to write to the file
3547     * @param charset the charset to use, {@code null} means platform default
3548     * @param append   if {@code true}, then the String will be added to the
3549     *                 end of the file rather than overwriting
3550     * @throws IOException in case of an I/O error
3551     * @since 2.3
3552     */
3553    public static void writeStringToFile(final File file, final String data, final Charset charset, final boolean append) throws IOException {
3554        try (OutputStream out = newOutputStream(file, append)) {
3555            IOUtils.write(data, out, charset);
3556        }
3557    }
3558
3559    /**
3560     * Writes a String to a file, creating the file if it does not exist.
3561     * The parent directories of the file are created if they do not exist.
3562     *
3563     * @param file     the file to write
3564     * @param data     the content to write to the file
3565     * @param charsetName the name of the requested charset, {@code null} means platform default
3566     * @throws IOException                          in case of an I/O error
3567     * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
3568     */
3569    public static void writeStringToFile(final File file, final String data, final String charsetName) throws IOException {
3570        writeStringToFile(file, data, charsetName, false);
3571    }
3572
3573    /**
3574     * Writes a String to a file, creating the file if it does not exist.
3575     * The parent directories of the file are created if they do not exist.
3576     *
3577     * @param file     the file to write
3578     * @param data     the content to write to the file
3579     * @param charsetName the name of the requested charset, {@code null} means platform default
3580     * @param append   if {@code true}, then the String will be added to the
3581     *                 end of the file rather than overwriting
3582     * @throws IOException                 in case of an I/O error
3583     * @throws java.nio.charset.UnsupportedCharsetException if the encoding is not supported by the VM
3584     * @since 2.1
3585     */
3586    public static void writeStringToFile(final File file, final String data, final String charsetName, final boolean append) throws IOException {
3587        writeStringToFile(file, data, Charsets.toCharset(charsetName), append);
3588    }
3589
3590    /**
3591     * Instances should NOT be constructed in standard programming.
3592     *
3593     * @deprecated TODO Make private in 3.0.
3594     */
3595    @Deprecated
3596    public FileUtils() { //NOSONAR
3597        // empty
3598    }
3599
3600}