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