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