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