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