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