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