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