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