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