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