View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.io;
18  
19  import java.io.File;
20  import java.io.FileFilter;
21  import java.io.FileInputStream;
22  import java.io.FileNotFoundException;
23  import java.io.FileOutputStream;
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.io.OutputStream;
27  import java.net.URL;
28  import java.nio.channels.FileChannel;
29  import java.util.ArrayList;
30  import java.util.Collection;
31  import java.util.Date;
32  import java.util.Iterator;
33  import java.util.List;
34  import java.util.zip.CRC32;
35  import java.util.zip.CheckedInputStream;
36  import java.util.zip.Checksum;
37  
38  import org.apache.commons.io.filefilter.DirectoryFileFilter;
39  import org.apache.commons.io.filefilter.FalseFileFilter;
40  import org.apache.commons.io.filefilter.FileFilterUtils;
41  import org.apache.commons.io.filefilter.IOFileFilter;
42  import org.apache.commons.io.filefilter.SuffixFileFilter;
43  import org.apache.commons.io.filefilter.TrueFileFilter;
44  import org.apache.commons.io.output.NullOutputStream;
45  
46  /**
47   * General file manipulation utilities.
48   * <p>
49   * Facilities are provided in the following areas:
50   * <ul>
51   * <li>writing to a file
52   * <li>reading from a file
53   * <li>make a directory including parent directories
54   * <li>copying files and directories
55   * <li>deleting files and directories
56   * <li>converting to and from a URL
57   * <li>listing files and directories by filter and extension
58   * <li>comparing file content
59   * <li>file last changed date
60   * <li>calculating a checksum
61   * </ul>
62   * <p>
63   * Origin of code: Excalibur, Alexandria, Commons-Utils
64   *
65   * @author <a href="mailto:burton@relativity.yi.org">Kevin A. Burton</A>
66   * @author <a href="mailto:sanders@apache.org">Scott Sanders</a>
67   * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
68   * @author <a href="mailto:Christoph.Reck@dlr.de">Christoph.Reck</a>
69   * @author <a href="mailto:peter@apache.org">Peter Donald</a>
70   * @author <a href="mailto:jefft@apache.org">Jeff Turner</a>
71   * @author Matthew Hawthorne
72   * @author <a href="mailto:jeremias@apache.org">Jeremias Maerki</a>
73   * @author Stephen Colebourne
74   * @author Ian Springer
75   * @author Chris Eldredge
76   * @author Jim Harrington
77   * @author Niall Pemberton
78   * @author Sandy McArthur
79   * @version $Id: FileUtils.java 619188 2008-02-06 22:33:04Z niallp $
80   */
81  public class FileUtils {
82  
83      /**
84       * Instances should NOT be constructed in standard programming.
85       */
86      public FileUtils() {
87          super();
88      }
89  
90      /**
91       * The number of bytes in a kilobyte.
92       */
93      public static final long ONE_KB = 1024;
94  
95      /**
96       * The number of bytes in a megabyte.
97       */
98      public static final long ONE_MB = ONE_KB * ONE_KB;
99  
100     /**
101      * The number of bytes in a gigabyte.
102      */
103     public static final long ONE_GB = ONE_KB * ONE_MB;
104 
105     /**
106      * An empty array of type <code>File</code>.
107      */
108     public static final File[] EMPTY_FILE_ARRAY = new File[0];
109 
110     //-----------------------------------------------------------------------
111     /**
112      * Opens a {@link FileInputStream} for the specified file, providing better
113      * error messages than simply calling <code>new FileInputStream(file)</code>.
114      * <p>
115      * At the end of the method either the stream will be successfully opened,
116      * or an exception will have been thrown.
117      * <p>
118      * An exception is thrown if the file does not exist.
119      * An exception is thrown if the file object exists but is a directory.
120      * An exception is thrown if the file exists but cannot be read.
121      * 
122      * @param file  the file to open for input, must not be <code>null</code>
123      * @return a new {@link FileInputStream} for the specified file
124      * @throws FileNotFoundException if the file does not exist
125      * @throws IOException if the file object is a directory
126      * @throws IOException if the file cannot be read
127      * @since Commons IO 1.3
128      */
129     public static FileInputStream openInputStream(File file) throws IOException {
130         if (file.exists()) {
131             if (file.isDirectory()) {
132                 throw new IOException("File '" + file + "' exists but is a directory");
133             }
134             if (file.canRead() == false) {
135                 throw new IOException("File '" + file + "' cannot be read");
136             }
137         } else {
138             throw new FileNotFoundException("File '" + file + "' does not exist");
139         }
140         return new FileInputStream(file);
141     }
142 
143     //-----------------------------------------------------------------------
144     /**
145      * Opens a {@link FileOutputStream} for the specified file, checking and
146      * creating the parent directory if it does not exist.
147      * <p>
148      * At the end of the method either the stream will be successfully opened,
149      * or an exception will have been thrown.
150      * <p>
151      * The parent directory will be created if it does not exist.
152      * The file will be created if it does not exist.
153      * An exception is thrown if the file object exists but is a directory.
154      * An exception is thrown if the file exists but cannot be written to.
155      * An exception is thrown if the parent directory cannot be created.
156      * 
157      * @param file  the file to open for output, must not be <code>null</code>
158      * @return a new {@link FileOutputStream} for the specified file
159      * @throws IOException if the file object is a directory
160      * @throws IOException if the file cannot be written to
161      * @throws IOException if a parent directory needs creating but that fails
162      * @since Commons IO 1.3
163      */
164     public static FileOutputStream openOutputStream(File file) throws IOException {
165         if (file.exists()) {
166             if (file.isDirectory()) {
167                 throw new IOException("File '" + file + "' exists but is a directory");
168             }
169             if (file.canWrite() == false) {
170                 throw new IOException("File '" + file + "' cannot be written to");
171             }
172         } else {
173             File parent = file.getParentFile();
174             if (parent != null && parent.exists() == false) {
175                 if (parent.mkdirs() == false) {
176                     throw new IOException("File '" + file + "' could not be created");
177                 }
178             }
179         }
180         return new FileOutputStream(file);
181     }
182 
183     //-----------------------------------------------------------------------
184     /**
185      * Returns a human-readable version of the file size, where the input
186      * represents a specific number of bytes.
187      *
188      * @param size  the number of bytes
189      * @return a human-readable display value (includes units)
190      */
191     public static String byteCountToDisplaySize(long size) {
192         String displaySize;
193 
194         if (size / ONE_GB > 0) {
195             displaySize = String.valueOf(size / ONE_GB) + " GB";
196         } else if (size / ONE_MB > 0) {
197             displaySize = String.valueOf(size / ONE_MB) + " MB";
198         } else if (size / ONE_KB > 0) {
199             displaySize = String.valueOf(size / ONE_KB) + " KB";
200         } else {
201             displaySize = String.valueOf(size) + " bytes";
202         }
203         return displaySize;
204     }
205 
206     //-----------------------------------------------------------------------
207     /**
208      * Implements the same behaviour as the "touch" utility on Unix. It creates
209      * a new file with size 0 or, if the file exists already, it is opened and
210      * closed without modifying it, but updating the file date and time.
211      * <p>
212      * NOTE: As from v1.3, this method throws an IOException if the last
213      * modified date of the file cannot be set. Also, as from v1.3 this method
214      * creates parent directories if they do not exist.
215      *
216      * @param file  the File to touch
217      * @throws IOException If an I/O problem occurs
218      */
219     public static void touch(File file) throws IOException {
220         if (!file.exists()) {
221             OutputStream out = openOutputStream(file);
222             IOUtils.closeQuietly(out);
223         }
224         boolean success = file.setLastModified(System.currentTimeMillis());
225         if (!success) {
226             throw new IOException("Unable to set the last modification time for " + file);
227         }
228     }
229 
230     //-----------------------------------------------------------------------
231     /**
232      * Converts a Collection containing java.io.File instanced into array
233      * representation. This is to account for the difference between
234      * File.listFiles() and FileUtils.listFiles().
235      *
236      * @param files  a Collection containing java.io.File instances
237      * @return an array of java.io.File
238      */
239     public static File[] convertFileCollectionToFileArray(Collection<File> files) {
240          return files.toArray(new File[files.size()]);
241     }
242 
243     //-----------------------------------------------------------------------
244     /**
245      * Finds files within a given directory (and optionally its
246      * subdirectories). All files found are filtered by an IOFileFilter.
247      *
248      * @param files the collection of files found.
249      * @param directory the directory to search in.
250      * @param filter the filter to apply to files and directories.
251      */
252     private static void innerListFiles(Collection<File> files, File directory,
253             IOFileFilter filter) {
254         File[] found = directory.listFiles((FileFilter) filter);
255         if (found != null) {
256             for (int i = 0; i < found.length; i++) {
257                 if (found[i].isDirectory()) {
258                     innerListFiles(files, found[i], filter);
259                 } else {
260                     files.add(found[i]);
261                 }
262             }
263         }
264     }
265 
266     /**
267      * Finds files within a given directory (and optionally its
268      * subdirectories). All files found are filtered by an IOFileFilter.
269      * <p>
270      * If your search should recurse into subdirectories you can pass in
271      * an IOFileFilter for directories. You don't need to bind a
272      * DirectoryFileFilter (via logical AND) to this filter. This method does
273      * that for you.
274      * <p>
275      * An example: If you want to search through all directories called
276      * "temp" you pass in <code>FileFilterUtils.NameFileFilter("temp")</code>
277      * <p>
278      * Another common usage of this method is find files in a directory
279      * tree but ignoring the directories generated CVS. You can simply pass
280      * in <code>FileFilterUtils.makeCVSAware(null)</code>.
281      *
282      * @param directory  the directory to search in
283      * @param fileFilter  filter to apply when finding files.
284      * @param dirFilter  optional filter to apply when finding subdirectories.
285      * If this parameter is <code>null</code>, subdirectories will not be included in the
286      * search. Use TrueFileFilter.INSTANCE to match all directories.
287      * @return an collection of java.io.File with the matching files
288      * @see org.apache.commons.io.filefilter.FileFilterUtils
289      * @see org.apache.commons.io.filefilter.NameFileFilter
290      */
291     public static Collection<File> listFiles(
292             File directory, IOFileFilter fileFilter, IOFileFilter dirFilter) {
293         if (!directory.isDirectory()) {
294             throw new IllegalArgumentException(
295                     "Parameter 'directory' is not a directory");
296         }
297         if (fileFilter == null) {
298             throw new NullPointerException("Parameter 'fileFilter' is null");
299         }
300 
301         //Setup effective file filter
302         IOFileFilter effFileFilter = FileFilterUtils.andFileFilter(fileFilter,
303             FileFilterUtils.notFileFilter(DirectoryFileFilter.INSTANCE));
304 
305         //Setup effective directory filter
306         IOFileFilter effDirFilter;
307         if (dirFilter == null) {
308             effDirFilter = FalseFileFilter.INSTANCE;
309         } else {
310             effDirFilter = FileFilterUtils.andFileFilter(dirFilter,
311                 DirectoryFileFilter.INSTANCE);
312         }
313 
314         //Find files
315         Collection<File> files = new java.util.LinkedList<File>();
316         innerListFiles(files, directory,
317             FileFilterUtils.orFileFilter(effFileFilter, effDirFilter));
318         return files;
319     }
320 
321     /**
322      * Allows iteration over the files in given directory (and optionally
323      * its subdirectories).
324      * <p>
325      * All files found are filtered by an IOFileFilter. This method is
326      * based on {@link #listFiles(File, IOFileFilter, IOFileFilter)}.
327      *
328      * @param directory  the directory to search in
329      * @param fileFilter  filter to apply when finding files.
330      * @param dirFilter  optional filter to apply when finding subdirectories.
331      * If this parameter is <code>null</code>, subdirectories will not be included in the
332      * search. Use TrueFileFilter.INSTANCE to match all directories.
333      * @return an iterator of java.io.File for the matching files
334      * @see org.apache.commons.io.filefilter.FileFilterUtils
335      * @see org.apache.commons.io.filefilter.NameFileFilter
336      * @since Commons IO 1.2
337      */
338     public static Iterator iterateFiles(
339             File directory, IOFileFilter fileFilter, IOFileFilter dirFilter) {
340         return listFiles(directory, fileFilter, dirFilter).iterator();
341     }
342 
343     //-----------------------------------------------------------------------
344     /**
345      * Converts an array of file extensions to suffixes for use
346      * with IOFileFilters.
347      *
348      * @param extensions  an array of extensions. Format: {"java", "xml"}
349      * @return an array of suffixes. Format: {".java", ".xml"}
350      */
351     private static String[] toSuffixes(String[] extensions) {
352         String[] suffixes = new String[extensions.length];
353         for (int i = 0; i < extensions.length; i++) {
354             suffixes[i] = "." + extensions[i];
355         }
356         return suffixes;
357     }
358 
359 
360     /**
361      * Finds files within a given directory (and optionally its subdirectories)
362      * which match an array of extensions.
363      *
364      * @param directory  the directory to search in
365      * @param extensions  an array of extensions, ex. {"java","xml"}. If this
366      * parameter is <code>null</code>, all files are returned.
367      * @param recursive  if true all subdirectories are searched as well
368      * @return an collection of java.io.File with the matching files
369      */
370     public static Collection listFiles(
371             File directory, String[] extensions, boolean recursive) {
372         IOFileFilter filter;
373         if (extensions == null) {
374             filter = TrueFileFilter.INSTANCE;
375         } else {
376             String[] suffixes = toSuffixes(extensions);
377             filter = new SuffixFileFilter(suffixes);
378         }
379         return listFiles(directory, filter,
380             (recursive ? TrueFileFilter.INSTANCE : FalseFileFilter.INSTANCE));
381     }
382 
383     /**
384      * Allows iteration over the files in a given directory (and optionally
385      * its subdirectories) which match an array of extensions. This method
386      * is based on {@link #listFiles(File, String[], boolean)}.
387      *
388      * @param directory  the directory to search in
389      * @param extensions  an array of extensions, ex. {"java","xml"}. If this
390      * parameter is <code>null</code>, all files are returned.
391      * @param recursive  if true all subdirectories are searched as well
392      * @return an iterator of java.io.File with the matching files
393      * @since Commons IO 1.2
394      */
395     public static Iterator iterateFiles(
396             File directory, String[] extensions, boolean recursive) {
397         return listFiles(directory, extensions, recursive).iterator();
398     }
399 
400     //-----------------------------------------------------------------------
401     /**
402      * Compares the contents of two files to determine if they are equal or not.
403      * <p>
404      * This method checks to see if the two files are different lengths
405      * or if they point to the same file, before resorting to byte-by-byte
406      * comparison of the contents.
407      * <p>
408      * Code origin: Avalon
409      *
410      * @param file1  the first file
411      * @param file2  the second file
412      * @return true if the content of the files are equal or they both don't
413      * exist, false otherwise
414      * @throws IOException in case of an I/O error
415      */
416     public static boolean contentEquals(File file1, File file2) throws IOException {
417         boolean file1Exists = file1.exists();
418         if (file1Exists != file2.exists()) {
419             return false;
420         }
421 
422         if (!file1Exists) {
423             // two not existing files are equal
424             return true;
425         }
426 
427         if (file1.isDirectory() || file2.isDirectory()) {
428             // don't want to compare directory contents
429             throw new IOException("Can't compare directories, only files");
430         }
431 
432         if (file1.length() != file2.length()) {
433             // lengths differ, cannot be equal
434             return false;
435         }
436 
437         if (file1.getCanonicalFile().equals(file2.getCanonicalFile())) {
438             // same file
439             return true;
440         }
441 
442         InputStream input1 = null;
443         InputStream input2 = null;
444         try {
445             input1 = new FileInputStream(file1);
446             input2 = new FileInputStream(file2);
447             return IOUtils.contentEquals(input1, input2);
448 
449         } finally {
450             IOUtils.closeQuietly(input1);
451             IOUtils.closeQuietly(input2);
452         }
453     }
454 
455     //-----------------------------------------------------------------------
456     /**
457      * Convert from a <code>URL</code> to a <code>File</code>.
458      * <p>
459      * From version 1.1 this method will decode the URL.
460      * Syntax such as <code>file:///my%20docs/file.txt</code> will be
461      * correctly decoded to <code>/my docs/file.txt</code>.
462      *
463      * @param url  the file URL to convert, <code>null</code> returns <code>null</code>
464      * @return the equivalent <code>File</code> object, or <code>null</code>
465      *  if the URL's protocol is not <code>file</code>
466      * @throws IllegalArgumentException if the file is incorrectly encoded
467      */
468     public static File toFile(URL url) {
469         if (url == null || !url.getProtocol().equals("file")) {
470             return null;
471         } else {
472             String filename = url.getFile().replace('/', File.separatorChar);
473             int pos =0;
474             while ((pos = filename.indexOf('%', pos)) >= 0) {
475                 if (pos + 2 < filename.length()) {
476                     String hexStr = filename.substring(pos + 1, pos + 3);
477                     char ch = (char) Integer.parseInt(hexStr, 16);
478                     filename = filename.substring(0, pos) + ch + filename.substring(pos + 3);
479                 }
480             }
481             return new File(filename);
482         }
483     }
484 
485     /**
486      * Converts each of an array of <code>URL</code> to a <code>File</code>.
487      * <p>
488      * Returns an array of the same size as the input.
489      * If the input is <code>null</code>, an empty array is returned.
490      * If the input contains <code>null</code>, the output array contains <code>null</code> at the same
491      * index.
492      * <p>
493      * This method will decode the URL.
494      * Syntax such as <code>file:///my%20docs/file.txt</code> will be
495      * correctly decoded to <code>/my docs/file.txt</code>.
496      *
497      * @param urls  the file URLs to convert, <code>null</code> returns empty array
498      * @return a non-<code>null</code> array of Files matching the input, with a <code>null</code> item
499      *  if there was a <code>null</code> at that index in the input array
500      * @throws IllegalArgumentException if any file is not a URL file
501      * @throws IllegalArgumentException if any file is incorrectly encoded
502      * @since Commons IO 1.1
503      */
504     public static File[] toFiles(URL[] urls) {
505         if (urls == null || urls.length == 0) {
506             return EMPTY_FILE_ARRAY;
507         }
508         File[] files = new File[urls.length];
509         for (int i = 0; i < urls.length; i++) {
510             URL url = urls[i];
511             if (url != null) {
512                 if (url.getProtocol().equals("file") == false) {
513                     throw new IllegalArgumentException(
514                             "URL could not be converted to a File: " + url);
515                 }
516                 files[i] = toFile(url);
517             }
518         }
519         return files;
520     }
521 
522     /**
523      * Converts each of an array of <code>File</code> to a <code>URL</code>.
524      * <p>
525      * Returns an array of the same size as the input.
526      *
527      * @param files  the files to convert
528      * @return an array of URLs matching the input
529      * @throws IOException if a file cannot be converted
530      */
531     public static URL[] toURLs(File[] files) throws IOException {
532         URL[] urls = new URL[files.length];
533 
534         for (int i = 0; i < urls.length; i++) {
535             urls[i] = files[i].toURL();
536         }
537 
538         return urls;
539     }
540 
541     //-----------------------------------------------------------------------
542     /**
543      * Copies a file to a directory preserving the file date.
544      * <p>
545      * This method copies the contents of the specified source file
546      * to a file of the same name in the specified destination directory.
547      * The destination directory is created if it does not exist.
548      * If the destination file exists, then this method will overwrite it.
549      *
550      * @param srcFile  an existing file to copy, must not be <code>null</code>
551      * @param destDir  the directory to place the copy in, must not be <code>null</code>
552      *
553      * @throws NullPointerException if source or destination is null
554      * @throws IOException if source or destination is invalid
555      * @throws IOException if an IO error occurs during copying
556      * @see #copyFile(File, File, boolean)
557      */
558     public static void copyFileToDirectory(File srcFile, File destDir) throws IOException {
559         copyFileToDirectory(srcFile, destDir, true);
560     }
561 
562     /**
563      * Copies a file to a directory optionally preserving the file date.
564      * <p>
565      * This method copies the contents of the specified source file
566      * to a file of the same name in the specified destination directory.
567      * The destination directory is created if it does not exist.
568      * If the destination file exists, then this method will overwrite it.
569      *
570      * @param srcFile  an existing file to copy, must not be <code>null</code>
571      * @param destDir  the directory to place the copy in, must not be <code>null</code>
572      * @param preserveFileDate  true if the file date of the copy
573      *  should be the same as the original
574      *
575      * @throws NullPointerException if source or destination is <code>null</code>
576      * @throws IOException if source or destination is invalid
577      * @throws IOException if an IO error occurs during copying
578      * @see #copyFile(File, File, boolean)
579      * @since Commons IO 1.3
580      */
581     public static void copyFileToDirectory(File srcFile, File destDir, boolean preserveFileDate) throws IOException {
582         if (destDir == null) {
583             throw new NullPointerException("Destination must not be null");
584         }
585         if (destDir.exists() && destDir.isDirectory() == false) {
586             throw new IllegalArgumentException("Destination '" + destDir + "' is not a directory");
587         }
588         copyFile(srcFile, new File(destDir, srcFile.getName()), preserveFileDate);
589     }
590 
591     /**
592      * Copies a file to a new location preserving the file date.
593      * <p>
594      * This method copies the contents of the specified source file to the
595      * specified destination file. The directory holding the destination file is
596      * created if it does not exist. If the destination file exists, then this
597      * method will overwrite it.
598      * 
599      * @param srcFile  an existing file to copy, must not be <code>null</code>
600      * @param destFile  the new file, must not be <code>null</code>
601      * 
602      * @throws NullPointerException if source or destination is <code>null</code>
603      * @throws IOException if source or destination is invalid
604      * @throws IOException if an IO error occurs during copying
605      * @see #copyFileToDirectory(File, File)
606      */
607     public static void copyFile(File srcFile, File destFile) throws IOException {
608         copyFile(srcFile, destFile, true);
609     }
610 
611     /**
612      * Copies a file to a new location.
613      * <p>
614      * This method copies the contents of the specified source file
615      * to the specified destination file.
616      * The directory holding the destination file is created if it does not exist.
617      * If the destination file exists, then this method will overwrite it.
618      *
619      * @param srcFile  an existing file to copy, must not be <code>null</code>
620      * @param destFile  the new file, must not be <code>null</code>
621      * @param preserveFileDate  true if the file date of the copy
622      *  should be the same as the original
623      *
624      * @throws NullPointerException if source or destination is <code>null</code>
625      * @throws IOException if source or destination is invalid
626      * @throws IOException if an IO error occurs during copying
627      * @see #copyFileToDirectory(File, File, boolean)
628      */
629     public static void copyFile(File srcFile, File destFile,
630             boolean preserveFileDate) throws IOException {
631         if (srcFile == null) {
632             throw new NullPointerException("Source must not be null");
633         }
634         if (destFile == null) {
635             throw new NullPointerException("Destination must not be null");
636         }
637         if (srcFile.exists() == false) {
638             throw new FileNotFoundException("Source '" + srcFile + "' does not exist");
639         }
640         if (srcFile.isDirectory()) {
641             throw new IOException("Source '" + srcFile + "' exists but is a directory");
642         }
643         if (srcFile.getCanonicalPath().equals(destFile.getCanonicalPath())) {
644             throw new IOException("Source '" + srcFile + "' and destination '" + destFile + "' are the same");
645         }
646         if (destFile.getParentFile() != null && destFile.getParentFile().exists() == false) {
647             if (destFile.getParentFile().mkdirs() == false) {
648                 throw new IOException("Destination '" + destFile + "' directory cannot be created");
649             }
650         }
651         if (destFile.exists() && destFile.canWrite() == false) {
652             throw new IOException("Destination '" + destFile + "' exists but is read-only");
653         }
654         doCopyFile(srcFile, destFile, preserveFileDate);
655     }
656 
657     /**
658      * Internal copy file method.
659      * 
660      * @param srcFile  the validated source file, must not be <code>null</code>
661      * @param destFile  the validated destination file, must not be <code>null</code>
662      * @param preserveFileDate  whether to preserve the file date
663      * @throws IOException if an error occurs
664      */
665     private static void doCopyFile(File srcFile, File destFile, boolean preserveFileDate) throws IOException {
666         if (destFile.exists() && destFile.isDirectory()) {
667             throw new IOException("Destination '" + destFile + "' exists but is a directory");
668         }
669 
670         FileChannel input = new FileInputStream(srcFile).getChannel();
671         try {
672             FileChannel output = new FileOutputStream(destFile).getChannel();
673             try {
674                 output.transferFrom(input, 0, input.size());
675             } finally {
676                 IOUtils.closeQuietly(output);
677             }
678         } finally {
679             IOUtils.closeQuietly(input);
680         }
681 
682         if (srcFile.length() != destFile.length()) {
683             throw new IOException("Failed to copy full contents from '" +
684                     srcFile + "' to '" + destFile + "'");
685         }
686         if (preserveFileDate) {
687             destFile.setLastModified(srcFile.lastModified());
688         }
689     }
690 
691     //-----------------------------------------------------------------------
692     /**
693      * Copies a directory to within another directory preserving the file dates.
694      * <p>
695      * This method copies the source directory and all its contents to a
696      * directory of the same name in the specified destination directory.
697      * <p>
698      * The destination directory is created if it does not exist.
699      * If the destination directory did exist, then this method merges
700      * the source with the destination, with the source taking precedence.
701      *
702      * @param srcDir  an existing directory to copy, must not be <code>null</code>
703      * @param destDir  the directory to place the copy in, must not be <code>null</code>
704      *
705      * @throws NullPointerException if source or destination is <code>null</code>
706      * @throws IOException if source or destination is invalid
707      * @throws IOException if an IO error occurs during copying
708      * @since Commons IO 1.2
709      */
710     public static void copyDirectoryToDirectory(File srcDir, File destDir) throws IOException {
711         if (srcDir == null) {
712             throw new NullPointerException("Source must not be null");
713         }
714         if (srcDir.exists() && srcDir.isDirectory() == false) {
715             throw new IllegalArgumentException("Source '" + destDir + "' is not a directory");
716         }
717         if (destDir == null) {
718             throw new NullPointerException("Destination must not be null");
719         }
720         if (destDir.exists() && destDir.isDirectory() == false) {
721             throw new IllegalArgumentException("Destination '" + destDir + "' is not a directory");
722         }
723         copyDirectory(srcDir, new File(destDir, srcDir.getName()), true);
724     }
725 
726     /**
727      * Copies a whole directory to a new location preserving the file dates.
728      * <p>
729      * This method copies the specified directory and all its child
730      * directories and files to the specified destination.
731      * The destination is the new location and name of the directory.
732      * <p>
733      * The destination directory is created if it does not exist.
734      * If the destination directory did exist, then this method merges
735      * the source with the destination, with the source taking precedence.
736      *
737      * @param srcDir  an existing directory to copy, must not be <code>null</code>
738      * @param destDir  the new directory, must not be <code>null</code>
739      *
740      * @throws NullPointerException if source or destination is <code>null</code>
741      * @throws IOException if source or destination is invalid
742      * @throws IOException if an IO error occurs during copying
743      * @since Commons IO 1.1
744      */
745     public static void copyDirectory(File srcDir, File destDir) throws IOException {
746         copyDirectory(srcDir, destDir, true);
747     }
748 
749     /**
750      * Copies a whole directory to a new location.
751      * <p>
752      * This method copies the contents of the specified source directory
753      * to within the specified destination directory.
754      * <p>
755      * The destination directory is created if it does not exist.
756      * If the destination directory did exist, then this method merges
757      * the source with the destination, with the source taking precedence.
758      *
759      * @param srcDir  an existing directory to copy, must not be <code>null</code>
760      * @param destDir  the new directory, must not be <code>null</code>
761      * @param preserveFileDate  true if the file date of the copy
762      *  should be the same as the original
763      *
764      * @throws NullPointerException if source or destination is <code>null</code>
765      * @throws IOException if source or destination is invalid
766      * @throws IOException if an IO error occurs during copying
767      * @since Commons IO 1.1
768      */
769     public static void copyDirectory(File srcDir, File destDir,
770             boolean preserveFileDate) throws IOException {
771         copyDirectory(srcDir, destDir, null, preserveFileDate);
772     }
773 
774     /**
775      * Copies a filtered directory to a new location preserving the file dates.
776      * <p>
777      * This method copies the contents of the specified source directory
778      * to within the specified destination directory.
779      * <p>
780      * The destination directory is created if it does not exist.
781      * If the destination directory did exist, then this method merges
782      * the source with the destination, with the source taking precedence.
783      *
784      * <h4>Example: Copy directories only</h4> 
785      *  <pre>
786      *  // only copy the directory structure
787      *  FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY);
788      *  </pre>
789      *
790      * <h4>Example: Copy directories and txt files</h4>
791      *  <pre>
792      *  // Create a filter for ".txt" files
793      *  IOFileFilter txtSuffixFilter = FileFilterUtils.suffixFileFilter(".txt");
794      *  IOFileFilter txtFiles = FileFilterUtils.andFileFilter(FileFileFilter.FILE, txtSuffixFilter);
795      *
796      *  // Create a filter for either directories or ".txt" files
797      *  FileFilter filter = FileFilterUtils.orFileFilter(DirectoryFileFilter.DIRECTORY, txtFiles);
798      *
799      *  // Copy using the filter
800      *  FileUtils.copyDirectory(srcDir, destDir, filter);
801      *  </pre>
802      *
803      * @param srcDir  an existing directory to copy, must not be <code>null</code>
804      * @param destDir  the new directory, must not be <code>null</code>
805      * @param filter  the filter to apply, null means copy all directories and files
806      *  should be the same as the original
807      *
808      * @throws NullPointerException if source or destination is <code>null</code>
809      * @throws IOException if source or destination is invalid
810      * @throws IOException if an IO error occurs during copying
811      * @since Commons IO 1.4
812      */
813     public static void copyDirectory(File srcDir, File destDir,
814             FileFilter filter) throws IOException {
815         copyDirectory(srcDir, destDir, filter, true);
816     }
817 
818     /**
819      * Copies a filtered directory to a new location.
820      * <p>
821      * This method copies the contents of the specified source directory
822      * to within the specified destination directory.
823      * <p>
824      * The destination directory is created if it does not exist.
825      * If the destination directory did exist, then this method merges
826      * the source with the destination, with the source taking precedence.
827      *
828      * <h4>Example: Copy directories only</h4> 
829      *  <pre>
830      *  // only copy the directory structure
831      *  FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY, false);
832      *  </pre>
833      *
834      * <h4>Example: Copy directories and txt files</h4>
835      *  <pre>
836      *  // Create a filter for ".txt" files
837      *  IOFileFilter txtSuffixFilter = FileFilterUtils.suffixFileFilter(".txt");
838      *  IOFileFilter txtFiles = FileFilterUtils.andFileFilter(FileFileFilter.FILE, txtSuffixFilter);
839      *
840      *  // Create a filter for either directories or ".txt" files
841      *  FileFilter filter = FileFilterUtils.orFileFilter(DirectoryFileFilter.DIRECTORY, txtFiles);
842      *
843      *  // Copy using the filter
844      *  FileUtils.copyDirectory(srcDir, destDir, filter, false);
845      *  </pre>
846      * 
847      * @param srcDir  an existing directory to copy, must not be <code>null</code>
848      * @param destDir  the new directory, must not be <code>null</code>
849      * @param filter  the filter to apply, null means copy all directories and files
850      * @param preserveFileDate  true if the file date of the copy
851      *  should be the same as the original
852      *
853      * @throws NullPointerException if source or destination is <code>null</code>
854      * @throws IOException if source or destination is invalid
855      * @throws IOException if an IO error occurs during copying
856      * @since Commons IO 1.4
857      */
858     public static void copyDirectory(File srcDir, File destDir,
859             FileFilter filter, boolean preserveFileDate) throws IOException {
860         if (srcDir == null) {
861             throw new NullPointerException("Source must not be null");
862         }
863         if (destDir == null) {
864             throw new NullPointerException("Destination must not be null");
865         }
866         if (srcDir.exists() == false) {
867             throw new FileNotFoundException("Source '" + srcDir + "' does not exist");
868         }
869         if (srcDir.isDirectory() == false) {
870             throw new IOException("Source '" + srcDir + "' exists but is not a directory");
871         }
872         if (srcDir.getCanonicalPath().equals(destDir.getCanonicalPath())) {
873             throw new IOException("Source '" + srcDir + "' and destination '" + destDir + "' are the same");
874         }
875 
876         // Cater for destination being directory within the source directory (see IO-141)
877         List<String> exclusionList = null;
878         if (destDir.getCanonicalPath().startsWith(srcDir.getCanonicalPath())) {
879             File[] srcFiles = filter == null ? srcDir.listFiles() : srcDir.listFiles(filter);
880             if (srcFiles != null && srcFiles.length > 0) {
881                 exclusionList = new ArrayList<String>(srcFiles.length);
882                 for (int i = 0; i < srcFiles.length; i++) {
883                     File copiedFile = new File(destDir, srcFiles[i].getName());
884                     exclusionList.add(copiedFile.getCanonicalPath());
885                 }
886             }
887         }
888         doCopyDirectory(srcDir, destDir, filter, preserveFileDate, exclusionList);
889     }
890 
891     /**
892      * Internal copy directory method.
893      * 
894      * @param srcDir  the validated source directory, must not be <code>null</code>
895      * @param destDir  the validated destination directory, must not be <code>null</code>
896      * @param filter  the filter to apply, null means copy all directories and files
897      * @param preserveFileDate  whether to preserve the file date
898      * @param exclusionList  List of files and directories to exclude from the copy, may be null
899      * @throws IOException if an error occurs
900      * @since Commons IO 1.1
901      */
902     private static void doCopyDirectory(File srcDir, File destDir, FileFilter filter,
903             boolean preserveFileDate, List<String> exclusionList) throws IOException {
904         if (destDir.exists()) {
905             if (destDir.isDirectory() == false) {
906                 throw new IOException("Destination '" + destDir + "' exists but is not a directory");
907             }
908         } else {
909             if (destDir.mkdirs() == false) {
910                 throw new IOException("Destination '" + destDir + "' directory cannot be created");
911             }
912             if (preserveFileDate) {
913                 destDir.setLastModified(srcDir.lastModified());
914             }
915         }
916         if (destDir.canWrite() == false) {
917             throw new IOException("Destination '" + destDir + "' cannot be written to");
918         }
919         // recurse
920         File[] files = filter == null ? srcDir.listFiles() : srcDir.listFiles(filter);
921         if (files == null) {  // null if security restricted
922             throw new IOException("Failed to list contents of " + srcDir);
923         }
924         for (int i = 0; i < files.length; i++) {
925             File copiedFile = new File(destDir, files[i].getName());
926             if (exclusionList == null || !exclusionList.contains(files[i].getCanonicalPath())) {
927                 if (files[i].isDirectory()) {
928                     doCopyDirectory(files[i], copiedFile, filter, preserveFileDate, exclusionList);
929                 } else {
930                     doCopyFile(files[i], copiedFile, preserveFileDate);
931                 }
932             }
933         }
934     }
935 
936     //-----------------------------------------------------------------------
937     /**
938      * Copies bytes from the URL <code>source</code> to a file
939      * <code>destination</code>. The directories up to <code>destination</code>
940      * will be created if they don't already exist. <code>destination</code>
941      * will be overwritten if it already exists.
942      *
943      * @param source  the <code>URL</code> to copy bytes from, must not be <code>null</code>
944      * @param destination  the non-directory <code>File</code> to write bytes to
945      *  (possibly overwriting), must not be <code>null</code>
946      * @throws IOException if <code>source</code> URL cannot be opened
947      * @throws IOException if <code>destination</code> is a directory
948      * @throws IOException if <code>destination</code> cannot be written
949      * @throws IOException if <code>destination</code> needs creating but can't be
950      * @throws IOException if an IO error occurs during copying
951      */
952     public static void copyURLToFile(URL source, File destination) throws IOException {
953         InputStream input = source.openStream();
954         try {
955             FileOutputStream output = openOutputStream(destination);
956             try {
957                 IOUtils.copy(input, output);
958             } finally {
959                 IOUtils.closeQuietly(output);
960             }
961         } finally {
962             IOUtils.closeQuietly(input);
963         }
964     }
965 
966     //-----------------------------------------------------------------------
967     /**
968      * Deletes a directory recursively. 
969      *
970      * @param directory  directory to delete
971      * @throws IOException in case deletion is unsuccessful
972      */
973     public static void deleteDirectory(File directory) throws IOException {
974         if (!directory.exists()) {
975             return;
976         }
977 
978         cleanDirectory(directory);
979         if (!directory.delete()) {
980             String message =
981                 "Unable to delete directory " + directory + ".";
982             throw new IOException(message);
983         }
984     }
985 
986     /**
987      * Deletes a file, never throwing an exception. If file is a directory, delete it and all sub-directories.
988      * <p>
989      * The difference between File.delete() and this method are:
990      * <ul>
991      * <li>A directory to be deleted does not have to be empty.</li>
992      * <li>No exceptions are thrown when a file or directory cannot be deleted.</li>
993      * </ul>
994      *
995      * @param file  file or directory to delete, can be <code>null</code>
996      * @return <code>true</code> if the file or directory was deleted, otherwise
997      * <code>false</code>
998      *
999      * @since Commons IO 1.4
1000      */
1001     public static boolean deleteQuietly(File file) {
1002         if (file == null) {
1003             return false;
1004         }
1005         try {
1006             if (file.isDirectory()) {
1007                 cleanDirectory(file);
1008             }
1009         } catch (Exception e) {
1010         }
1011 
1012         try {
1013             return file.delete();
1014         } catch (Exception e) {
1015             return false;
1016         }
1017     }
1018 
1019     /**
1020      * Cleans a directory without deleting it.
1021      *
1022      * @param directory directory to clean
1023      * @throws IOException in case cleaning is unsuccessful
1024      */
1025     public static void cleanDirectory(File directory) throws IOException {
1026         if (!directory.exists()) {
1027             String message = directory + " does not exist";
1028             throw new IllegalArgumentException(message);
1029         }
1030 
1031         if (!directory.isDirectory()) {
1032             String message = directory + " is not a directory";
1033             throw new IllegalArgumentException(message);
1034         }
1035 
1036         File[] files = directory.listFiles();
1037         if (files == null) {  // null if security restricted
1038             throw new IOException("Failed to list contents of " + directory);
1039         }
1040 
1041         IOException exception = null;
1042         for (int i = 0; i < files.length; i++) {
1043             File file = files[i];
1044             try {
1045                 forceDelete(file);
1046             } catch (IOException ioe) {
1047                 exception = ioe;
1048             }
1049         }
1050 
1051         if (null != exception) {
1052             throw exception;
1053         }
1054     }
1055 
1056     //-----------------------------------------------------------------------
1057     /**
1058      * Waits for NFS to propagate a file creation, imposing a timeout.
1059      * <p>
1060      * This method repeatedly tests {@link File#exists()} until it returns
1061      * true up to the maximum time specified in seconds.
1062      *
1063      * @param file  the file to check, must not be <code>null</code>
1064      * @param seconds  the maximum time in seconds to wait
1065      * @return true if file exists
1066      * @throws NullPointerException if the file is <code>null</code>
1067      */
1068     public static boolean waitFor(File file, int seconds) {
1069         int timeout = 0;
1070         int tick = 0;
1071         while (!file.exists()) {
1072             if (tick++ >= 10) {
1073                 tick = 0;
1074                 if (timeout++ > seconds) {
1075                     return false;
1076                 }
1077             }
1078             try {
1079                 Thread.sleep(100);
1080             } catch (InterruptedException ignore) {
1081                 // ignore exception
1082             } catch (Exception ex) {
1083                 break;
1084             }
1085         }
1086         return true;
1087     }
1088 
1089     //-----------------------------------------------------------------------
1090     /**
1091      * Reads the contents of a file into a String.
1092      * The file is always closed.
1093      *
1094      * @param file  the file to read, must not be <code>null</code>
1095      * @param encoding  the encoding to use, <code>null</code> means platform default
1096      * @return the file contents, never <code>null</code>
1097      * @throws IOException in case of an I/O error
1098      * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
1099      */
1100     public static String readFileToString(File file, String encoding) throws IOException {
1101         InputStream in = null;
1102         try {
1103             in = openInputStream(file);
1104             return IOUtils.toString(in, encoding);
1105         } finally {
1106             IOUtils.closeQuietly(in);
1107         }
1108     }
1109 
1110 
1111     /**
1112      * Reads the contents of a file into a String using the default encoding for the VM. 
1113      * The file is always closed.
1114      *
1115      * @param file  the file to read, must not be <code>null</code>
1116      * @return the file contents, never <code>null</code>
1117      * @throws IOException in case of an I/O error
1118      * @since Commons IO 1.3.1
1119      */
1120     public static String readFileToString(File file) throws IOException {
1121         return readFileToString(file, null);
1122     }
1123 
1124     /**
1125      * Reads the contents of a file into a byte array.
1126      * The file is always closed.
1127      *
1128      * @param file  the file to read, must not be <code>null</code>
1129      * @return the file contents, never <code>null</code>
1130      * @throws IOException in case of an I/O error
1131      * @since Commons IO 1.1
1132      */
1133     public static byte[] readFileToByteArray(File file) throws IOException {
1134         InputStream in = null;
1135         try {
1136             in = openInputStream(file);
1137             return IOUtils.toByteArray(in);
1138