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