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