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