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.File;
020    import java.io.FileFilter;
021    import java.io.FileInputStream;
022    import java.io.FileNotFoundException;
023    import java.io.FileOutputStream;
024    import java.io.IOException;
025    import java.io.InputStream;
026    import java.io.OutputStream;
027    import java.math.BigInteger;
028    import java.net.URL;
029    import java.net.URLConnection;
030    import java.nio.ByteBuffer;
031    import java.nio.channels.FileChannel;
032    import java.nio.charset.Charset;
033    import java.util.ArrayList;
034    import java.util.Collection;
035    import java.util.Date;
036    import java.util.Iterator;
037    import java.util.List;
038    import java.util.zip.CRC32;
039    import java.util.zip.CheckedInputStream;
040    import java.util.zip.Checksum;
041    
042    import org.apache.commons.io.filefilter.DirectoryFileFilter;
043    import org.apache.commons.io.filefilter.FalseFileFilter;
044    import org.apache.commons.io.filefilter.FileFilterUtils;
045    import org.apache.commons.io.filefilter.IOFileFilter;
046    import org.apache.commons.io.filefilter.SuffixFileFilter;
047    import org.apache.commons.io.filefilter.TrueFileFilter;
048    import org.apache.commons.io.output.NullOutputStream;
049    
050    /**
051     * General file manipulation utilities.
052     * <p>
053     * Facilities are provided in the following areas:
054     * <ul>
055     * <li>writing to a file
056     * <li>reading from a file
057     * <li>make a directory including parent directories
058     * <li>copying files and directories
059     * <li>deleting files and directories
060     * <li>converting to and from a URL
061     * <li>listing files and directories by filter and extension
062     * <li>comparing file content
063     * <li>file last changed date
064     * <li>calculating a checksum
065     * </ul>
066     * <p>
067     * Origin of code: Excalibur, Alexandria, Commons-Utils
068     *
069     * @author <a href="mailto:burton@relativity.yi.org">Kevin A. Burton</A>
070     * @author <a href="mailto:sanders@apache.org">Scott Sanders</a>
071     * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
072     * @author <a href="mailto:Christoph.Reck@dlr.de">Christoph.Reck</a>
073     * @author <a href="mailto:peter@apache.org">Peter Donald</a>
074     * @author <a href="mailto:jefft@apache.org">Jeff Turner</a>
075     * @author Matthew Hawthorne
076     * @author <a href="mailto:jeremias@apache.org">Jeremias Maerki</a>
077     * @author Stephen Colebourne
078     * @author Ian Springer
079     * @author Chris Eldredge
080     * @author Jim Harrington
081     * @author Sandy McArthur
082     * @version $Id: FileUtils.java 1163295 2011-08-30 17:22:38Z ggregory $
083     */
084    public class FileUtils {
085    
086        /**
087         * Instances should NOT be constructed in standard programming.
088         */
089        public FileUtils() {
090            super();
091        }
092    
093        /**
094         * The number of bytes in a kilobyte.
095         */
096        public static final long ONE_KB = 1024;
097    
098        /**
099         * The number of bytes in a megabyte.
100         */
101        public static final long ONE_MB = ONE_KB * ONE_KB;
102    
103        /**
104         * The file copy buffer size (30 MB)
105         */
106        private static final long FILE_COPY_BUFFER_SIZE = ONE_MB * 30;
107    
108        /**
109         * The number of bytes in a gigabyte.
110         */
111        public static final long ONE_GB = ONE_KB * ONE_MB;
112    
113        /**
114         * The number of bytes in a terabyte.
115         */
116        public static final long ONE_TB = ONE_KB * ONE_GB;
117    
118        /**
119         * The number of bytes in a petabyte.
120         */
121        public static final long ONE_PB = ONE_KB * ONE_TB;
122    
123        /**
124         * The number of bytes in an exabyte.
125         */
126        public static final long ONE_EB = ONE_KB * ONE_PB;
127    
128        /**
129         * The number of bytes in a zettabyte.
130         */
131        public static final BigInteger ONE_ZB = BigInteger.valueOf(ONE_KB).multiply(BigInteger.valueOf(ONE_EB));
132    
133        /**
134         * The number of bytes in a yottabyte.
135         */
136        public static final BigInteger ONE_YB = ONE_ZB.multiply(BigInteger.valueOf(ONE_EB));
137    
138        /**
139         * An empty array of type <code>File</code>.
140         */
141        public static final File[] EMPTY_FILE_ARRAY = new File[0];
142    
143        /**
144         * The UTF-8 character set, used to decode octets in URLs.
145         */
146        private static final Charset UTF8 = Charset.forName("UTF-8");
147    
148        //-----------------------------------------------------------------------
149        /**
150         * Construct a file from the set of name elements.
151         * 
152         * @param directory the parent directory
153         * @param names the name elements
154         * @return the file
155         * @since Commons IO 2.1
156         */
157        public static File getFile(File directory, String... names) {
158            if (directory == null) {
159                throw new NullPointerException("directorydirectory must not be null");
160            }
161            if (names == null) {
162                throw new NullPointerException("names must not be null");
163            }
164            File file = directory;
165            for (String name : names) {
166                file = new File(file, name);
167            }
168            return file;
169        }
170    
171        /**
172         * Construct a file from the set of name elements.
173         * 
174         * @param names the name elements
175         * @return the file
176         * @since Commons IO 2.1
177         */
178        public static File getFile(String... names) {
179            if (names == null) {
180                throw new NullPointerException("names must not be null");
181            }
182            File file = null;
183            for (String name : names) {
184                if (file == null) {
185                    file = new File(name);
186                } else {
187                    file = new File(file, name);
188                }
189            }
190            return file;
191        }
192    
193        /**
194         * Returns the path to the system temporary directory.
195         * 
196         * @return the path to the system temporary directory.
197         * 
198         * @since Commons IO 2.0
199         */
200        public static String getTempDirectoryPath() {
201            return System.getProperty("java.io.tmpdir");
202        }
203        
204        /**
205         * Returns a {@link File} representing the system temporary directory.
206         * 
207         * @return the system temporary directory. 
208         * 
209         * @since Commons IO 2.0
210         */
211        public static File getTempDirectory() {
212            return new File(getTempDirectoryPath());
213        }
214        
215        /**
216         * Returns the path to the user's home directory.
217         * 
218         * @return the path to the user's home directory.
219         * 
220         * @since Commons IO 2.0
221         */
222        public static String getUserDirectoryPath() {
223            return System.getProperty("user.home");
224        }
225        
226        /**
227         * Returns a {@link File} representing the user's home directory.
228         * 
229         * @return the user's home directory.
230         * 
231         * @since Commons IO 2.0
232         */
233        public static File getUserDirectory() {
234            return new File(getUserDirectoryPath());
235        }
236        
237        //-----------------------------------------------------------------------
238        /**
239         * Opens a {@link FileInputStream} for the specified file, providing better
240         * error messages than simply calling <code>new FileInputStream(file)</code>.
241         * <p>
242         * At the end of the method either the stream will be successfully opened,
243         * or an exception will have been thrown.
244         * <p>
245         * An exception is thrown if the file does not exist.
246         * An exception is thrown if the file object exists but is a directory.
247         * An exception is thrown if the file exists but cannot be read.
248         * 
249         * @param file  the file to open for input, must not be <code>null</code>
250         * @return a new {@link FileInputStream} for the specified file
251         * @throws FileNotFoundException if the file does not exist
252         * @throws IOException if the file object is a directory
253         * @throws IOException if the file cannot be read
254         * @since Commons IO 1.3
255         */
256        public static FileInputStream openInputStream(File file) throws IOException {
257            if (file.exists()) {
258                if (file.isDirectory()) {
259                    throw new IOException("File '" + file + "' exists but is a directory");
260                }
261                if (file.canRead() == false) {
262                    throw new IOException("File '" + file + "' cannot be read");
263                }
264            } else {
265                throw new FileNotFoundException("File '" + file + "' does not exist");
266            }
267            return new FileInputStream(file);
268        }
269    
270        //-----------------------------------------------------------------------
271        /**
272         * Opens a {@link FileOutputStream} for the specified file, checking and
273         * creating the parent directory if it does not exist.
274         * <p>
275         * At the end of the method either the stream will be successfully opened,
276         * or an exception will have been thrown.
277         * <p>
278         * The parent directory will be created if it does not exist.
279         * The file will be created if it 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 written to.
282         * An exception is thrown if the parent directory cannot be created.
283         * 
284         * @param file  the file to open for output, must not be <code>null</code>
285         * @return a new {@link FileOutputStream} for the specified file
286         * @throws IOException if the file object is a directory
287         * @throws IOException if the file cannot be written to
288         * @throws IOException if a parent directory needs creating but that fails
289         * @since Commons IO 1.3
290         */
291        public static FileOutputStream openOutputStream(File file) throws IOException {
292            return openOutputStream(file, false);
293        }
294    
295        /**
296         * Opens a {@link FileOutputStream} for the specified file, checking and
297         * creating the parent directory if it does not exist.
298         * <p>
299         * At the end of the method either the stream will be successfully opened,
300         * or an exception will have been thrown.
301         * <p>
302         * The parent directory will be created if it does not exist.
303         * The file will be created if it does not exist.
304         * An exception is thrown if the file object exists but is a directory.
305         * An exception is thrown if the file exists but cannot be written to.
306         * An exception is thrown if the parent directory cannot be created.
307         * 
308         * @param file  the file to open for output, must not be <code>null</code>
309         * @param append if <code>true</code>, then bytes will be added to the
310         * end of the file rather than overwriting
311         * @return a new {@link FileOutputStream} for the specified file
312         * @throws IOException if the file object is a directory
313         * @throws IOException if the file cannot be written to
314         * @throws IOException if a parent directory needs creating but that fails
315         * @since Commons IO 2.1
316         */
317        public static FileOutputStream openOutputStream(File file, boolean append) throws IOException {
318            if (file.exists()) {
319                if (file.isDirectory()) {
320                    throw new IOException("File '" + file + "' exists but is a directory");
321                }
322                if (file.canWrite() == false) {
323                    throw new IOException("File '" + file + "' cannot be written to");
324                }
325            } else {
326                File parent = file.getParentFile();
327                if (parent != null) {
328                    if (!parent.mkdirs() && !parent.isDirectory()) {
329                        throw new IOException("Directory '" + parent + "' could not be created");
330                    }
331                }
332            }
333            return new FileOutputStream(file, append);
334        }
335    
336        //-----------------------------------------------------------------------
337        /**
338         * Returns a human-readable version of the file size, where the input
339         * represents a specific number of bytes.
340         * 
341         * If the size is over 1GB, the size is returned as the number of whole GB,
342         * i.e. the size is rounded down to the nearest GB boundary.
343         * 
344         * Similarly for the 1MB and 1KB boundaries.
345         *
346         * @param size  the number of bytes
347         * @return a human-readable display value (includes units - GB, MB, KB or bytes)
348         */
349        // See https://issues.apache.org/jira/browse/IO-226 - should the rounding be changed?
350        public static String byteCountToDisplaySize(long size) {
351            String displaySize;
352    
353    //        if (size / ONE_EB > 0) {
354    //            displaySize = String.valueOf(size / ONE_EB) + " EB";
355    //        } else if (size / ONE_PB > 0) {
356    //            displaySize = String.valueOf(size / ONE_PB) + " PB";
357    //        } else if (size / ONE_TB > 0) {
358    //            displaySize = String.valueOf(size / ONE_TB) + " TB";
359    //        } else 
360            if (size / ONE_GB > 0) {
361                displaySize = String.valueOf(size / ONE_GB) + " GB";
362            } else if (size / ONE_MB > 0) {
363                displaySize = String.valueOf(size / ONE_MB) + " MB";
364            } else if (size / ONE_KB > 0) {
365                displaySize = String.valueOf(size / ONE_KB) + " KB";
366            } else {
367                displaySize = String.valueOf(size) + " bytes";
368            }
369            return displaySize;
370        }
371    
372        //-----------------------------------------------------------------------
373        /**
374         * Implements the same behaviour as the "touch" utility on Unix. It creates
375         * a new file with size 0 or, if the file exists already, it is opened and
376         * closed without modifying it, but updating the file date and time.
377         * <p>
378         * NOTE: As from v1.3, this method throws an IOException if the last
379         * modified date of the file cannot be set. Also, as from v1.3 this method
380         * creates parent directories if they do not exist.
381         *
382         * @param file  the File to touch
383         * @throws IOException If an I/O problem occurs
384         */
385        public static void touch(File file) throws IOException {
386            if (!file.exists()) {
387                OutputStream out = openOutputStream(file);
388                IOUtils.closeQuietly(out);
389            }
390            boolean success = file.setLastModified(System.currentTimeMillis());
391            if (!success) {
392                throw new IOException("Unable to set the last modification time for " + file);
393            }
394        }
395    
396        //-----------------------------------------------------------------------
397        /**
398         * Converts a Collection containing java.io.File instanced into array
399         * representation. This is to account for the difference between
400         * File.listFiles() and FileUtils.listFiles().
401         *
402         * @param files  a Collection containing java.io.File instances
403         * @return an array of java.io.File
404         */
405        public static File[] convertFileCollectionToFileArray(Collection<File> files) {
406             return files.toArray(new File[files.size()]);
407        }
408    
409        //-----------------------------------------------------------------------
410        /**
411         * Finds files within a given directory (and optionally its
412         * subdirectories). All files found are filtered by an IOFileFilter.
413         *
414         * @param files the collection of files found.
415         * @param directory the directory to search in.
416         * @param filter the filter to apply to files and directories.
417         */
418        private static void innerListFiles(Collection<File> files, File directory,
419                IOFileFilter filter) {
420            File[] found = directory.listFiles((FileFilter) filter);
421            if (found != null) {
422                for (File file : found) {
423                    if (file.isDirectory()) {
424                        innerListFiles(files, file, filter);
425                    } else {
426                        files.add(file);
427                    }
428                }
429            }
430        }
431    
432        /**
433         * Finds files within a given directory (and optionally its
434         * subdirectories). All files found are filtered by an IOFileFilter.
435         * <p>
436         * If your search should recurse into subdirectories you can pass in
437         * an IOFileFilter for directories. You don't need to bind a
438         * DirectoryFileFilter (via logical AND) to this filter. This method does
439         * that for you.
440         * <p>
441         * An example: If you want to search through all directories called
442         * "temp" you pass in <code>FileFilterUtils.NameFileFilter("temp")</code>
443         * <p>
444         * Another common usage of this method is find files in a directory
445         * tree but ignoring the directories generated CVS. You can simply pass
446         * in <code>FileFilterUtils.makeCVSAware(null)</code>.
447         *
448         * @param directory  the directory to search in
449         * @param fileFilter  filter to apply when finding files.
450         * @param dirFilter  optional filter to apply when finding subdirectories.
451         * If this parameter is <code>null</code>, subdirectories will not be included in the
452         * search. Use TrueFileFilter.INSTANCE to match all directories.
453         * @return an collection of java.io.File with the matching files
454         * @see org.apache.commons.io.filefilter.FileFilterUtils
455         * @see org.apache.commons.io.filefilter.NameFileFilter
456         */
457        public static Collection<File> listFiles(
458                File directory, IOFileFilter fileFilter, IOFileFilter dirFilter) {
459            if (!directory.isDirectory()) {
460                throw new IllegalArgumentException(
461                        "Parameter 'directory' is not a directory");
462            }
463            if (fileFilter == null) {
464                throw new NullPointerException("Parameter 'fileFilter' is null");
465            }
466    
467            //Setup effective file filter
468            IOFileFilter effFileFilter = FileFilterUtils.and(fileFilter,
469                FileFilterUtils.notFileFilter(DirectoryFileFilter.INSTANCE));
470    
471            //Setup effective directory filter
472            IOFileFilter effDirFilter;
473            if (dirFilter == null) {
474                effDirFilter = FalseFileFilter.INSTANCE;
475            } else {
476                effDirFilter = FileFilterUtils.and(dirFilter,
477                    DirectoryFileFilter.INSTANCE);
478            }
479    
480            //Find files
481            Collection<File> files = new java.util.LinkedList<File>();
482            innerListFiles(files, directory,
483                FileFilterUtils.or(effFileFilter, effDirFilter));
484            return files;
485        }
486    
487        /**
488         * Allows iteration over the files in given directory (and optionally
489         * its subdirectories).
490         * <p>
491         * All files found are filtered by an IOFileFilter. This method is
492         * based on {@link #listFiles(File, IOFileFilter, IOFileFilter)},
493         * which supports Iterable ('foreach' loop).
494         * <p>
495         * @param directory  the directory to search in
496         * @param fileFilter  filter to apply when finding files.
497         * @param dirFilter  optional filter to apply when finding subdirectories.
498         * If this parameter is <code>null</code>, subdirectories will not be included in the
499         * search. Use TrueFileFilter.INSTANCE to match all directories.
500         * @return an iterator of java.io.File for the matching files
501         * @see org.apache.commons.io.filefilter.FileFilterUtils
502         * @see org.apache.commons.io.filefilter.NameFileFilter
503         * @since Commons IO 1.2
504         */
505        public static Iterator<File> iterateFiles(
506                File directory, IOFileFilter fileFilter, IOFileFilter dirFilter) {
507            return listFiles(directory, fileFilter, dirFilter).iterator();
508        }
509    
510        //-----------------------------------------------------------------------
511        /**
512         * Converts an array of file extensions to suffixes for use
513         * with IOFileFilters.
514         *
515         * @param extensions  an array of extensions. Format: {"java", "xml"}
516         * @return an array of suffixes. Format: {".java", ".xml"}
517         */
518        private static String[] toSuffixes(String[] extensions) {
519            String[] suffixes = new String[extensions.length];
520            for (int i = 0; i < extensions.length; i++) {
521                suffixes[i] = "." + extensions[i];
522            }
523            return suffixes;
524        }
525    
526    
527        /**
528         * Finds files within a given directory (and optionally its subdirectories)
529         * which match an array of extensions.
530         *
531         * @param directory  the directory to search in
532         * @param extensions  an array of extensions, ex. {"java","xml"}. If this
533         * parameter is <code>null</code>, all files are returned.
534         * @param recursive  if true all subdirectories are searched as well
535         * @return an collection of java.io.File with the matching files
536         */
537        public static Collection<File> listFiles(
538                File directory, String[] extensions, boolean recursive) {
539            IOFileFilter filter;
540            if (extensions == null) {
541                filter = TrueFileFilter.INSTANCE;
542            } else {
543                String[] suffixes = toSuffixes(extensions);
544                filter = new SuffixFileFilter(suffixes);
545            }
546            return listFiles(directory, filter,
547                (recursive ? TrueFileFilter.INSTANCE : FalseFileFilter.INSTANCE));
548        }
549    
550        /**
551         * Allows iteration over the files in a given directory (and optionally
552         * its subdirectories) which match an array of extensions. This method
553         * is based on {@link #listFiles(File, String[], boolean)},
554         * which supports Iterable ('foreach' loop).
555         *
556         * @param directory  the directory to search in
557         * @param extensions  an array of extensions, ex. {"java","xml"}. If this
558         * parameter is <code>null</code>, all files are returned.
559         * @param recursive  if true all subdirectories are searched as well
560         * @return an iterator of java.io.File with the matching files
561         * @since Commons IO 1.2
562         */
563        public static Iterator<File> iterateFiles(
564                File directory, String[] extensions, boolean recursive) {
565            return listFiles(directory, extensions, recursive).iterator();
566        }
567    
568        //-----------------------------------------------------------------------
569        /**
570         * Compares the contents of two files to determine if they are equal or not.
571         * <p>
572         * This method checks to see if the two files are different lengths
573         * or if they point to the same file, before resorting to byte-by-byte
574         * comparison of the contents.
575         * <p>
576         * Code origin: Avalon
577         *
578         * @param file1  the first file
579         * @param file2  the second file
580         * @return true if the content of the files are equal or they both don't
581         * exist, false otherwise
582         * @throws IOException in case of an I/O error
583         */
584        public static boolean contentEquals(File file1, File file2) throws IOException {
585            boolean file1Exists = file1.exists();
586            if (file1Exists != file2.exists()) {
587                return false;
588            }
589    
590            if (!file1Exists) {
591                // two not existing files are equal
592                return true;
593            }
594    
595            if (file1.isDirectory() || file2.isDirectory()) {
596                // don't want to compare directory contents
597                throw new IOException("Can't compare directories, only files");
598            }
599    
600            if (file1.length() != file2.length()) {
601                // lengths differ, cannot be equal
602                return false;
603            }
604    
605            if (file1.getCanonicalFile().equals(file2.getCanonicalFile())) {
606                // same file
607                return true;
608            }
609    
610            InputStream input1 = null;
611            InputStream input2 = null;
612            try {
613                input1 = new FileInputStream(file1);
614                input2 = new FileInputStream(file2);
615                return IOUtils.contentEquals(input1, input2);
616    
617            } finally {
618                IOUtils.closeQuietly(input1);
619                IOUtils.closeQuietly(input2);
620            }
621        }
622    
623        //-----------------------------------------------------------------------
624        /**
625         * Convert from a <code>URL</code> to a <code>File</code>.
626         * <p>
627         * From version 1.1 this method will decode the URL.
628         * Syntax such as <code>file:///my%20docs/file.txt</code> will be
629         * correctly decoded to <code>/my docs/file.txt</code>. Starting with version
630         * 1.5, this method uses UTF-8 to decode percent-encoded octets to characters.
631         * Additionally, malformed percent-encoded octets are handled leniently by
632         * passing them through literally.
633         *
634         * @param url  the file URL to convert, <code>null</code> returns <code>null</code>
635         * @return the equivalent <code>File</code> object, or <code>null</code>
636         *  if the URL's protocol is not <code>file</code>
637         */
638        public static File toFile(URL url) {
639            if (url == null || !"file".equalsIgnoreCase(url.getProtocol())) {
640                return null;
641            } else {
642                String filename = url.getFile().replace('/', File.separatorChar);
643                filename = decodeUrl(filename);
644                return new File(filename);
645            }
646        }
647    
648        /**
649         * Decodes the specified URL as per RFC 3986, i.e. transforms
650         * percent-encoded octets to characters by decoding with the UTF-8 character
651         * set. This function is primarily intended for usage with
652         * {@link java.net.URL} which unfortunately does not enforce proper URLs. As
653         * such, this method will leniently accept invalid characters or malformed
654         * percent-encoded octets and simply pass them literally through to the
655         * result string. Except for rare edge cases, this will make unencoded URLs
656         * pass through unaltered.
657         * 
658         * @param url  The URL to decode, may be <code>null</code>.
659         * @return The decoded URL or <code>null</code> if the input was
660         *         <code>null</code>.
661         */
662        static String decodeUrl(String url) {
663            String decoded = url;
664            if (url != null && url.indexOf('%') >= 0) {
665                int n = url.length();
666                StringBuffer buffer = new StringBuffer();
667                ByteBuffer bytes = ByteBuffer.allocate(n);
668                for (int i = 0; i < n;) {
669                    if (url.charAt(i) == '%') {
670                        try {
671                            do {
672                                byte octet = (byte) Integer.parseInt(url.substring(i + 1, i + 3), 16);
673                                bytes.put(octet);
674                                i += 3;
675                            } while (i < n && url.charAt(i) == '%');
676                            continue;
677                        } catch (RuntimeException e) {
678                            // malformed percent-encoded octet, fall through and
679                            // append characters literally
680                        } finally {
681                            if (bytes.position() > 0) {
682                                bytes.flip();
683                                buffer.append(UTF8.decode(bytes).toString());
684                                bytes.clear();
685                            }
686                        }
687                    }
688                    buffer.append(url.charAt(i++));
689                }
690                decoded = buffer.toString();
691            }
692            return decoded;
693        }
694    
695        /**
696         * Converts each of an array of <code>URL</code> to a <code>File</code>.
697         * <p>
698         * Returns an array of the same size as the input.
699         * If the input is <code>null</code>, an empty array is returned.
700         * If the input contains <code>null</code>, the output array contains <code>null</code> at the same
701         * index.
702         * <p>
703         * This method will decode the URL.
704         * Syntax such as <code>file:///my%20docs/file.txt</code> will be
705         * correctly decoded to <code>/my docs/file.txt</code>.
706         *
707         * @param urls  the file URLs to convert, <code>null</code> returns empty array
708         * @return a non-<code>null</code> array of Files matching the input, with a <code>null</code> item
709         *  if there was a <code>null</code> at that index in the input array
710         * @throws IllegalArgumentException if any file is not a URL file
711         * @throws IllegalArgumentException if any file is incorrectly encoded
712         * @since Commons IO 1.1
713         */
714        public static File[] toFiles(URL[] urls) {
715            if (urls == null || urls.length == 0) {
716                return EMPTY_FILE_ARRAY;
717            }
718            File[] files = new File[urls.length];
719            for (int i = 0; i < urls.length; i++) {
720                URL url = urls[i];
721                if (url != null) {
722                    if (url.getProtocol().equals("file") == false) {
723                        throw new IllegalArgumentException(
724                                "URL could not be converted to a File: " + url);
725                    }
726                    files[i] = toFile(url);
727                }
728            }
729            return files;
730        }
731    
732        /**
733         * Converts each of an array of <code>File</code> to a <code>URL</code>.
734         * <p>
735         * Returns an array of the same size as the input.
736         *
737         * @param files  the files to convert
738         * @return an array of URLs matching the input
739         * @throws IOException if a file cannot be converted
740         */
741        public static URL[] toURLs(File[] files) throws IOException {
742            URL[] urls = new URL[files.length];
743    
744            for (int i = 0; i < urls.length; i++) {
745                urls[i] = files[i].toURI().toURL();
746            }
747    
748            return urls;
749        }
750    
751        //-----------------------------------------------------------------------
752        /**
753         * Copies a file to a directory preserving the file date.
754         * <p>
755         * This method copies the contents of the specified source file
756         * to a file of the same name in the specified destination directory.
757         * The destination directory is created if it does not exist.
758         * If the destination file exists, then this method will overwrite it.
759         * <p>
760         * <strong>Note:</strong> This method tries to preserve the file's last
761         * modified date/times using {@link File#setLastModified(long)}, however
762         * it is not guaranteed that the operation will succeed.
763         * If the modification operation fails, no indication is provided.
764         *
765         * @param srcFile  an existing file to copy, must not be <code>null</code>
766         * @param destDir  the directory to place the copy in, must not be <code>null</code>
767         *
768         * @throws NullPointerException if source or destination is null
769         * @throws IOException if source or destination is invalid
770         * @throws IOException if an IO error occurs during copying
771         * @see #copyFile(File, File, boolean)
772         */
773        public static void copyFileToDirectory(File srcFile, File destDir) throws IOException {
774            copyFileToDirectory(srcFile, destDir, true);
775        }
776    
777        /**
778         * Copies a file to a directory optionally preserving the file date.
779         * <p>
780         * This method copies the contents of the specified source file
781         * to a file of the same name in the specified destination directory.
782         * The destination directory is created if it does not exist.
783         * If the destination file exists, then this method will overwrite it.
784         * <p>
785         * <strong>Note:</strong> Setting <code>preserveFileDate</code> to
786         * <code>true</code> tries to preserve the file's last modified
787         * date/times using {@link File#setLastModified(long)}, however it is
788         * not guaranteed that the operation will succeed.
789         * If the modification operation fails, no indication is provided.
790         *
791         * @param srcFile  an existing file to copy, must not be <code>null</code>
792         * @param destDir  the directory to place the copy in, must not be <code>null</code>
793         * @param preserveFileDate  true if the file date of the copy
794         *  should be the same as the original
795         *
796         * @throws NullPointerException if source or destination is <code>null</code>
797         * @throws IOException if source or destination is invalid
798         * @throws IOException if an IO error occurs during copying
799         * @see #copyFile(File, File, boolean)
800         * @since Commons IO 1.3
801         */
802        public static void copyFileToDirectory(File srcFile, File destDir, boolean preserveFileDate) throws IOException {
803            if (destDir == null) {
804                throw new NullPointerException("Destination must not be null");
805            }
806            if (destDir.exists() && destDir.isDirectory() == false) {
807                throw new IllegalArgumentException("Destination '" + destDir + "' is not a directory");
808            }
809            File destFile = new File(destDir, srcFile.getName());
810            copyFile(srcFile, destFile, preserveFileDate);
811        }
812    
813        /**
814         * Copies a file to a new location preserving the file date.
815         * <p>
816         * This method copies the contents of the specified source file to the
817         * specified destination file. The directory holding the destination file is
818         * created if it does not exist. If the destination file exists, then this
819         * method will overwrite it.
820         * <p>
821         * <strong>Note:</strong> This method tries to preserve the file's last
822         * modified date/times using {@link File#setLastModified(long)}, however
823         * it is not guaranteed that the operation will succeed.
824         * If the modification operation fails, no indication is provided.
825         * 
826         * @param srcFile  an existing file to copy, must not be <code>null</code>
827         * @param destFile  the new file, must not be <code>null</code>
828         * 
829         * @throws NullPointerException if source or destination is <code>null</code>
830         * @throws IOException if source or destination is invalid
831         * @throws IOException if an IO error occurs during copying
832         * @see #copyFileToDirectory(File, File)
833         */
834        public static void copyFile(File srcFile, File destFile) throws IOException {
835            copyFile(srcFile, destFile, true);
836        }
837    
838        /**
839         * Copies a file to a new location.
840         * <p>
841         * This method copies the contents of the specified source file
842         * to the specified destination file.
843         * The directory holding the destination file is created if it does not exist.
844         * If the destination file exists, then this method will overwrite it.
845         * <p>
846         * <strong>Note:</strong> Setting <code>preserveFileDate</code> to
847         * <code>true</code> tries to preserve the file's last modified
848         * date/times using {@link File#setLastModified(long)}, however it is
849         * not guaranteed that the operation will succeed.
850         * If the modification operation fails, no indication is provided.
851         *
852         * @param srcFile  an existing file to copy, must not be <code>null</code>
853         * @param destFile  the new file, must not be <code>null</code>
854         * @param preserveFileDate  true if the file date of the copy
855         *  should be the same as the original
856         *
857         * @throws NullPointerException if source or destination is <code>null</code>
858         * @throws IOException if source or destination is invalid
859         * @throws IOException if an IO error occurs during copying
860         * @see #copyFileToDirectory(File, File, boolean)
861         */
862        public static void copyFile(File srcFile, File destFile,
863                boolean preserveFileDate) throws IOException {
864            if (srcFile == null) {
865                throw new NullPointerException("Source must not be null");
866            }
867            if (destFile == null) {
868                throw new NullPointerException("Destination must not be null");
869            }
870            if (srcFile.exists() == false) {
871                throw new FileNotFoundException("Source '" + srcFile + "' does not exist");
872            }
873            if (srcFile.isDirectory()) {
874                throw new IOException("Source '" + srcFile + "' exists but is a directory");
875            }
876            if (srcFile.getCanonicalPath().equals(destFile.getCanonicalPath())) {
877                throw new IOException("Source '" + srcFile + "' and destination '" + destFile + "' are the same");
878            }
879            File parentFile = destFile.getParentFile();
880            if (parentFile != null) {
881                if (!parentFile.mkdirs() && !parentFile.isDirectory()) {
882                    throw new IOException("Destination '" + parentFile + "' directory cannot be created");
883                }
884            }
885            if (destFile.exists() && destFile.canWrite() == false) {
886                throw new IOException("Destination '" + destFile + "' exists but is read-only");
887            }
888            doCopyFile(srcFile, destFile, preserveFileDate);
889        }
890    
891        /**
892         * Copy bytes from a <code>File</code> to an <code>OutputStream</code>.
893         * <p>
894         * This method buffers the input internally, so there is no need to use a <code>BufferedInputStream</code>.
895         * </p>
896         * 
897         * @param input
898         *            the <code>File</code> to read from
899         * @param output
900         *            the <code>OutputStream</code> to write to
901         * @return the number of bytes copied
902         * @throws NullPointerException
903         *             if the input or output is null
904         * @throws IOException
905         *             if an I/O error occurs
906         * @since Commons IO 2.1
907         */
908        public static long copyFile(File input, OutputStream output) throws IOException {
909            final FileInputStream fis = new FileInputStream(input);
910            try {
911                return IOUtils.copyLarge(fis, output);
912            } finally {
913                fis.close();
914            }
915        }
916        
917        /**
918         * Internal copy file method.
919         * 
920         * @param srcFile  the validated source file, must not be <code>null</code>
921         * @param destFile  the validated destination file, must not be <code>null</code>
922         * @param preserveFileDate  whether to preserve the file date
923         * @throws IOException if an error occurs
924         */
925        private static void doCopyFile(File srcFile, File destFile, boolean preserveFileDate) throws IOException {
926            if (destFile.exists() && destFile.isDirectory()) {
927                throw new IOException("Destination '" + destFile + "' exists but is a directory");
928            }
929    
930            FileInputStream fis = null;
931            FileOutputStream fos = null;
932            FileChannel input = null;
933            FileChannel output = null;
934            try {
935                fis = new FileInputStream(srcFile);
936                fos = new FileOutputStream(destFile);
937                input  = fis.getChannel();
938                output = fos.getChannel();
939                long size = input.size();
940                long pos = 0;
941                long count = 0;
942                while (pos < size) {
943                    count = (size - pos) > FILE_COPY_BUFFER_SIZE ? FILE_COPY_BUFFER_SIZE : (size - pos);
944                    pos += output.transferFrom(input, pos, count);
945                }
946            } finally {
947                IOUtils.closeQuietly(output);
948                IOUtils.closeQuietly(fos);
949                IOUtils.closeQuietly(input);
950                IOUtils.closeQuietly(fis);
951            }
952    
953            if (srcFile.length() != destFile.length()) {
954                throw new IOException("Failed to copy full contents from '" +
955                        srcFile + "' to '" + destFile + "'");
956            }
957            if (preserveFileDate) {
958                destFile.setLastModified(srcFile.lastModified());
959            }
960        }
961    
962        //-----------------------------------------------------------------------
963        /**
964         * Copies a directory to within another directory preserving the file dates.
965         * <p>
966         * This method copies the source directory and all its contents to a
967         * directory of the same name in the specified destination directory.
968         * <p>
969         * The destination directory is created if it does not exist.
970         * If the destination directory did exist, then this method merges
971         * the source with the destination, with the source taking precedence.
972         * <p>
973         * <strong>Note:</strong> This method tries to preserve the files' last
974         * modified date/times using {@link File#setLastModified(long)}, however
975         * it is not guaranteed that those operations will succeed.
976         * If the modification operation fails, no indication is provided.
977         *
978         * @param srcDir  an existing directory to copy, must not be <code>null</code>
979         * @param destDir  the directory to place the copy in, must not be <code>null</code>
980         *
981         * @throws NullPointerException if source or destination is <code>null</code>
982         * @throws IOException if source or destination is invalid
983         * @throws IOException if an IO error occurs during copying
984         * @since Commons IO 1.2
985         */
986        public static void copyDirectoryToDirectory(File srcDir, File destDir) throws IOException {
987            if (srcDir == null) {
988                throw new NullPointerException("Source must not be null");
989            }
990            if (srcDir.exists() && srcDir.isDirectory() == false) {
991                throw new IllegalArgumentException("Source '" + destDir + "' is not a directory");
992            }
993            if (destDir == null) {
994                throw new NullPointerException("Destination must not be null");
995            }
996            if (destDir.exists() && destDir.isDirectory() == false) {
997                throw new IllegalArgumentException("Destination '" + destDir + "' is not a directory");
998            }
999            copyDirectory(srcDir, new File(destDir, srcDir.getName()), true);
1000        }
1001    
1002        /**
1003         * Copies a whole directory to a new location preserving the file dates.
1004         * <p>
1005         * This method copies the specified directory and all its child
1006         * directories and files to the specified destination.
1007         * The destination is the new location and name of the directory.
1008         * <p>
1009         * The destination directory is created if it does not exist.
1010         * If the destination directory did exist, then this method merges
1011         * the source with the destination, with the source taking precedence.
1012         * <p>
1013         * <strong>Note:</strong> This method tries to preserve the files' last
1014         * modified date/times using {@link File#setLastModified(long)}, however
1015         * it is not guaranteed that those operations will succeed.
1016         * If the modification operation fails, no indication is provided.
1017         *
1018         * @param srcDir  an existing directory to copy, must not be <code>null</code>
1019         * @param destDir  the new directory, must not be <code>null</code>
1020         *
1021         * @throws NullPointerException if source or destination is <code>null</code>
1022         * @throws IOException if source or destination is invalid
1023         * @throws IOException if an IO error occurs during copying
1024         * @since Commons IO 1.1
1025         */
1026        public static void copyDirectory(File srcDir, File destDir) throws IOException {
1027            copyDirectory(srcDir, destDir, true);
1028        }
1029    
1030        /**
1031         * Copies a whole directory to a new location.
1032         * <p>
1033         * This method copies the contents of the specified source directory
1034         * to within the specified destination directory.
1035         * <p>
1036         * The destination directory is created if it does not exist.
1037         * If the destination directory did exist, then this method merges
1038         * the source with the destination, with the source taking precedence.
1039         * <p>
1040         * <strong>Note:</strong> Setting <code>preserveFileDate</code> to
1041         * <code>true</code> tries to preserve the files' last modified
1042         * date/times using {@link File#setLastModified(long)}, however it is
1043         * not guaranteed that those operations will succeed.
1044         * If the modification operation fails, no indication is provided.
1045         *
1046         * @param srcDir  an existing directory to copy, must not be <code>null</code>
1047         * @param destDir  the new directory, must not be <code>null</code>
1048         * @param preserveFileDate  true if the file date of the copy
1049         *  should be the same as the original
1050         *
1051         * @throws NullPointerException if source or destination is <code>null</code>
1052         * @throws IOException if source or destination is invalid
1053         * @throws IOException if an IO error occurs during copying
1054         * @since Commons IO 1.1
1055         */
1056        public static void copyDirectory(File srcDir, File destDir,
1057                boolean preserveFileDate) throws IOException {
1058            copyDirectory(srcDir, destDir, null, preserveFileDate);
1059        }
1060    
1061        /**
1062         * Copies a filtered directory to a new location preserving the file dates.
1063         * <p>
1064         * This method copies the contents of the specified source directory
1065         * to within the specified destination directory.
1066         * <p>
1067         * The destination directory is created if it does not exist.
1068         * If the destination directory did exist, then this method merges
1069         * the source with the destination, with the source taking precedence.
1070         * <p>
1071         * <strong>Note:</strong> This method tries to preserve the files' last
1072         * modified date/times using {@link File#setLastModified(long)}, however
1073         * it is not guaranteed that those operations will succeed.
1074         * If the modification operation fails, no indication is provided.
1075         *
1076         * <h4>Example: Copy directories only</h4> 
1077         *  <pre>
1078         *  // only copy the directory structure
1079         *  FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY);
1080         *  </pre>
1081         *
1082         * <h4>Example: Copy directories and txt files</h4>
1083         *  <pre>
1084         *  // Create a filter for ".txt" files
1085         *  IOFileFilter txtSuffixFilter = FileFilterUtils.suffixFileFilter(".txt");
1086         *  IOFileFilter txtFiles = FileFilterUtils.andFileFilter(FileFileFilter.FILE, txtSuffixFilter);
1087         *
1088         *  // Create a filter for either directories or ".txt" files
1089         *  FileFilter filter = FileFilterUtils.orFileFilter(DirectoryFileFilter.DIRECTORY, txtFiles);
1090         *
1091         *  // Copy using the filter
1092         *  FileUtils.copyDirectory(srcDir, destDir, filter);
1093         *  </pre>
1094         *
1095         * @param srcDir  an existing directory to copy, must not be <code>null</code>
1096         * @param destDir  the new directory, must not be <code>null</code>
1097         * @param filter  the filter to apply, null means copy all directories and files
1098         *  should be the same as the original
1099         *
1100         * @throws NullPointerException if source or destination is <code>null</code>
1101         * @throws IOException if source or destination is invalid
1102         * @throws IOException if an IO error occurs during copying
1103         * @since Commons IO 1.4
1104         */
1105        public static void copyDirectory(File srcDir, File destDir,
1106                FileFilter filter) throws IOException {
1107            copyDirectory(srcDir, destDir, filter, true);
1108        }
1109    
1110        /**
1111         * Copies a filtered directory to a new location.
1112         * <p>
1113         * This method copies the contents of the specified source directory
1114         * to within the specified destination directory.
1115         * <p>
1116         * The destination directory is created if it does not exist.
1117         * If the destination directory did exist, then this method merges
1118         * the source with the destination, with the source taking precedence.
1119         * <p>
1120         * <strong>Note:</strong> Setting <code>preserveFileDate</code> to
1121         * <code>true</code> tries to preserve the files' last modified
1122         * date/times using {@link File#setLastModified(long)}, however it is
1123         * not guaranteed that those operations will succeed.
1124         * If the modification operation fails, no indication is provided.
1125         *
1126         * <h4>Example: Copy directories only</h4> 
1127         *  <pre>
1128         *  // only copy the directory structure
1129         *  FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY, false);
1130         *  </pre>
1131         *
1132         * <h4>Example: Copy directories and txt files</h4>
1133         *  <pre>
1134         *  // Create a filter for ".txt" files
1135         *  IOFileFilter txtSuffixFilter = FileFilterUtils.suffixFileFilter(".txt");
1136         *  IOFileFilter txtFiles = FileFilterUtils.andFileFilter(FileFileFilter.FILE, txtSuffixFilter);
1137         *
1138         *  // Create a filter for either directories or ".txt" files
1139         *  FileFilter filter = FileFilterUtils.orFileFilter(DirectoryFileFilter.DIRECTORY, txtFiles);
1140         *
1141         *  // Copy using the filter
1142         *  FileUtils.copyDirectory(srcDir, destDir, filter, false);
1143         *  </pre>
1144         * 
1145         * @param srcDir  an existing directory to copy, must not be <code>null</code>
1146         * @param destDir  the new directory, must not be <code>null</code>
1147         * @param filter  the filter to apply, null means copy all directories and files
1148         * @param preserveFileDate  true if the file date of the copy
1149         *  should be the same as the original
1150         *
1151         * @throws NullPointerException if source or destination is <code>null</code>
1152         * @throws IOException if source or destination is invalid
1153         * @throws IOException if an IO error occurs during copying
1154         * @since Commons IO 1.4
1155         */
1156        public static void copyDirectory(File srcDir, File destDir,
1157                FileFilter filter, boolean preserveFileDate) throws IOException {
1158            if (srcDir == null) {
1159                throw new NullPointerException("Source must not be null");
1160            }
1161            if (destDir == null) {
1162                throw new NullPointerException("Destination must not be null");
1163            }
1164            if (srcDir.exists() == false) {
1165                throw new FileNotFoundException("Source '" + srcDir + "' does not exist");
1166            }
1167            if (srcDir.isDirectory() == false) {
1168                throw new IOException("Source '" + srcDir + "' exists but is not a directory");
1169            }
1170            if (srcDir.getCanonicalPath().equals(destDir.getCanonicalPath())) {
1171                throw new IOException("Source '" + srcDir + "' and destination '" + destDir + "' are the same");
1172            }
1173    
1174            // Cater for destination being directory within the source directory (see IO-141)
1175            List<String> exclusionList = null;
1176            if (destDir.getCanonicalPath().startsWith(srcDir.getCanonicalPath())) {
1177                File[] srcFiles = filter == null ? srcDir.listFiles() : srcDir.listFiles(filter);
1178                if (srcFiles != null && srcFiles.length > 0) {
1179                    exclusionList = new ArrayList<String>(srcFiles.length);
1180                    for (File srcFile : srcFiles) {
1181                        File copiedFile = new File(destDir, srcFile.getName());
1182                        exclusionList.add(copiedFile.getCanonicalPath());
1183                    }
1184                }
1185            }
1186            doCopyDirectory(srcDir, destDir, filter, preserveFileDate, exclusionList);
1187        }
1188    
1189        /**
1190         * Internal copy directory method.
1191         * 
1192         * @param srcDir  the validated source directory, must not be <code>null</code>
1193         * @param destDir  the validated destination directory, must not be <code>null</code>
1194         * @param filter  the filter to apply, null means copy all directories and files
1195         * @param preserveFileDate  whether to preserve the file date
1196         * @param exclusionList  List of files and directories to exclude from the copy, may be null
1197         * @throws IOException if an error occurs
1198         * @since Commons IO 1.1
1199         */
1200        private static void doCopyDirectory(File srcDir, File destDir, FileFilter filter,
1201                boolean preserveFileDate, List<String> exclusionList) throws IOException {
1202            // recurse
1203            File[] srcFiles = filter == null ? srcDir.listFiles() : srcDir.listFiles(filter);
1204            if (srcFiles == null) {  // null if abstract pathname does not denote a directory, or if an I/O error occurs
1205                throw new IOException("Failed to list contents of " + srcDir);
1206            }
1207            if (destDir.exists()) {
1208                if (destDir.isDirectory() == false) {
1209                    throw new IOException("Destination '" + destDir + "' exists but is not a directory");
1210                }
1211            } else {
1212                if (!destDir.mkdirs() && !destDir.isDirectory()) {
1213                    throw new IOException("Destination '" + destDir + "' directory cannot be created");
1214                }
1215            }
1216            if (destDir.canWrite() == false) {
1217                throw new IOException("Destination '" + destDir + "' cannot be written to");
1218            }
1219            for (File srcFile : srcFiles) {
1220                File dstFile = new File(destDir, srcFile.getName());
1221                if (exclusionList == null || !exclusionList.contains(srcFile.getCanonicalPath())) {
1222                    if (srcFile.isDirectory()) {
1223                        doCopyDirectory(srcFile, dstFile, filter, preserveFileDate, exclusionList);
1224                    } else {
1225                        doCopyFile(srcFile, dstFile, preserveFileDate);
1226                    }
1227                }
1228            }
1229    
1230            // Do this last, as the above has probably affected directory metadata
1231            if (preserveFileDate) {
1232                destDir.setLastModified(srcDir.lastModified());
1233            }
1234        }
1235    
1236        //-----------------------------------------------------------------------
1237        /**
1238         * Copies bytes from the URL <code>source</code> to a file
1239         * <code>destination</code>. The directories up to <code>destination</code>
1240         * will be created if they don't already exist. <code>destination</code>
1241         * will be overwritten if it already exists.
1242         * <p>
1243         * Warning: this method does not set a connection or read timeout and thus
1244         * might block forever. Use {@link #copyURLToFile(URL, File, int, int)}
1245         * with reasonable timeouts to prevent this.
1246         *
1247         * @param source  the <code>URL</code> to copy bytes from, must not be <code>null</code>
1248         * @param destination  the non-directory <code>File</code> to write bytes to
1249         *  (possibly overwriting), must not be <code>null</code>
1250         * @throws IOException if <code>source</code> URL cannot be opened
1251         * @throws IOException if <code>destination</code> is a directory
1252         * @throws IOException if <code>destination</code> cannot be written
1253         * @throws IOException if <code>destination</code> needs creating but can't be
1254         * @throws IOException if an IO error occurs during copying
1255         */
1256        public static void copyURLToFile(URL source, File destination) throws IOException {
1257            InputStream input = source.openStream();
1258            copyInputStreamToFile(input, destination);
1259        }
1260    
1261        /**
1262         * Copies bytes from the URL <code>source</code> to a file
1263         * <code>destination</code>. The directories up to <code>destination</code>
1264         * will be created if they don't already exist. <code>destination</code>
1265         * will be overwritten if it already exists.
1266         *
1267         * @param source  the <code>URL</code> to copy bytes from, must not be <code>null</code>
1268         * @param destination  the non-directory <code>File</code> to write bytes to
1269         *  (possibly overwriting), must not be <code>null</code>
1270         * @param connectionTimeout the number of milliseconds until this method
1271         *  will timeout if no connection could be established to the <code>source</code>
1272         * @param readTimeout the number of milliseconds until this method will
1273         *  timeout if no data could be read from the <code>source</code> 
1274         * @throws IOException if <code>source</code> URL cannot be opened
1275         * @throws IOException if <code>destination</code> is a directory
1276         * @throws IOException if <code>destination</code> cannot be written
1277         * @throws IOException if <code>destination</code> needs creating but can't be
1278         * @throws IOException if an IO error occurs during copying
1279         * @since Commons IO 2.0
1280         */
1281        public static void copyURLToFile(URL source, File destination,
1282                int connectionTimeout, int readTimeout) throws IOException {
1283            URLConnection connection = source.openConnection();
1284            connection.setConnectTimeout(connectionTimeout);
1285            connection.setReadTimeout(readTimeout);
1286            InputStream input = connection.getInputStream();
1287            copyInputStreamToFile(input, destination);
1288        }
1289    
1290        /**
1291         * Copies bytes from an {@link InputStream} <code>source</code> to a file
1292         * <code>destination</code>. The directories up to <code>destination</code>
1293         * will be created if they don't already exist. <code>destination</code>
1294         * will be overwritten if it already exists.
1295         *
1296         * @param source  the <code>InputStream</code> to copy bytes from, must not be <code>null</code>
1297         * @param destination  the non-directory <code>File</code> to write bytes to
1298         *  (possibly overwriting), must not be <code>null</code>
1299         * @throws IOException if <code>destination</code> is a directory
1300         * @throws IOException if <code>destination</code> cannot be written
1301         * @throws IOException if <code>destination</code> needs creating but can't be
1302         * @throws IOException if an IO error occurs during copying
1303         * @since Commons IO 2.0
1304         */
1305        public static void copyInputStreamToFile(InputStream source, File destination) throws IOException {
1306            try {
1307                FileOutputStream output = openOutputStream(destination);
1308                try {
1309                    IOUtils.copy(source, output);
1310                } finally {
1311                    IOUtils.closeQuietly(output);
1312                }
1313            } finally {
1314                IOUtils.closeQuietly(source);
1315            }
1316        }
1317    
1318        //-----------------------------------------------------------------------
1319        /**
1320         * Deletes a directory recursively. 
1321         *
1322         * @param directory  directory to delete
1323         * @throws IOException in case deletion is unsuccessful
1324         */
1325        public static void deleteDirectory(File directory) throws IOException {
1326            if (!directory.exists()) {
1327                return;
1328            }
1329    
1330            if (!isSymlink(directory)) {
1331                cleanDirectory(directory);
1332            }
1333    
1334            if (!directory.delete()) {
1335                String message =
1336                    "Unable to delete directory " + directory + ".";
1337                throw new IOException(message);
1338            }
1339        }
1340    
1341        /**
1342         * Deletes a file, never throwing an exception. If file is a directory, delete it and all sub-directories.
1343         * <p>
1344         * The difference between File.delete() and this method are:
1345         * <ul>
1346         * <li>A directory to be deleted does not have to be empty.</li>
1347         * <li>No exceptions are thrown when a file or directory cannot be deleted.</li>
1348         * </ul>
1349         *
1350         * @param file  file or directory to delete, can be <code>null</code>
1351         * @return <code>true</code> if the file or directory was deleted, otherwise
1352         * <code>false</code>
1353         *
1354         * @since Commons IO 1.4
1355         */
1356        public static boolean deleteQuietly(File file) {
1357            if (file == null) {
1358                return false;
1359            }
1360            try {
1361                if (file.isDirectory()) {
1362                    cleanDirectory(file);
1363                }
1364            } catch (Exception ignored) {
1365            }
1366    
1367            try {
1368                return file.delete();
1369            } catch (Exception ignored) {
1370                return false;
1371            }
1372        }
1373    
1374        /**
1375         * Cleans a directory without deleting it.
1376         *
1377         * @param directory directory to clean
1378         * @throws IOException in case cleaning is unsuccessful
1379         */
1380        public static void cleanDirectory(File directory) throws IOException {
1381            if (!directory.exists()) {
1382                String message = directory + " does not exist";
1383                throw new IllegalArgumentException(message);
1384            }
1385    
1386            if (!directory.isDirectory()) {
1387                String message = directory + " is not a directory";
1388                throw new IllegalArgumentException(message);
1389            }
1390    
1391            File[] files = directory.listFiles();
1392            if (files == null) {  // null if security restricted
1393                throw new IOException("Failed to list contents of " + directory);
1394            }
1395    
1396            IOException exception = null;
1397            for (File file : files) {
1398                try {
1399                    forceDelete(file);
1400                } catch (IOException ioe) {
1401                    exception = ioe;
1402                }
1403            }
1404    
1405            if (null != exception) {
1406                throw exception;
1407            }
1408        }
1409    
1410        //-----------------------------------------------------------------------
1411        /**
1412         * Waits for NFS to propagate a file creation, imposing a timeout.
1413         * <p>
1414         * This method repeatedly tests {@link File#exists()} until it returns
1415         * true up to the maximum time specified in seconds.
1416         *
1417         * @param file  the file to check, must not be <code>null</code>
1418         * @param seconds  the maximum time in seconds to wait
1419         * @return true if file exists
1420         * @throws NullPointerException if the file is <code>null</code>
1421         */
1422        public static boolean waitFor(File file, int seconds) {
1423            int timeout = 0;
1424            int tick = 0;
1425            while (!file.exists()) {
1426                if (tick++ >= 10) {
1427                    tick = 0;
1428                    if (timeout++ > seconds) {
1429                        return false;
1430                    }
1431                }
1432                try {
1433                    Thread.sleep(100);
1434                } catch (InterruptedException ignore) {
1435                    // ignore exception
1436                } catch (Exception ex) {
1437                    break;
1438                }
1439            }
1440            return true;
1441        }
1442    
1443        //-----------------------------------------------------------------------
1444        /**
1445         * Reads the contents of a file into a String.
1446         * The file is always closed.
1447         *
1448         * @param file  the file to read, must not be <code>null</code>
1449         * @param encoding  the encoding to use, <code>null</code> means platform default
1450         * @return the file contents, never <code>null</code>
1451         * @throws IOException in case of an I/O error
1452         * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
1453         */
1454        public static String readFileToString(File file, String encoding) throws IOException {
1455            InputStream in = null;
1456            try {
1457                in = openInputStream(file);
1458                return IOUtils.toString(in, encoding);
1459            } finally {
1460                IOUtils.closeQuietly(in);
1461            }
1462        }
1463    
1464    
1465        /**
1466         * Reads the contents of a file into a String using the default encoding for the VM. 
1467         * The file is always closed.
1468         *
1469         * @param file  the file to read, must not be <code>null</code>
1470         * @return the file contents, never <code>null</code>
1471         * @throws IOException in case of an I/O error
1472         * @since Commons IO 1.3.1
1473         */
1474        public static String readFileToString(File file) throws IOException {
1475            return readFileToString(file, null);
1476        }
1477    
1478        /**
1479         * Reads the contents of a file into a byte array.
1480         * The file is always closed.
1481         *
1482         * @param file  the file to read, must not be <code>null</code>
1483         * @return the file contents, never <code>null</code>
1484         * @throws IOException in case of an I/O error
1485         * @since Commons IO 1.1
1486         */
1487        public static byte[] readFileToByteArray(File file) throws IOException {
1488            InputStream in = null;
1489            try {
1490                in = openInputStream(file);
1491                return IOUtils.toByteArray(in, file.length());
1492            } finally {
1493                IOUtils.closeQuietly(in);
1494            }
1495        }
1496    
1497        /**
1498         * Reads the contents of a file line by line to a List of Strings.
1499         * The file is always closed.
1500         *
1501         * @param file  the file to read, must not be <code>null</code>
1502         * @param encoding  the encoding to use, <code>null</code> means platform default
1503         * @return the list of Strings representing each line in the file, never <code>null</code>
1504         * @throws IOException in case of an I/O error
1505         * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
1506         * @since Commons IO 1.1
1507         */
1508        public static List<String> readLines(File file, String encoding) throws IOException {
1509            InputStream in = null;
1510            try {
1511                in = openInputStream(file);
1512                return IOUtils.readLines(in, encoding);
1513            } finally {
1514                IOUtils.closeQuietly(in);
1515            }
1516        }
1517    
1518        /**
1519         * Reads the contents of a file line by line to a List of Strings using the default encoding for the VM.
1520         * The file is always closed.
1521         *
1522         * @param file  the file to read, must not be <code>null</code>
1523         * @return the list of Strings representing each line in the file, never <code>null</code>
1524         * @throws IOException in case of an I/O error
1525         * @since Commons IO 1.3
1526         */
1527        public static List<String> readLines(File file) throws IOException {
1528            return readLines(file, null);
1529        }
1530    
1531        /**
1532         * Returns an Iterator for the lines in a <code>File</code>.
1533         * <p>
1534         * This method opens an <code>InputStream</code> for the file.
1535         * When you have finished with the iterator you should close the stream
1536         * to free internal resources. This can be done by calling the
1537         * {@link LineIterator#close()} or
1538         * {@link LineIterator#closeQuietly(LineIterator)} method.
1539         * <p>
1540         * The recommended usage pattern is:
1541         * <pre>
1542         * LineIterator it = FileUtils.lineIterator(file, "UTF-8");
1543         * try {
1544         *   while (it.hasNext()) {
1545         *     String line = it.nextLine();
1546         *     /// do something with line
1547         *   }
1548         * } finally {
1549         *   LineIterator.closeQuietly(iterator);
1550         * }
1551         * </pre>
1552         * <p>
1553         * If an exception occurs during the creation of the iterator, the
1554         * underlying stream is closed.
1555         *
1556         * @param file  the file to open for input, must not be <code>null</code>
1557         * @param encoding  the encoding to use, <code>null</code> means platform default
1558         * @return an Iterator of the lines in the file, never <code>null</code>
1559         * @throws IOException in case of an I/O error (file closed)
1560         * @since Commons IO 1.2
1561         */
1562        public static LineIterator lineIterator(File file, String encoding) throws IOException {
1563            InputStream in = null;
1564            try {
1565                in = openInputStream(file);
1566                return IOUtils.lineIterator(in, encoding);
1567            } catch (IOException ex) {
1568                IOUtils.closeQuietly(in);
1569                throw ex;
1570            } catch (RuntimeException ex) {
1571                IOUtils.closeQuietly(in);
1572                throw ex;
1573            }
1574        }
1575    
1576        /**
1577         * Returns an Iterator for the lines in a <code>File</code> using the default encoding for the VM.
1578         *
1579         * @param file  the file to open for input, must not be <code>null</code>
1580         * @return an Iterator of the lines in the file, never <code>null</code>
1581         * @throws IOException in case of an I/O error (file closed)
1582         * @since Commons IO 1.3
1583         * @see #lineIterator(File, String)
1584         */
1585        public static LineIterator lineIterator(File file) throws IOException {
1586            return lineIterator(file, null);
1587        }
1588    
1589        //-----------------------------------------------------------------------
1590        /**
1591         * Writes a String to a file creating the file if it does not exist.
1592         *
1593         * NOTE: As from v1.3, the parent directories of the file will be created
1594         * if they do not exist.
1595         *
1596         * @param file  the file to write
1597         * @param data  the content to write to the file
1598         * @param encoding  the encoding to use, <code>null</code> means platform default
1599         * @throws IOException in case of an I/O error
1600         * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
1601         */
1602        public static void writeStringToFile(File file, String data, String encoding) throws IOException {
1603            writeStringToFile(file, data, encoding, false);
1604        }
1605    
1606        /**
1607         * Writes a String to a file creating the file if it does not exist.
1608         *
1609         * @param file  the file to write
1610         * @param data  the content to write to the file
1611         * @param encoding  the encoding to use, <code>null</code> means platform default
1612         * @param append if <code>true</code>, then the String will be added to the
1613         * end of the file rather than overwriting
1614         * @throws IOException in case of an I/O error
1615         * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
1616         * @since Commons IO 2.1
1617         */
1618        public static void writeStringToFile(File file, String data, String encoding, boolean append) throws IOException {
1619            OutputStream out = null;
1620            try {
1621                out = openOutputStream(file, append);
1622                IOUtils.write(data, out, encoding);
1623            } finally {
1624                IOUtils.closeQuietly(out);
1625            }
1626        }
1627    
1628        /**
1629         * Writes a String to a file creating the file if it does not exist using the default encoding for the VM.
1630         * 
1631         * @param file  the file to write
1632         * @param data  the content to write to the file
1633         * @throws IOException in case of an I/O error
1634         */
1635        public static void writeStringToFile(File file, String data) throws IOException {
1636            writeStringToFile(file, data, null, false);
1637        }
1638    
1639        /**
1640         * Writes a String to a file creating the file if it does not exist using the default encoding for the VM.
1641         * 
1642         * @param file  the file to write
1643         * @param data  the content to write to the file
1644         * @param append if <code>true</code>, then the String will be added to the
1645         * end of the file rather than overwriting
1646         * @throws IOException in case of an I/O error
1647         * @since Commons IO 2.1
1648         */
1649        public static void writeStringToFile(File file, String data, boolean append) throws IOException {
1650            writeStringToFile(file, data, null, append);
1651        }
1652    
1653        /**
1654         * Writes a CharSequence to a file creating the file if it does not exist using the default encoding for the VM.
1655         * 
1656         * @param file  the file to write
1657         * @param data  the content to write to the file
1658         * @throws IOException in case of an I/O error
1659         * @since Commons IO 2.0
1660         */
1661        public static void write(File file, CharSequence data) throws IOException {
1662            write(file, data, null, false);
1663        }
1664    
1665        /**
1666         * Writes a CharSequence to a file creating the file if it does not exist using the default encoding for the VM.
1667         * 
1668         * @param file  the file to write
1669         * @param data  the content to write to the file
1670         * @param append if <code>true</code>, then the data will be added to the
1671         * end of the file rather than overwriting
1672         * @throws IOException in case of an I/O error
1673         * @since Commons IO 2.1
1674         */
1675        public static void write(File file, CharSequence data, boolean append) throws IOException {
1676            write(file, data, null, append);
1677        }
1678    
1679        /**
1680         * Writes a CharSequence to a file creating the file if it does not exist.
1681         *
1682         * @param file  the file to write
1683         * @param data  the content to write to the file
1684         * @param encoding  the encoding to use, <code>null</code> means platform default
1685         * @throws IOException in case of an I/O error
1686         * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
1687         * @since Commons IO 2.0
1688         */
1689        public static void write(File file, CharSequence data, String encoding) throws IOException {
1690            write(file, data, encoding, false);
1691        }
1692    
1693        /**
1694         * Writes a CharSequence to a file creating the file if it does not exist.
1695         *
1696         * @param file  the file to write
1697         * @param data  the content to write to the file
1698         * @param encoding  the encoding to use, <code>null</code> means platform default
1699         * @param append if <code>true</code>, then the data will be added to the
1700         * end of the file rather than overwriting
1701         * @throws IOException in case of an I/O error
1702         * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
1703         * @since IO 2.1
1704         */
1705        public static void write(File file, CharSequence data, String encoding, boolean append) throws IOException {
1706            String str = data == null ? null : data.toString();
1707            writeStringToFile(file, str, encoding, append);
1708        }
1709    
1710        /**
1711         * Writes a byte array to a file creating the file if it does not exist.
1712         * <p>
1713         * NOTE: As from v1.3, the parent directories of the file will be created
1714         * if they do not exist.
1715         *
1716         * @param file  the file to write to
1717         * @param data  the content to write to the file
1718         * @throws IOException in case of an I/O error
1719         * @since Commons IO 1.1
1720         */
1721        public static void writeByteArrayToFile(File file, byte[] data) throws IOException {
1722            writeByteArrayToFile(file, data, false);
1723        }
1724    
1725        /**
1726         * Writes a byte array to a file creating the file if it does not exist.
1727         *
1728         * @param file  the file to write to
1729         * @param data  the content to write to the file
1730         * @param append if <code>true</code>, then bytes will be added to the
1731         * end of the file rather than overwriting
1732         * @throws IOException in case of an I/O error
1733         * @since IO 2.1
1734         */
1735        public static void writeByteArrayToFile(File file, byte[] data, boolean append) throws IOException {
1736            OutputStream out = null;
1737            try {
1738                out = openOutputStream(file, append);
1739                out.write(data);
1740            } finally {
1741                IOUtils.closeQuietly(out);
1742            }
1743        }
1744    
1745        /**
1746         * Writes the <code>toString()</code> value of each item in a collection to
1747         * the specified <code>File</code> line by line.
1748         * The specified character encoding and the default line ending will be used.
1749         * <p>
1750         * NOTE: As from v1.3, the parent directories of the file will be created
1751         * if they do not exist.
1752         *
1753         * @param file  the file to write to
1754         * @param encoding  the encoding to use, <code>null</code> means platform default
1755         * @param lines  the lines to write, <code>null</code> entries produce blank lines
1756         * @throws IOException in case of an I/O error
1757         * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
1758         * @since Commons IO 1.1
1759         */
1760        public static void writeLines(File file, String encoding, Collection<?> lines) throws IOException {
1761            writeLines(file, encoding, lines, null, false);
1762        }
1763    
1764        /**
1765         * Writes the <code>toString()</code> value of each item in a collection to
1766         * the specified <code>File</code> line by line, optionally appending.
1767         * The specified character encoding and the default line ending will be used.
1768         *
1769         * @param file  the file to write to
1770         * @param encoding  the encoding to use, <code>null</code> means platform default
1771         * @param lines  the lines to write, <code>null</code> entries produce blank lines
1772         * @param append if <code>true</code>, then the lines will be added to the
1773         * end of the file rather than overwriting
1774         * @throws IOException in case of an I/O error
1775         * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
1776         * @since Commons IO 2.1
1777         */
1778        public static void writeLines(File file, String encoding, Collection<?> lines, boolean append) throws IOException {
1779            writeLines(file, encoding, lines, null, append);
1780        }
1781    
1782        /**
1783         * Writes the <code>toString()</code> value of each item in a collection to
1784         * the specified <code>File</code> line by line.
1785         * The default VM encoding and the default line ending will be used.
1786         *
1787         * @param file  the file to write to
1788         * @param lines  the lines to write, <code>null</code> entries produce blank lines
1789         * @throws IOException in case of an I/O error
1790         * @since Commons IO 1.3
1791         */
1792        public static void writeLines(File file, Collection<?> lines) throws IOException {
1793            writeLines(file, null, lines, null, false);
1794        }
1795        
1796        /**
1797         * Writes the <code>toString()</code> value of each item in a collection to
1798         * the specified <code>File</code> line by line.
1799         * The default VM encoding and the default line ending will be used.
1800         *
1801         * @param file  the file to write to
1802         * @param lines  the lines to write, <code>null</code> entries produce blank lines
1803         * @param append if <code>true</code>, then the lines will be added to the
1804         * end of the file rather than overwriting
1805         * @throws IOException in case of an I/O error
1806         * @since Commons IO 2.1
1807         */
1808        public static void writeLines(File file, Collection<?> lines, boolean append) throws IOException {
1809            writeLines(file, null, lines, null, append);
1810        }
1811    
1812        /**
1813         * Writes the <code>toString()</code> value of each item in a collection to
1814         * the specified <code>File</code> line by line.
1815         * The specified character encoding and the line ending will be used.
1816         * <p>
1817         * NOTE: As from v1.3, the parent directories of the file will be created
1818         * if they do not exist.
1819         *
1820         * @param file  the file to write to
1821         * @param encoding  the encoding to use, <code>null</code> means platform default
1822         * @param lines  the lines to write, <code>null</code> entries produce blank lines
1823         * @param lineEnding  the line separator to use, <code>null</code> is system default
1824         * @throws IOException in case of an I/O error
1825         * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
1826         * @since Commons IO 1.1
1827         */
1828        public static void writeLines(File file, String encoding, Collection<?> lines, String lineEnding)
1829            throws IOException {
1830            writeLines(file, encoding, lines, lineEnding, false);
1831        }
1832    
1833        /**
1834         * Writes the <code>toString()</code> value of each item in a collection to
1835         * the specified <code>File</code> line by line.
1836         * The specified character encoding and the line ending will be used.
1837         *
1838         * @param file  the file to write to
1839         * @param encoding  the encoding to use, <code>null</code> means platform default
1840         * @param lines  the lines to write, <code>null</code> entries produce blank lines
1841         * @param lineEnding  the line separator to use, <code>null</code> is system default
1842         * @param append if <code>true</code>, then the lines will be added to the
1843         * end of the file rather than overwriting
1844         * @throws IOException in case of an I/O error
1845         * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
1846         * @since Commons IO 2.1
1847         */
1848        public static void writeLines(File file, String encoding, Collection<?> lines, String lineEnding, boolean append)
1849            throws IOException {
1850            OutputStream out = null;
1851            try {
1852                out = openOutputStream(file, append);
1853                IOUtils.writeLines(lines, lineEnding, out, encoding);
1854            } finally {
1855                IOUtils.closeQuietly(out);
1856            }
1857        }
1858    
1859        /**
1860         * Writes the <code>toString()</code> value of each item in a collection to
1861         * the specified <code>File</code> line by line.
1862         * The default VM encoding and the specified line ending will be used.
1863         *
1864         * @param file  the file to write to
1865         * @param lines  the lines to write, <code>null</code> entries produce blank lines
1866         * @param lineEnding  the line separator to use, <code>null</code> is system default
1867         * @throws IOException in case of an I/O error
1868         * @since Commons IO 1.3
1869         */
1870        public static void writeLines(File file, Collection<?> lines, String lineEnding) throws IOException {
1871            writeLines(file, null, lines, lineEnding, false);
1872        }
1873    
1874        /**
1875         * Writes the <code>toString()</code> value of each item in a collection to
1876         * the specified <code>File</code> line by line.
1877         * The default VM encoding and the specified line ending will be used.
1878         *
1879         * @param file  the file to write to
1880         * @param lines  the lines to write, <code>null</code> entries produce blank lines
1881         * @param lineEnding  the line separator to use, <code>null</code> is system default
1882         * @param append if <code>true</code>, then the lines will be added to the
1883         * end of the file rather than overwriting
1884         * @throws IOException in case of an I/O error
1885         * @since Commons IO 2.1
1886         */
1887        public static void writeLines(File file, Collection<?> lines, String lineEnding, boolean append)
1888            throws IOException {
1889            writeLines(file, null, lines, lineEnding, append);
1890        }
1891    
1892        //-----------------------------------------------------------------------
1893        /**
1894         * Deletes a file. If file is a directory, delete it and all sub-directories.
1895         * <p>
1896         * The difference between File.delete() and this method are:
1897         * <ul>
1898         * <li>A directory to be deleted does not have to be empty.</li>
1899         * <li>You get exceptions when a file or directory cannot be deleted.
1900         *      (java.io.File methods returns a boolean)</li>
1901         * </ul>
1902         *
1903         * @param file  file or directory to delete, must not be <code>null</code>
1904         * @throws NullPointerException if the directory is <code>null</code>
1905         * @throws FileNotFoundException if the file was not found
1906         * @throws IOException in case deletion is unsuccessful
1907         */
1908        public static void forceDelete(File file) throws IOException {
1909            if (file.isDirectory()) {
1910                deleteDirectory(file);
1911            } else {
1912                boolean filePresent = file.exists();
1913                if (!file.delete()) {
1914                    if (!filePresent){
1915                        throw new FileNotFoundException("File does not exist: " + file);
1916                    }
1917                    String message =
1918                        "Unable to delete file: " + file;
1919                    throw new IOException(message);
1920                }
1921            }
1922        }
1923    
1924        /**
1925         * Schedules a file to be deleted when JVM exits.
1926         * If file is directory delete it and all sub-directories.
1927         *
1928         * @param file  file or directory to delete, must not be <code>null</code>
1929         * @throws NullPointerException if the file is <code>null</code>
1930         * @throws IOException in case deletion is unsuccessful
1931         */
1932        public static void forceDeleteOnExit(File file) throws IOException {
1933            if (file.isDirectory()) {
1934                deleteDirectoryOnExit(file);
1935            } else {
1936                file.deleteOnExit();
1937            }
1938        }
1939    
1940        /**
1941         * Schedules a directory recursively for deletion on JVM exit.
1942         *
1943         * @param directory  directory to delete, must not be <code>null</code>
1944         * @throws NullPointerException if the directory is <code>null</code>
1945         * @throws IOException in case deletion is unsuccessful
1946         */
1947        private static void deleteDirectoryOnExit(File directory) throws IOException {
1948            if (!directory.exists()) {
1949                return;
1950            }
1951    
1952            if (!isSymlink(directory)) {
1953                cleanDirectoryOnExit(directory);
1954            }
1955            directory.deleteOnExit();
1956        }
1957    
1958        /**
1959         * Cleans a directory without deleting it.
1960         *
1961         * @param directory  directory to clean, must not be <code>null</code>
1962         * @throws NullPointerException if the directory is <code>null</code>
1963         * @throws IOException in case cleaning is unsuccessful
1964         */
1965        private static void cleanDirectoryOnExit(File directory) throws IOException {
1966            if (!directory.exists()) {
1967                String message = directory + " does not exist";
1968                throw new IllegalArgumentException(message);
1969            }
1970    
1971            if (!directory.isDirectory()) {
1972                String message = directory + " is not a directory";
1973                throw new IllegalArgumentException(message);
1974            }
1975    
1976            File[] files = directory.listFiles();
1977            if (files == null) {  // null if security restricted
1978                throw new IOException("Failed to list contents of " + directory);
1979            }
1980    
1981            IOException exception = null;
1982            for (File file : files) {
1983                try {
1984                    forceDeleteOnExit(file);
1985                } catch (IOException ioe) {
1986                    exception = ioe;
1987                }
1988            }
1989    
1990            if (null != exception) {
1991                throw exception;
1992            }
1993        }
1994    
1995        /**
1996         * Makes a directory, including any necessary but nonexistent parent
1997         * directories. If a file already exists with specified name but it is
1998         * not a directory then an IOException is thrown.
1999         * If the directory cannot be created (or does not already exist)
2000         * then an IOException is thrown.
2001         *
2002         * @param directory  directory to create, must not be <code>null</code>
2003         * @throws NullPointerException if the directory is <code>null</code>
2004         * @throws IOException if the directory cannot be created or the file already exists but is not a directory
2005         */
2006        public static void forceMkdir(File directory) throws IOException {
2007            if (directory.exists()) {
2008                if (!directory.isDirectory()) {
2009                    String message =
2010                        "File "
2011                            + directory
2012                            + " exists and is "
2013                            + "not a directory. Unable to create directory.";
2014                    throw new IOException(message);
2015                }
2016            } else {
2017                if (!directory.mkdirs()) {
2018                    // Double-check that some other thread or process hasn't made
2019                    // the directory in the background
2020                    if (!directory.isDirectory())
2021                    {
2022                        String message =
2023                            "Unable to create directory " + directory;
2024                        throw new IOException(message);
2025                    }
2026                }
2027            }
2028        }
2029    
2030        //-----------------------------------------------------------------------
2031        /**
2032         * Returns the size of the specified file or directory. If the provided 
2033         * {@link File} is a regular file, then the file's length is returned.
2034         * If the argument is a directory, then the size of the directory is
2035         * calculated recursively. If a directory or subdirectory is security 
2036         * restricted, its size will not be included.
2037         * 
2038         * @param file the regular file or directory to return the size 
2039         *        of (must not be <code>null</code>).
2040         * 
2041         * @return the length of the file, or recursive size of the directory, 
2042         *         provided (in bytes).
2043         * 
2044         * @throws NullPointerException if the file is <code>null</code>
2045         * @throws IllegalArgumentException if the file does not exist.
2046         *         
2047         * @since Commons IO 2.0
2048         */
2049        public static long sizeOf(File file) {
2050    
2051            if (!file.exists()) {
2052                String message = file + " does not exist";
2053                throw new IllegalArgumentException(message);
2054            }
2055    
2056            if (file.isDirectory()) {
2057                return sizeOfDirectory(file);
2058            } else {
2059                return file.length();
2060            }
2061    
2062        }
2063    
2064        /**
2065         * Counts the size of a directory recursively (sum of the length of all files).
2066         *
2067         * @param directory  directory to inspect, must not be <code>null</code>
2068         * @return size of directory in bytes, 0 if directory is security restricted
2069         * @throws NullPointerException if the directory is <code>null</code>
2070         */
2071        public static long sizeOfDirectory(File directory) {
2072            if (!directory.exists()) {
2073                String message = directory + " does not exist";
2074                throw new IllegalArgumentException(message);
2075            }
2076    
2077            if (!directory.isDirectory()) {
2078                String message = directory + " is not a directory";
2079                throw new IllegalArgumentException(message);
2080            }
2081    
2082            long size = 0;
2083    
2084            File[] files = directory.listFiles();
2085            if (files == null) {  // null if security restricted
2086                return 0L;
2087            }
2088            for (File file : files) {
2089                size += sizeOf(file);
2090            }
2091    
2092            return size;
2093        }
2094    
2095        //-----------------------------------------------------------------------
2096        /**
2097         * Tests if the specified <code>File</code> is newer than the reference
2098         * <code>File</code>.
2099         *
2100         * @param file  the <code>File</code> of which the modification date must
2101         * be compared, must not be <code>null</code>
2102         * @param reference  the <code>File</code> of which the modification date
2103         * is used, must not be <code>null</code>
2104         * @return true if the <code>File</code> exists and has been modified more
2105         * recently than the reference <code>File</code>
2106         * @throws IllegalArgumentException if the file is <code>null</code>
2107         * @throws IllegalArgumentException if the reference file is <code>null</code> or doesn't exist
2108         */
2109         public static boolean isFileNewer(File file, File reference) {
2110            if (reference == null) {
2111                throw new IllegalArgumentException("No specified reference file");
2112            }
2113            if (!reference.exists()) {
2114                throw new IllegalArgumentException("The reference file '"
2115                        + reference + "' doesn't exist");
2116            }
2117            return isFileNewer(file, reference.lastModified());
2118        }
2119    
2120        /**
2121         * Tests if the specified <code>File</code> is newer than the specified
2122         * <code>Date</code>.
2123         * 
2124         * @param file  the <code>File</code> of which the modification date
2125         * must be compared, must not be <code>null</code>
2126         * @param date  the date reference, must not be <code>null</code>
2127         * @return true if the <code>File</code> exists and has been modified
2128         * after the given <code>Date</code>.
2129         * @throws IllegalArgumentException if the file is <code>null</code>
2130         * @throws IllegalArgumentException if the date is <code>null</code>
2131         */
2132        public static boolean isFileNewer(File file, Date date) {
2133            if (date == null) {
2134                throw new IllegalArgumentException("No specified date");
2135            }
2136            return isFileNewer(file, date.getTime());
2137        }
2138    
2139        /**
2140         * Tests if the specified <code>File</code> is newer than the specified
2141         * time reference.
2142         *
2143         * @param file  the <code>File</code> of which the modification date must
2144         * be compared, must not be <code>null</code>
2145         * @param timeMillis  the time reference measured in milliseconds since the
2146         * epoch (00:00:00 GMT, January 1, 1970)
2147         * @return true if the <code>File</code> exists and has been modified after
2148         * the given time reference.
2149         * @throws IllegalArgumentException if the file is <code>null</code>
2150         */
2151         public static boolean isFileNewer(File file, long timeMillis) {
2152            if (file == null) {
2153                throw new IllegalArgumentException("No specified file");
2154            }
2155            if (!file.exists()) {
2156                return false;
2157            }
2158            return file.lastModified() > timeMillis;
2159        }
2160    
2161    
2162        //-----------------------------------------------------------------------
2163        /**
2164         * Tests if the specified <code>File</code> is older than the reference
2165         * <code>File</code>.
2166         *
2167         * @param file  the <code>File</code> of which the modification date must
2168         * be compared, must not be <code>null</code>
2169         * @param reference  the <code>File</code> of which the modification date
2170         * is used, must not be <code>null</code>
2171         * @return true if the <code>File</code> exists and has been modified before
2172         * the reference <code>File</code>
2173         * @throws IllegalArgumentException if the file is <code>null</code>
2174         * @throws IllegalArgumentException if the reference file is <code>null</code> or doesn't exist
2175         */
2176         public static boolean isFileOlder(File file, File reference) {
2177            if (reference == null) {
2178                throw new IllegalArgumentException("No specified reference file");
2179            }
2180            if (!reference.exists()) {
2181                throw new IllegalArgumentException("The reference file '"
2182                        + reference + "' doesn't exist");
2183            }
2184            return isFileOlder(file, reference.lastModified());
2185        }
2186    
2187        /**
2188         * Tests if the specified <code>File</code> is older than the specified
2189         * <code>Date</code>.
2190         * 
2191         * @param file  the <code>File</code> of which the modification date
2192         * must be compared, must not be <code>null</code>
2193         * @param date  the date reference, must not be <code>null</code>
2194         * @return true if the <code>File</code> exists and has been modified
2195         * before the given <code>Date</code>.
2196         * @throws IllegalArgumentException if the file is <code>null</code>
2197         * @throws IllegalArgumentException if the date is <code>null</code>
2198         */
2199        public static boolean isFileOlder(File file, Date date) {
2200            if (date == null) {
2201                throw new IllegalArgumentException("No specified date");
2202            }
2203            return isFileOlder(file, date.getTime());
2204        }
2205    
2206        /**
2207         * Tests if the specified <code>File</code> is older than the specified
2208         * time reference.
2209         *
2210         * @param file  the <code>File</code> of which the modification date must
2211         * be compared, must not be <code>null</code>
2212         * @param timeMillis  the time reference measured in milliseconds since the
2213         * epoch (00:00:00 GMT, January 1, 1970)
2214         * @return true if the <code>File</code> exists and has been modified before
2215         * the given time reference.
2216         * @throws IllegalArgumentException if the file is <code>null</code>
2217         */
2218         public static boolean isFileOlder(File file, long timeMillis) {
2219            if (file == null) {
2220                throw new IllegalArgumentException("No specified file");
2221            }
2222            if (!file.exists()) {
2223                return false;
2224            }
2225            return file.lastModified() < timeMillis;
2226        }
2227    
2228        //-----------------------------------------------------------------------
2229        /**
2230         * Computes the checksum of a file using the CRC32 checksum routine.
2231         * The value of the checksum is returned.
2232         *
2233         * @param file  the file to checksum, must not be <code>null</code>
2234         * @return the checksum value
2235         * @throws NullPointerException if the file or checksum is <code>null</code>
2236         * @throws IllegalArgumentException if the file is a directory
2237         * @throws IOException if an IO error occurs reading the file
2238         * @since Commons IO 1.3
2239         */
2240        public static long checksumCRC32(File file) throws IOException {
2241            CRC32 crc = new CRC32();
2242            checksum(file, crc);
2243            return crc.getValue();
2244        }
2245    
2246        /**
2247         * Computes the checksum of a file using the specified checksum object.
2248         * Multiple files may be checked using one <code>Checksum</code> instance
2249         * if desired simply by reusing the same checksum object.
2250         * For example:
2251         * <pre>
2252         *   long csum = FileUtils.checksum(file, new CRC32()).getValue();
2253         * </pre>
2254         *
2255         * @param file  the file to checksum, must not be <code>null</code>
2256         * @param checksum  the checksum object to be used, must not be <code>null</code>
2257         * @return the checksum specified, updated with the content of the file
2258         * @throws NullPointerException if the file or checksum is <code>null</code>
2259         * @throws IllegalArgumentException if the file is a directory
2260         * @throws IOException if an IO error occurs reading the file
2261         * @since Commons IO 1.3
2262         */
2263        public static Checksum checksum(File file, Checksum checksum) throws IOException {
2264            if (file.isDirectory()) {
2265                throw new IllegalArgumentException("Checksums can't be computed on directories");
2266            }
2267            InputStream in = null;
2268            try {
2269                in = new CheckedInputStream(new FileInputStream(file), checksum);
2270                IOUtils.copy(in, new NullOutputStream());
2271            } finally {
2272                IOUtils.closeQuietly(in);
2273            }
2274            return checksum;
2275        }
2276    
2277        /**
2278         * Moves a directory.
2279         * <p>
2280         * When the destination directory is on another file system, do a "copy and delete".
2281         *
2282         * @param srcDir the directory to be moved
2283         * @param destDir the destination directory
2284         * @throws NullPointerException if source or destination is <code>null</code>
2285         * @throws FileExistsException if the destination directory exists
2286         * @throws IOException if source or destination is invalid
2287         * @throws IOException if an IO error occurs moving the file
2288         * @since Commons IO 1.4
2289         */
2290        public static void moveDirectory(File srcDir, File destDir) throws IOException {
2291            if (srcDir == null) {
2292                throw new NullPointerException("Source must not be null");
2293            }
2294            if (destDir == null) {
2295                throw new NullPointerException("Destination must not be null");
2296            }
2297            if (!srcDir.exists()) {
2298                throw new FileNotFoundException("Source '" + srcDir + "' does not exist");
2299            }
2300            if (!srcDir.isDirectory()) {
2301                throw new IOException("Source '" + srcDir + "' is not a directory");
2302            }
2303            if (destDir.exists()) {
2304                throw new FileExistsException("Destination '" + destDir + "' already exists");
2305            }
2306            boolean rename = srcDir.renameTo(destDir);
2307            if (!rename) {
2308                copyDirectory( srcDir, destDir );
2309                deleteDirectory( srcDir );
2310                if (srcDir.exists()) {
2311                    throw new IOException("Failed to delete original directory '" + srcDir +
2312                            "' after copy to '" + destDir + "'");
2313                }
2314            }
2315        }
2316    
2317        /**
2318         * Moves a directory to another directory.
2319         *
2320         * @param src the file to be moved
2321         * @param destDir the destination file
2322         * @param createDestDir If <code>true</code> create the destination directory,
2323         * otherwise if <code>false</code> throw an IOException
2324         * @throws NullPointerException if source or destination is <code>null</code>
2325         * @throws FileExistsException if the directory exists in the destination directory
2326         * @throws IOException if source or destination is invalid
2327         * @throws IOException if an IO error occurs moving the file
2328         * @since Commons IO 1.4
2329         */
2330        public static void moveDirectoryToDirectory(File src, File destDir, boolean createDestDir) throws IOException {
2331            if (src == null) {
2332                throw new NullPointerException("Source must not be null");
2333            }
2334            if (destDir == null) {
2335                throw new NullPointerException("Destination directory must not be null");
2336            }
2337            if (!destDir.exists() && createDestDir) {
2338                destDir.mkdirs();
2339            }
2340            if (!destDir.exists()) {
2341                throw new FileNotFoundException("Destination directory '" + destDir +
2342                        "' does not exist [createDestDir=" + createDestDir +"]");
2343            }
2344            if (!destDir.isDirectory()) {
2345                throw new IOException("Destination '" + destDir + "' is not a directory");
2346            }
2347            moveDirectory(src, new File(destDir, src.getName()));
2348        
2349        }
2350    
2351        /**
2352         * Moves a file.
2353         * <p>
2354         * When the destination file is on another file system, do a "copy and delete".
2355         *
2356         * @param srcFile the file to be moved
2357         * @param destFile the destination file
2358         * @throws NullPointerException if source or destination is <code>null</code>
2359         * @throws FileExistsException if the destination file exists
2360         * @throws IOException if source or destination is invalid
2361         * @throws IOException if an IO error occurs moving the file
2362         * @since Commons IO 1.4
2363         */
2364        public static void moveFile(File srcFile, File destFile) throws IOException {
2365            if (srcFile == null) {
2366                throw new NullPointerException("Source must not be null");
2367            }
2368            if (destFile == null) {
2369                throw new NullPointerException("Destination must not be null");
2370            }
2371            if (!srcFile.exists()) {
2372                throw new FileNotFoundException("Source '" + srcFile + "' does not exist");
2373            }
2374            if (srcFile.isDirectory()) {
2375                throw new IOException("Source '" + srcFile + "' is a directory");
2376            }
2377            if (destFile.exists()) {
2378                throw new FileExistsException("Destination '" + destFile + "' already exists");
2379            }
2380            if (destFile.isDirectory()) {
2381                throw new IOException("Destination '" + destFile + "' is a directory");
2382            }
2383            boolean rename = srcFile.renameTo(destFile);
2384            if (!rename) {
2385                copyFile( srcFile, destFile );
2386                if (!srcFile.delete()) {
2387                    FileUtils.deleteQuietly(destFile);
2388                    throw new IOException("Failed to delete original file '" + srcFile +
2389                            "' after copy to '" + destFile + "'");
2390                }
2391            }
2392        }
2393    
2394        /**
2395         * Moves a file to a directory.
2396         *
2397         * @param srcFile the file to be moved
2398         * @param destDir the destination file
2399         * @param createDestDir If <code>true</code> create the destination directory,
2400         * otherwise if <code>false</code> throw an IOException
2401         * @throws NullPointerException if source or destination is <code>null</code>
2402         * @throws FileExistsException if the destination file exists
2403         * @throws IOException if source or destination is invalid
2404         * @throws IOException if an IO error occurs moving the file
2405         * @since Commons IO 1.4
2406         */
2407        public static void moveFileToDirectory(File srcFile, File destDir, boolean createDestDir) throws IOException {
2408            if (srcFile == null) {
2409                throw new NullPointerException("Source must not be null");
2410            }
2411            if (destDir == null) {
2412                throw new NullPointerException("Destination directory must not be null");
2413            }
2414            if (!destDir.exists() && createDestDir) {
2415                destDir.mkdirs();
2416            }
2417            if (!destDir.exists()) {
2418                throw new FileNotFoundException("Destination directory '" + destDir +
2419                        "' does not exist [createDestDir=" + createDestDir +"]");
2420            }
2421            if (!destDir.isDirectory()) {
2422                throw new IOException("Destination '" + destDir + "' is not a directory");
2423            }
2424            moveFile(srcFile, new File(destDir, srcFile.getName()));
2425        }
2426    
2427        /**
2428         * Moves a file or directory to the destination directory.
2429         * <p>
2430         * When the destination is on another file system, do a "copy and delete".
2431         *
2432         * @param src the file or directory to be moved
2433         * @param destDir the destination directory 
2434         * @param createDestDir If <code>true</code> create the destination directory,
2435         * otherwise if <code>false</code> throw an IOException
2436         * @throws NullPointerException if source or destination is <code>null</code>
2437         * @throws FileExistsException if the directory or file exists in the destination directory
2438         * @throws IOException if source or destination is invalid
2439         * @throws IOException if an IO error occurs moving the file
2440         * @since Commons IO 1.4
2441         */
2442        public static void moveToDirectory(File src, File destDir, boolean createDestDir) throws IOException {
2443            if (src == null) {
2444                throw new NullPointerException("Source must not be null");
2445            }
2446            if (destDir == null) {
2447                throw new NullPointerException("Destination must not be null");
2448            }
2449            if (!src.exists()) {
2450                throw new FileNotFoundException("Source '" + src + "' does not exist");
2451            }
2452            if (src.isDirectory()) {
2453                moveDirectoryToDirectory(src, destDir, createDestDir);
2454            } else {
2455                moveFileToDirectory(src, destDir, createDestDir);
2456            }
2457        }
2458    
2459        /**
2460         * Determines whether the specified file is a Symbolic Link rather than an actual file.
2461         * <p>
2462         * Will not return true if there is a Symbolic Link anywhere in the path,
2463         * only if the specific file is.
2464         *
2465         * @param file the file to check
2466         * @return true if the file is a Symbolic Link
2467         * @throws IOException if an IO error occurs while checking the file
2468         * @since Commons IO 2.0
2469         */
2470        public static boolean isSymlink(File file) throws IOException {
2471            if (file == null) {
2472                throw new NullPointerException("File must not be null");
2473            }
2474            if (FilenameUtils.isSystemWindows()) {
2475                return false;
2476            }
2477            File fileInCanonicalDir = null;
2478            if (file.getParent() == null) {
2479                fileInCanonicalDir = file;
2480            } else {
2481                File canonicalDir = file.getParentFile().getCanonicalFile();
2482                fileInCanonicalDir = new File(canonicalDir, file.getName());
2483            }
2484            
2485            if (fileInCanonicalDir.getCanonicalFile().equals(fileInCanonicalDir.getAbsoluteFile())) {
2486                return false;
2487            } else {
2488                return true;
2489            }
2490        }
2491    }