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