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