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