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