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