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