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 com.google.common.annotations.VisibleForTesting;
20 
21 import java.io.File;
22 import java.io.FileFilter;
23 import java.io.FileInputStream;
24 import java.io.FileNotFoundException;
25 import java.io.FileOutputStream;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.OutputStream;
29 import java.net.URL;
30 import java.util.ArrayList;
31 import java.util.Collection;
32 import java.util.Date;
33 import java.util.Iterator;
34 import java.util.List;
35 import java.util.zip.CRC32;
36 import java.util.zip.CheckedInputStream;
37 import java.util.zip.Checksum;
38 
39 import org.apache.commons.io.filefilter.DirectoryFileFilter;
40 import org.apache.commons.io.filefilter.FalseFileFilter;
41 import org.apache.commons.io.filefilter.FileFilterUtils;
42 import org.apache.commons.io.filefilter.IOFileFilter;
43 import org.apache.commons.io.filefilter.SuffixFileFilter;
44 import org.apache.commons.io.filefilter.TrueFileFilter;
45 import org.apache.commons.io.output.NullOutputStream;
46 
47 /**
48  * General file manipulation utilities.
49  * <p>
50  * Facilities are provided in the following areas:
51  * <ul>
52  * <li>writing to a file
53  * <li>reading from a file
54  * <li>make a directory including parent directories
55  * <li>copying files and directories
56  * <li>deleting files and directories
57  * <li>converting to and from a URL
58  * <li>listing files and directories by filter and extension
59  * <li>comparing file content
60  * <li>file last changed date
61  * <li>calculating a checksum
62  * </ul>
63  * <p>
64  * Origin of code: Excalibur, Alexandria, Commons-Utils
65  *
66  * @author <a href="mailto:burton@relativity.yi.org">Kevin A. Burton</A>
67  * @author <a href="mailto:sanders@apache.org">Scott Sanders</a>
68  * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
69  * @author <a href="mailto:Christoph.Reck@dlr.de">Christoph.Reck</a>
70  * @author <a href="mailto:peter@apache.org">Peter Donald</a>
71  * @author <a href="mailto:jefft@apache.org">Jeff Turner</a>
72  * @author Matthew Hawthorne
73  * @author <a href="mailto:jeremias@apache.org">Jeremias Maerki</a>
74  * @author Stephen Colebourne
75  * @author Ian Springer
76  * @author Chris Eldredge
77  * @author Jim Harrington
78  * @author Niall Pemberton
79  * @author Sandy McArthur
80  * @version $Id: FileUtils.java 610810 2008-01-10 15:04:49Z niallp $
81  */
82 @VisibleForTesting
83 public class FileUtils {
84 
85     /**
86      * Instances should NOT be constructed in standard programming.
87      */
FileUtils()88     public FileUtils() {
89         super();
90     }
91 
92     /**
93      * The number of bytes in a kilobyte.
94      */
95     public static final long ONE_KB = 1024;
96 
97     /**
98      * The number of bytes in a megabyte.
99      */
100     public static final long ONE_MB = ONE_KB * ONE_KB;
101 
102     /**
103      * The number of bytes in a gigabyte.
104      */
105     public static final long ONE_GB = ONE_KB * ONE_MB;
106 
107     /**
108      * An empty array of type <code>File</code>.
109      */
110     public static final File[] EMPTY_FILE_ARRAY = new File[0];
111 
112     //-----------------------------------------------------------------------
113     /**
114      * Opens a {@link FileInputStream} for the specified file, providing better
115      * error messages than simply calling <code>new FileInputStream(file)</code>.
116      * <p>
117      * At the end of the method either the stream will be successfully opened,
118      * or an exception will have been thrown.
119      * <p>
120      * An exception is thrown if the file does not exist.
121      * An exception is thrown if the file object exists but is a directory.
122      * An exception is thrown if the file exists but cannot be read.
123      *
124      * @param file  the file to open for input, must not be <code>null</code>
125      * @return a new {@link FileInputStream} for the specified file
126      * @throws FileNotFoundException if the file does not exist
127      * @throws IOException if the file object is a directory
128      * @throws IOException if the file cannot be read
129      * @since Commons IO 1.3
130      */
openInputStream(File file)131     public static FileInputStream openInputStream(File file) throws IOException {
132         if (file.exists()) {
133             if (file.isDirectory()) {
134                 throw new IOException("File '" + file + "' exists but is a directory");
135             }
136             if (file.canRead() == false) {
137                 throw new IOException("File '" + file + "' cannot be read");
138             }
139         } else {
140             throw new FileNotFoundException("File '" + file + "' does not exist");
141         }
142         return new FileInputStream(file);
143     }
144 
145     //-----------------------------------------------------------------------
146     /**
147      * Opens a {@link FileOutputStream} for the specified file, checking and
148      * creating the parent directory if it does not exist.
149      * <p>
150      * At the end of the method either the stream will be successfully opened,
151      * or an exception will have been thrown.
152      * <p>
153      * The parent directory will be created if it does not exist.
154      * The file will be created if it does not exist.
155      * An exception is thrown if the file object exists but is a directory.
156      * An exception is thrown if the file exists but cannot be written to.
157      * An exception is thrown if the parent directory cannot be created.
158      *
159      * @param file  the file to open for output, must not be <code>null</code>
160      * @return a new {@link FileOutputStream} for the specified file
161      * @throws IOException if the file object is a directory
162      * @throws IOException if the file cannot be written to
163      * @throws IOException if a parent directory needs creating but that fails
164      * @since Commons IO 1.3
165      */
openOutputStream(File file)166     public static FileOutputStream openOutputStream(File file) throws IOException {
167         if (file.exists()) {
168             if (file.isDirectory()) {
169                 throw new IOException("File '" + file + "' exists but is a directory");
170             }
171             if (file.canWrite() == false) {
172                 throw new IOException("File '" + file + "' cannot be written to");
173             }
174         } else {
175             File parent = file.getParentFile();
176             if (parent != null && parent.exists() == false) {
177                 if (parent.mkdirs() == false) {
178                     throw new IOException("File '" + file + "' could not be created");
179                 }
180             }
181         }
182         return new FileOutputStream(file);
183     }
184 
185     //-----------------------------------------------------------------------
186     /**
187      * Returns a human-readable version of the file size, where the input
188      * represents a specific number of bytes.
189      *
190      * @param size  the number of bytes
191      * @return a human-readable display value (includes units)
192      */
byteCountToDisplaySize(long size)193     public static String byteCountToDisplaySize(long size) {
194         String displaySize;
195 
196         if (size / ONE_GB > 0) {
197             displaySize = String.valueOf(size / ONE_GB) + " GB";
198         } else if (size / ONE_MB > 0) {
199             displaySize = String.valueOf(size / ONE_MB) + " MB";
200         } else if (size / ONE_KB > 0) {
201             displaySize = String.valueOf(size / ONE_KB) + " KB";
202         } else {
203             displaySize = String.valueOf(size) + " bytes";
204         }
205         return displaySize;
206     }
207 
208     //-----------------------------------------------------------------------
209     /**
210      * Implements the same behaviour as the "touch" utility on Unix. It creates
211      * a new file with size 0 or, if the file exists already, it is opened and
212      * closed without modifying it, but updating the file date and time.
213      * <p>
214      * NOTE: As from v1.3, this method throws an IOException if the last
215      * modified date of the file cannot be set. Also, as from v1.3 this method
216      * creates parent directories if they do not exist.
217      *
218      * @param file  the File to touch
219      * @throws IOException If an I/O problem occurs
220      */
touch(File file)221     public static void touch(File file) throws IOException {
222         if (!file.exists()) {
223             OutputStream out = openOutputStream(file);
224             IOUtils.closeQuietly(out);
225         }
226         boolean success = file.setLastModified(System.currentTimeMillis());
227         if (!success) {
228             throw new IOException("Unable to set the last modification time for " + file);
229         }
230     }
231 
232     //-----------------------------------------------------------------------
233     /**
234      * Converts a Collection containing java.io.File instanced into array
235      * representation. This is to account for the difference between
236      * File.listFiles() and FileUtils.listFiles().
237      *
238      * @param files  a Collection containing java.io.File instances
239      * @return an array of java.io.File
240      */
convertFileCollectionToFileArray(Collection<File> files)241     public static File[] convertFileCollectionToFileArray(Collection<File> files) {
242          return files.toArray(new File[files.size()]);
243     }
244 
245     //-----------------------------------------------------------------------
246     /**
247      * Finds files within a given directory (and optionally its
248      * subdirectories). All files found are filtered by an IOFileFilter.
249      *
250      * @param files the collection of files found.
251      * @param directory the directory to search in.
252      * @param filter the filter to apply to files and directories.
253      */
innerListFiles(Collection<File> files, File directory, IOFileFilter filter)254     private static void innerListFiles(Collection<File> files, File directory,
255             IOFileFilter filter) {
256         File[] found = directory.listFiles((FileFilter) filter);
257         if (found != null) {
258             for (int i = 0; i < found.length; i++) {
259                 if (found[i].isDirectory()) {
260                     innerListFiles(files, found[i], filter);
261                 } else {
262                     files.add(found[i]);
263                 }
264             }
265         }
266     }
267 
268     /**
269      * Finds files within a given directory (and optionally its
270      * subdirectories). All files found are filtered by an IOFileFilter.
271      * <p>
272      * If your search should recurse into subdirectories you can pass in
273      * an IOFileFilter for directories. You don't need to bind a
274      * DirectoryFileFilter (via logical AND) to this filter. This method does
275      * that for you.
276      * <p>
277      * An example: If you want to search through all directories called
278      * "temp" you pass in <code>FileFilterUtils.NameFileFilter("temp")</code>
279      * <p>
280      * Another common usage of this method is find files in a directory
281      * tree but ignoring the directories generated CVS. You can simply pass
282      * in <code>FileFilterUtils.makeCVSAware(null)</code>.
283      *
284      * @param directory  the directory to search in
285      * @param fileFilter  filter to apply when finding files.
286      * @param dirFilter  optional filter to apply when finding subdirectories.
287      * If this parameter is <code>null</code>, subdirectories will not be included in the
288      * search. Use TrueFileFilter.INSTANCE to match all directories.
289      * @return an collection of java.io.File with the matching files
290      * @see org.apache.commons.io.filefilter.FileFilterUtils
291      * @see org.apache.commons.io.filefilter.NameFileFilter
292      */
listFiles( File directory, IOFileFilter fileFilter, IOFileFilter dirFilter)293     public static Collection<File> listFiles(
294             File directory, IOFileFilter fileFilter, IOFileFilter dirFilter) {
295         if (!directory.isDirectory()) {
296             throw new IllegalArgumentException(
297                     "Parameter 'directory' is not a directory");
298         }
299         if (fileFilter == null) {
300             throw new NullPointerException("Parameter 'fileFilter' is null");
301         }
302 
303         //Setup effective file filter
304         IOFileFilter effFileFilter = FileFilterUtils.andFileFilter(fileFilter,
305             FileFilterUtils.notFileFilter(DirectoryFileFilter.INSTANCE));
306 
307         //Setup effective directory filter
308         IOFileFilter effDirFilter;
309         if (dirFilter == null) {
310             effDirFilter = FalseFileFilter.INSTANCE;
311         } else {
312             effDirFilter = FileFilterUtils.andFileFilter(dirFilter,
313                 DirectoryFileFilter.INSTANCE);
314         }
315 
316         //Find files
317         Collection<File> files = new java.util.LinkedList<File>();
318         innerListFiles(files, directory,
319             FileFilterUtils.orFileFilter(effFileFilter, effDirFilter));
320         return files;
321     }
322 
323     /**
324      * Allows iteration over the files in given directory (and optionally
325      * its subdirectories).
326      * <p>
327      * All files found are filtered by an IOFileFilter. This method is
328      * based on {@link #listFiles(File, IOFileFilter, IOFileFilter)}.
329      *
330      * @param directory  the directory to search in
331      * @param fileFilter  filter to apply when finding files.
332      * @param dirFilter  optional filter to apply when finding subdirectories.
333      * If this parameter is <code>null</code>, subdirectories will not be included in the
334      * search. Use TrueFileFilter.INSTANCE to match all directories.
335      * @return an iterator of java.io.File for the matching files
336      * @see org.apache.commons.io.filefilter.FileFilterUtils
337      * @see org.apache.commons.io.filefilter.NameFileFilter
338      * @since Commons IO 1.2
339      */
iterateFiles( File directory, IOFileFilter fileFilter, IOFileFilter dirFilter)340     public static Iterator<File> iterateFiles(
341             File directory, IOFileFilter fileFilter, IOFileFilter dirFilter) {
342         return listFiles(directory, fileFilter, dirFilter).iterator();
343     }
344 
345     //-----------------------------------------------------------------------
346     /**
347      * Converts an array of file extensions to suffixes for use
348      * with IOFileFilters.
349      *
350      * @param extensions  an array of extensions. Format: {"java", "xml"}
351      * @return an array of suffixes. Format: {".java", ".xml"}
352      */
toSuffixes(String[] extensions)353     private static String[] toSuffixes(String[] extensions) {
354         String[] suffixes = new String[extensions.length];
355         for (int i = 0; i < extensions.length; i++) {
356             suffixes[i] = "." + extensions[i];
357         }
358         return suffixes;
359     }
360 
361 
362     /**
363      * Finds files within a given directory (and optionally its subdirectories)
364      * which match an array of extensions.
365      *
366      * @param directory  the directory to search in
367      * @param extensions  an array of extensions, ex. {"java","xml"}. If this
368      * parameter is <code>null</code>, all files are returned.
369      * @param recursive  if true all subdirectories are searched as well
370      * @return an collection of java.io.File with the matching files
371      */
listFiles( File directory, String[] extensions, boolean recursive)372     public static Collection<File> listFiles(
373             File directory, String[] extensions, boolean recursive) {
374         IOFileFilter filter;
375         if (extensions == null) {
376             filter = TrueFileFilter.INSTANCE;
377         } else {
378             String[] suffixes = toSuffixes(extensions);
379             filter = new SuffixFileFilter(suffixes);
380         }
381         return listFiles(directory, filter,
382             (recursive ? TrueFileFilter.INSTANCE : FalseFileFilter.INSTANCE));
383     }
384 
385     /**
386      * Allows iteration over the files in a given directory (and optionally
387      * its subdirectories) which match an array of extensions. This method
388      * is based on {@link #listFiles(File, String[], boolean)}.
389      *
390      * @param directory  the directory to search in
391      * @param extensions  an array of extensions, ex. {"java","xml"}. If this
392      * parameter is <code>null</code>, all files are returned.
393      * @param recursive  if true all subdirectories are searched as well
394      * @return an iterator of java.io.File with the matching files
395      * @since Commons IO 1.2
396      */
iterateFiles( File directory, String[] extensions, boolean recursive)397     public static Iterator<File> iterateFiles(
398             File directory, String[] extensions, boolean recursive) {
399         return listFiles(directory, extensions, recursive).iterator();
400     }
401 
402     //-----------------------------------------------------------------------
403     /**
404      * Compares the contents of two files to determine if they are equal or not.
405      * <p>
406      * This method checks to see if the two files are different lengths
407      * or if they point to the same file, before resorting to byte-by-byte
408      * comparison of the contents.
409      * <p>
410      * Code origin: Avalon
411      *
412      * @param file1  the first file
413      * @param file2  the second file
414      * @return true if the content of the files are equal or they both don't
415      * exist, false otherwise
416      * @throws IOException in case of an I/O error
417      */
contentEquals(File file1, File file2)418     public static boolean contentEquals(File file1, File file2) throws IOException {
419         boolean file1Exists = file1.exists();
420         if (file1Exists != file2.exists()) {
421             return false;
422         }
423 
424         if (!file1Exists) {
425             // two not existing files are equal
426             return true;
427         }
428 
429         if (file1.isDirectory() || file2.isDirectory()) {
430             // don't want to compare directory contents
431             throw new IOException("Can't compare directories, only files");
432         }
433 
434         if (file1.length() != file2.length()) {
435             // lengths differ, cannot be equal
436             return false;
437         }
438 
439         if (file1.getCanonicalFile().equals(file2.getCanonicalFile())) {
440             // same file
441             return true;
442         }
443 
444         InputStream input1 = null;
445         InputStream input2 = null;
446         try {
447             input1 = new FileInputStream(file1);
448             input2 = new FileInputStream(file2);
449             return IOUtils.contentEquals(input1, input2);
450 
451         } finally {
452             IOUtils.closeQuietly(input1);
453             IOUtils.closeQuietly(input2);
454         }
455     }
456 
457     //-----------------------------------------------------------------------
458     /**
459      * Convert from a <code>URL</code> to a <code>File</code>.
460      * <p>
461      * From version 1.1 this method will decode the URL.
462      * Syntax such as <code>file:///my%20docs/file.txt</code> will be
463      * correctly decoded to <code>/my docs/file.txt</code>.
464      *
465      * @param url  the file URL to convert, <code>null</code> returns <code>null</code>
466      * @return the equivalent <code>File</code> object, or <code>null</code>
467      *  if the URL's protocol is not <code>file</code>
468      * @throws IllegalArgumentException if the file is incorrectly encoded
469      */
toFile(URL url)470     public static File toFile(URL url) {
471         if (url == null || !url.getProtocol().equals("file")) {
472             return null;
473         } else {
474             String filename = url.getFile().replace('/', File.separatorChar);
475             int pos =0;
476             while ((pos = filename.indexOf('%', pos)) >= 0) {
477                 if (pos + 2 < filename.length()) {
478                     String hexStr = filename.substring(pos + 1, pos + 3);
479                     char ch = (char) Integer.parseInt(hexStr, 16);
480                     filename = filename.substring(0, pos) + ch + filename.substring(pos + 3);
481                 }
482             }
483             return new File(filename);
484         }
485     }
486 
487     /**
488      * Converts each of an array of <code>URL</code> to a <code>File</code>.
489      * <p>
490      * Returns an array of the same size as the input.
491      * If the input is <code>null</code>, an empty array is returned.
492      * If the input contains <code>null</code>, the output array contains <code>null</code> at the same
493      * index.
494      * <p>
495      * This method will decode the URL.
496      * Syntax such as <code>file:///my%20docs/file.txt</code> will be
497      * correctly decoded to <code>/my docs/file.txt</code>.
498      *
499      * @param urls  the file URLs to convert, <code>null</code> returns empty array
500      * @return a non-<code>null</code> array of Files matching the input, with a <code>null</code> item
501      *  if there was a <code>null</code> at that index in the input array
502      * @throws IllegalArgumentException if any file is not a URL file
503      * @throws IllegalArgumentException if any file is incorrectly encoded
504      * @since Commons IO 1.1
505      */
toFiles(URL[] urls)506     public static File[] toFiles(URL[] urls) {
507         if (urls == null || urls.length == 0) {
508             return EMPTY_FILE_ARRAY;
509         }
510         File[] files = new File[urls.length];
511         for (int i = 0; i < urls.length; i++) {
512             URL url = urls[i];
513             if (url != null) {
514                 if (url.getProtocol().equals("file") == false) {
515                     throw new IllegalArgumentException(
516                             "URL could not be converted to a File: " + url);
517                 }
518                 files[i] = toFile(url);
519             }
520         }
521         return files;
522     }
523 
524     /**
525      * Converts each of an array of <code>File</code> to a <code>URL</code>.
526      * <p>
527      * Returns an array of the same size as the input.
528      *
529      * @param files  the files to convert
530      * @return an array of URLs matching the input
531      * @throws IOException if a file cannot be converted
532      */
toURLs(File[] files)533     public static URL[] toURLs(File[] files) throws IOException {
534         URL[] urls = new URL[files.length];
535 
536         for (int i = 0; i < urls.length; i++) {
537             urls[i] = files[i].toURI().toURL();
538         }
539 
540         return urls;
541     }
542 
543     //-----------------------------------------------------------------------
544     /**
545      * Copies a file to a directory preserving the file date.
546      * <p>
547      * This method copies the contents of the specified source file
548      * to a file of the same name in the specified destination directory.
549      * The destination directory is created if it does not exist.
550      * If the destination file exists, then this method will overwrite it.
551      *
552      * @param srcFile  an existing file to copy, must not be <code>null</code>
553      * @param destDir  the directory to place the copy in, must not be <code>null</code>
554      *
555      * @throws NullPointerException if source or destination is null
556      * @throws IOException if source or destination is invalid
557      * @throws IOException if an IO error occurs during copying
558      * @see #copyFile(File, File, boolean)
559      */
copyFileToDirectory(File srcFile, File destDir)560     public static void copyFileToDirectory(File srcFile, File destDir) throws IOException {
561         copyFileToDirectory(srcFile, destDir, true);
562     }
563 
564     /**
565      * Copies a file to a directory optionally preserving the file date.
566      * <p>
567      * This method copies the contents of the specified source file
568      * to a file of the same name in the specified destination directory.
569      * The destination directory is created if it does not exist.
570      * If the destination file exists, then this method will overwrite it.
571      *
572      * @param srcFile  an existing file to copy, must not be <code>null</code>
573      * @param destDir  the directory to place the copy in, must not be <code>null</code>
574      * @param preserveFileDate  true if the file date of the copy
575      *  should be the same as the original
576      *
577      * @throws NullPointerException if source or destination is <code>null</code>
578      * @throws IOException if source or destination is invalid
579      * @throws IOException if an IO error occurs during copying
580      * @see #copyFile(File, File, boolean)
581      * @since Commons IO 1.3
582      */
copyFileToDirectory(File srcFile, File destDir, boolean preserveFileDate)583     public static void copyFileToDirectory(File srcFile, File destDir, boolean preserveFileDate) throws IOException {
584         if (destDir == null) {
585             throw new NullPointerException("Destination must not be null");
586         }
587         if (destDir.exists() && destDir.isDirectory() == false) {
588             throw new IllegalArgumentException("Destination '" + destDir + "' is not a directory");
589         }
590         copyFile(srcFile, new File(destDir, srcFile.getName()), preserveFileDate);
591     }
592 
593     /**
594      * Copies a file to a new location preserving the file date.
595      * <p>
596      * This method copies the contents of the specified source file to the
597      * specified destination file. The directory holding the destination file is
598      * created if it does not exist. If the destination file exists, then this
599      * method will overwrite it.
600      *
601      * @param srcFile  an existing file to copy, must not be <code>null</code>
602      * @param destFile  the new file, must not be <code>null</code>
603      *
604      * @throws NullPointerException if source or destination is <code>null</code>
605      * @throws IOException if source or destination is invalid
606      * @throws IOException if an IO error occurs during copying
607      * @see #copyFileToDirectory(File, File)
608      */
copyFile(File srcFile, File destFile)609     public static void copyFile(File srcFile, File destFile) throws IOException {
610         copyFile(srcFile, destFile, true);
611     }
612 
613     /**
614      * Copies a file to a new location.
615      * <p>
616      * This method copies the contents of the specified source file
617      * to the specified destination file.
618      * The directory holding the destination file is created if it does not exist.
619      * If the destination file exists, then this method will overwrite it.
620      *
621      * @param srcFile  an existing file to copy, must not be <code>null</code>
622      * @param destFile  the new file, must not be <code>null</code>
623      * @param preserveFileDate  true if the file date of the copy
624      *  should be the same as the original
625      *
626      * @throws NullPointerException if source or destination is <code>null</code>
627      * @throws IOException if source or destination is invalid
628      * @throws IOException if an IO error occurs during copying
629      * @see #copyFileToDirectory(File, File, boolean)
630      */
copyFile(File srcFile, File destFile, boolean preserveFileDate)631     public static void copyFile(File srcFile, File destFile,
632             boolean preserveFileDate) throws IOException {
633         if (srcFile == null) {
634             throw new NullPointerException("Source must not be null");
635         }
636         if (destFile == null) {
637             throw new NullPointerException("Destination must not be null");
638         }
639         if (srcFile.exists() == false) {
640             throw new FileNotFoundException("Source '" + srcFile + "' does not exist");
641         }
642         if (srcFile.isDirectory()) {
643             throw new IOException("Source '" + srcFile + "' exists but is a directory");
644         }
645         if (srcFile.getCanonicalPath().equals(destFile.getCanonicalPath())) {
646             throw new IOException("Source '" + srcFile + "' and destination '" + destFile + "' are the same");
647         }
648         if (destFile.getParentFile() != null && destFile.getParentFile().exists() == false) {
649             if (destFile.getParentFile().mkdirs() == false) {
650                 throw new IOException("Destination '" + destFile + "' directory cannot be created");
651             }
652         }
653         if (destFile.exists() && destFile.canWrite() == false) {
654             throw new IOException("Destination '" + destFile + "' exists but is read-only");
655         }
656         doCopyFile(srcFile, destFile, preserveFileDate);
657     }
658 
659     /**
660      * Internal copy file method.
661      *
662      * @param srcFile  the validated source file, must not be <code>null</code>
663      * @param destFile  the validated destination file, must not be <code>null</code>
664      * @param preserveFileDate  whether to preserve the file date
665      * @throws IOException if an error occurs
666      */
doCopyFile(File srcFile, File destFile, boolean preserveFileDate)667     private static void doCopyFile(File srcFile, File destFile, boolean preserveFileDate) throws IOException {
668         if (destFile.exists() && destFile.isDirectory()) {
669             throw new IOException("Destination '" + destFile + "' exists but is a directory");
670         }
671 
672         FileInputStream input = new FileInputStream(srcFile);
673         try {
674             FileOutputStream output = new FileOutputStream(destFile);
675             try {
676                 IOUtils.copy(input, output);
677             } finally {
678                 IOUtils.closeQuietly(output);
679             }
680         } finally {
681             IOUtils.closeQuietly(input);
682         }
683 
684         if (srcFile.length() != destFile.length()) {
685             throw new IOException("Failed to copy full contents from '" +
686                     srcFile + "' to '" + destFile + "'");
687         }
688         if (preserveFileDate) {
689             destFile.setLastModified(srcFile.lastModified());
690         }
691     }
692 
693     //-----------------------------------------------------------------------
694     /**
695      * Copies a directory to within another directory preserving the file dates.
696      * <p>
697      * This method copies the source directory and all its contents to a
698      * directory of the same name in the specified destination directory.
699      * <p>
700      * The destination directory is created if it does not exist.
701      * If the destination directory did exist, then this method merges
702      * the source with the destination, with the source taking precedence.
703      *
704      * @param srcDir  an existing directory to copy, must not be <code>null</code>
705      * @param destDir  the directory to place the copy in, must not be <code>null</code>
706      *
707      * @throws NullPointerException if source or destination is <code>null</code>
708      * @throws IOException if source or destination is invalid
709      * @throws IOException if an IO error occurs during copying
710      * @since Commons IO 1.2
711      */
copyDirectoryToDirectory(File srcDir, File destDir)712     public static void copyDirectoryToDirectory(File srcDir, File destDir) throws IOException {
713         if (srcDir == null) {
714             throw new NullPointerException("Source must not be null");
715         }
716         if (srcDir.exists() && srcDir.isDirectory() == false) {
717             throw new IllegalArgumentException("Source '" + destDir + "' is not a directory");
718         }
719         if (destDir == null) {
720             throw new NullPointerException("Destination must not be null");
721         }
722         if (destDir.exists() && destDir.isDirectory() == false) {
723             throw new IllegalArgumentException("Destination '" + destDir + "' is not a directory");
724         }
725         copyDirectory(srcDir, new File(destDir, srcDir.getName()), true);
726     }
727 
728     /**
729      * Copies a whole directory to a new location preserving the file dates.
730      * <p>
731      * This method copies the specified directory and all its child
732      * directories and files to the specified destination.
733      * The destination is the new location and name of the directory.
734      * <p>
735      * The destination directory is created if it does not exist.
736      * If the destination directory did exist, then this method merges
737      * the source with the destination, with the source taking precedence.
738      *
739      * @param srcDir  an existing directory to copy, must not be <code>null</code>
740      * @param destDir  the new directory, must not be <code>null</code>
741      *
742      * @throws NullPointerException if source or destination is <code>null</code>
743      * @throws IOException if source or destination is invalid
744      * @throws IOException if an IO error occurs during copying
745      * @since Commons IO 1.1
746      */
747     @VisibleForTesting
copyDirectory(File srcDir, File destDir)748     public static void copyDirectory(File srcDir, File destDir) throws IOException {
749         copyDirectory(srcDir, destDir, true);
750     }
751 
752     /**
753      * Copies a whole directory to a new location.
754      * <p>
755      * This method copies the contents of the specified source directory
756      * to within the specified destination directory.
757      * <p>
758      * The destination directory is created if it does not exist.
759      * If the destination directory did exist, then this method merges
760      * the source with the destination, with the source taking precedence.
761      *
762      * @param srcDir  an existing directory to copy, must not be <code>null</code>
763      * @param destDir  the new directory, must not be <code>null</code>
764      * @param preserveFileDate  true if the file date of the copy
765      *  should be the same as the original
766      *
767      * @throws NullPointerException if source or destination is <code>null</code>
768      * @throws IOException if source or destination is invalid
769      * @throws IOException if an IO error occurs during copying
770      * @since Commons IO 1.1
771      */
copyDirectory(File srcDir, File destDir, boolean preserveFileDate)772     public static void copyDirectory(File srcDir, File destDir,
773             boolean preserveFileDate) throws IOException {
774         copyDirectory(srcDir, destDir, null, preserveFileDate);
775     }
776 
777     /**
778      * Copies a filtered directory to a new location preserving the file dates.
779      * <p>
780      * This method copies the contents of the specified source directory
781      * to within the specified destination directory.
782      * <p>
783      * The destination directory is created if it does not exist.
784      * If the destination directory did exist, then this method merges
785      * the source with the destination, with the source taking precedence.
786      *
787      * <h4>Example: Copy directories only</h4>
788      *  <pre>
789      *  // only copy the directory structure
790      *  FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY);
791      *  </pre>
792      *
793      * <h4>Example: Copy directories and txt files</h4>
794      *  <pre>
795      *  // Create a filter for ".txt" files
796      *  IOFileFilter txtSuffixFilter = FileFilterUtils.suffixFileFilter(".txt");
797      *  IOFileFilter txtFiles = FileFilterUtils.andFileFilter(FileFileFilter.FILE, txtSuffixFilter);
798      *
799      *  // Create a filter for either directories or ".txt" files
800      *  FileFilter filter = FileFilterUtils.orFileFilter(DirectoryFileFilter.DIRECTORY, txtFiles);
801      *
802      *  // Copy using the filter
803      *  FileUtils.copyDirectory(srcDir, destDir, filter);
804      *  </pre>
805      *
806      * @param srcDir  an existing directory to copy, must not be <code>null</code>
807      * @param destDir  the new directory, must not be <code>null</code>
808      * @param filter  the filter to apply, null means copy all directories and files
809      *  should be the same as the original
810      *
811      * @throws NullPointerException if source or destination is <code>null</code>
812      * @throws IOException if source or destination is invalid
813      * @throws IOException if an IO error occurs during copying
814      * @since Commons IO 1.4
815      */
copyDirectory(File srcDir, File destDir, FileFilter filter)816     public static void copyDirectory(File srcDir, File destDir,
817             FileFilter filter) throws IOException {
818         copyDirectory(srcDir, destDir, filter, true);
819     }
820 
821     /**
822      * Copies a filtered directory to a new location.
823      * <p>
824      * This method copies the contents of the specified source directory
825      * to within the specified destination directory.
826      * <p>
827      * The destination directory is created if it does not exist.
828      * If the destination directory did exist, then this method merges
829      * the source with the destination, with the source taking precedence.
830      *
831      * <h4>Example: Copy directories only</h4>
832      *  <pre>
833      *  // only copy the directory structure
834      *  FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY, false);
835      *  </pre>
836      *
837      * <h4>Example: Copy directories and txt files</h4>
838      *  <pre>
839      *  // Create a filter for ".txt" files
840      *  IOFileFilter txtSuffixFilter = FileFilterUtils.suffixFileFilter(".txt");
841      *  IOFileFilter txtFiles = FileFilterUtils.andFileFilter(FileFileFilter.FILE, txtSuffixFilter);
842      *
843      *  // Create a filter for either directories or ".txt" files
844      *  FileFilter filter = FileFilterUtils.orFileFilter(DirectoryFileFilter.DIRECTORY, txtFiles);
845      *
846      *  // Copy using the filter
847      *  FileUtils.copyDirectory(srcDir, destDir, filter, false);
848      *  </pre>
849      *
850      * @param srcDir  an existing directory to copy, must not be <code>null</code>
851      * @param destDir  the new directory, must not be <code>null</code>
852      * @param filter  the filter to apply, null means copy all directories and files
853      * @param preserveFileDate  true if the file date of the copy
854      *  should be the same as the original
855      *
856      * @throws NullPointerException if source or destination is <code>null</code>
857      * @throws IOException if source or destination is invalid
858      * @throws IOException if an IO error occurs during copying
859      * @since Commons IO 1.4
860      */
copyDirectory(File srcDir, File destDir, FileFilter filter, boolean preserveFileDate)861     public static void copyDirectory(File srcDir, File destDir,
862             FileFilter filter, boolean preserveFileDate) throws IOException {
863         if (srcDir == null) {
864             throw new NullPointerException("Source must not be null");
865         }
866         if (destDir == null) {
867             throw new NullPointerException("Destination must not be null");
868         }
869         if (srcDir.exists() == false) {
870             throw new FileNotFoundException("Source '" + srcDir + "' does not exist");
871         }
872         if (srcDir.isDirectory() == false) {
873             throw new IOException("Source '" + srcDir + "' exists but is not a directory");
874         }
875         if (srcDir.getCanonicalPath().equals(destDir.getCanonicalPath())) {
876             throw new IOException("Source '" + srcDir + "' and destination '" + destDir + "' are the same");
877         }
878 
879         // Cater for destination being directory within the source directory (see IO-141)
880         List<String> exclusionList = null;
881         if (destDir.getCanonicalPath().startsWith(srcDir.getCanonicalPath())) {
882             File[] srcFiles = filter == null ? srcDir.listFiles() : srcDir.listFiles(filter);
883             if (srcFiles != null && srcFiles.length > 0) {
884                 exclusionList = new ArrayList<String>(srcFiles.length);
885                 for (int i = 0; i < srcFiles.length; i++) {
886                     File copiedFile = new File(destDir, srcFiles[i].getName());
887                     exclusionList.add(copiedFile.getCanonicalPath());
888                 }
889             }
890         }
891         doCopyDirectory(srcDir, destDir, filter, preserveFileDate, exclusionList);
892     }
893 
894     /**
895      * Internal copy directory method.
896      *
897      * @param srcDir  the validated source directory, must not be <code>null</code>
898      * @param destDir  the validated destination directory, must not be <code>null</code>
899      * @param filter  the filter to apply, null means copy all directories and files
900      * @param preserveFileDate  whether to preserve the file date
901      * @param exclusionList  List of files and directories to exclude from the copy, may be null
902      * @throws IOException if an error occurs
903      * @since Commons IO 1.1
904      */
doCopyDirectory(File srcDir, File destDir, FileFilter filter, boolean preserveFileDate, List<String> exclusionList)905     private static void doCopyDirectory(File srcDir, File destDir, FileFilter filter,
906             boolean preserveFileDate, List<String> exclusionList) throws IOException {
907         if (destDir.exists()) {
908             if (destDir.isDirectory() == false) {
909                 throw new IOException("Destination '" + destDir + "' exists but is not a directory");
910             }
911         } else {
912             if (destDir.mkdirs() == false) {
913                 throw new IOException("Destination '" + destDir + "' directory cannot be created");
914             }
915             if (preserveFileDate) {
916                 destDir.setLastModified(srcDir.lastModified());
917             }
918         }
919         if (destDir.canWrite() == false) {
920             throw new IOException("Destination '" + destDir + "' cannot be written to");
921         }
922         // recurse
923         File[] files = filter == null ? srcDir.listFiles() : srcDir.listFiles(filter);
924         if (files == null) {  // null if security restricted
925             throw new IOException("Failed to list contents of " + srcDir);
926         }
927         for (int i = 0; i < files.length; i++) {
928             File copiedFile = new File(destDir, files[i].getName());
929             if (exclusionList == null || !exclusionList.contains(files[i].getCanonicalPath())) {
930                 if (files[i].isDirectory()) {
931                     doCopyDirectory(files[i], copiedFile, filter, preserveFileDate, exclusionList);
932                 } else {
933                     doCopyFile(files[i], copiedFile, preserveFileDate);
934                 }
935             }
936         }
937     }
938 
939     //-----------------------------------------------------------------------
940     /**
941      * Copies bytes from the URL <code>source</code> to a file
942      * <code>destination</code>. The directories up to <code>destination</code>
943      * will be created if they don't already exist. <code>destination</code>
944      * will be overwritten if it already exists.
945      *
946      * @param source  the <code>URL</code> to copy bytes from, must not be <code>null</code>
947      * @param destination  the non-directory <code>File</code> to write bytes to
948      *  (possibly overwriting), must not be <code>null</code>
949      * @throws IOException if <code>source</code> URL cannot be opened
950      * @throws IOException if <code>destination</code> is a directory
951      * @throws IOException if <code>destination</code> cannot be written
952      * @throws IOException if <code>destination</code> needs creating but can't be
953      * @throws IOException if an IO error occurs during copying
954      */
copyURLToFile(URL source, File destination)955     public static void copyURLToFile(URL source, File destination) throws IOException {
956         InputStream input = source.openStream();
957         try {
958             FileOutputStream output = openOutputStream(destination);
959             try {
960                 IOUtils.copy(input, output);
961             } finally {
962                 IOUtils.closeQuietly(output);
963             }
964         } finally {
965             IOUtils.closeQuietly(input);
966         }
967     }
968 
969     //-----------------------------------------------------------------------
970     /**
971      * Deletes a directory recursively.
972      *
973      * @param directory  directory to delete
974      * @throws IOException in case deletion is unsuccessful
975      */
deleteDirectory(File directory)976     public static void deleteDirectory(File directory) throws IOException {
977         if (!directory.exists()) {
978             return;
979         }
980 
981         cleanDirectory(directory);
982         if (!directory.delete()) {
983             String message =
984                 "Unable to delete directory " + directory + ".";
985             throw new IOException(message);
986         }
987     }
988 
989     /**
990      * Deletes a file, never throwing an exception. If file is a directory, delete it and all sub-directories.
991      * <p>
992      * The difference between File.delete() and this method are:
993      * <ul>
994      * <li>A directory to be deleted does not have to be empty.</li>
995      * <li>No exceptions are thrown when a file or directory cannot be deleted.</li>
996      * </ul>
997      *
998      * @param file  file or directory to delete, can be <code>null</code>
999      * @return <code>true</code> if the file or directory was deleted, otherwise
1000      * <code>false</code>
1001      *
1002      * @since Commons IO 1.4
1003      */
deleteQuietly(File file)1004     public static boolean deleteQuietly(File file) {
1005         if (file == null) {
1006             return false;
1007         }
1008         try {
1009             if (file.isDirectory()) {
1010                 cleanDirectory(file);
1011             }
1012         } catch (Exception e) {
1013         }
1014 
1015         try {
1016             return file.delete();
1017         } catch (Exception e) {
1018             return false;
1019         }
1020     }
1021 
1022     /**
1023      * Cleans a directory without deleting it.
1024      *
1025      * @param directory directory to clean
1026      * @throws IOException in case cleaning is unsuccessful
1027      */
cleanDirectory(File directory)1028     public static void cleanDirectory(File directory) throws IOException {
1029         if (!directory.exists()) {
1030             String message = directory + " does not exist";
1031             throw new IllegalArgumentException(message);
1032         }
1033 
1034         if (!directory.isDirectory()) {
1035             String message = directory + " is not a directory";
1036             throw new IllegalArgumentException(message);
1037         }
1038 
1039         File[] files = directory.listFiles();
1040         if (files == null) {  // null if security restricted
1041             throw new IOException("Failed to list contents of " + directory);
1042         }
1043 
1044         IOException exception = null;
1045         for (int i = 0; i < files.length; i++) {
1046             File file = files[i];
1047             try {
1048                 forceDelete(file);
1049             } catch (IOException ioe) {
1050                 exception = ioe;
1051             }
1052         }
1053 
1054         if (null != exception) {
1055             throw exception;
1056         }
1057     }
1058 
1059     //-----------------------------------------------------------------------
1060     /**
1061      * Waits for NFS to propagate a file creation, imposing a timeout.
1062      * <p>
1063      * This method repeatedly tests {@link File#exists()} until it returns
1064      * true up to the maximum time specified in seconds.
1065      *
1066      * @param file  the file to check, must not be <code>null</code>
1067      * @param seconds  the maximum time in seconds to wait
1068      * @return true if file exists
1069      * @throws NullPointerException if the file is <code>null</code>
1070      */
waitFor(File file, int seconds)1071     public static boolean waitFor(File file, int seconds) {
1072         int timeout = 0;
1073         int tick = 0;
1074         while (!file.exists()) {
1075             if (tick++ >= 10) {
1076                 tick = 0;
1077                 if (timeout++ > seconds) {
1078                     return false;
1079                 }
1080             }
1081             try {
1082                 Thread.sleep(100);
1083             } catch (InterruptedException ignore) {
1084                 // ignore exception
1085             } catch (Exception ex) {
1086                 break;
1087             }
1088         }
1089         return true;
1090     }
1091 
1092     //-----------------------------------------------------------------------
1093     /**
1094      * Reads the contents of a file into a String.
1095      * The file is always closed.
1096      *
1097      * @param file  the file to read, must not be <code>null</code>
1098      * @param encoding  the encoding to use, <code>null</code> means platform default
1099      * @return the file contents, never <code>null</code>
1100      * @throws IOException in case of an I/O error
1101      * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
1102      */
readFileToString(File file, String encoding)1103     public static String readFileToString(File file, String encoding) throws IOException {
1104         InputStream in = null;
1105         try {
1106             in = openInputStream(file);
1107             return IOUtils.toString(in, encoding);
1108         } finally {
1109             IOUtils.closeQuietly(in);
1110         }
1111     }
1112 
1113 
1114     /**
1115      * Reads the contents of a file into a String using the default encoding for the VM.
1116      * The file is always closed.
1117      *
1118      * @param file  the file to read, must not be <code>null</code>
1119      * @return the file contents, never <code>null</code>
1120      * @throws IOException in case of an I/O error
1121      * @since Commons IO 1.3.1
1122      */
readFileToString(File file)1123     public static String readFileToString(File file) throws IOException {
1124         return readFileToString(file, null);
1125     }
1126 
1127     /**
1128      * Reads the contents of a file into a byte array.
1129      * The file is always closed.
1130      *
1131      * @param file  the file to read, must not be <code>null</code>
1132      * @return the file contents, never <code>null</code>
1133      * @throws IOException in case of an I/O error
1134      * @since Commons IO 1.1
1135      */
readFileToByteArray(File file)1136     public static byte[] readFileToByteArray(File file) throws IOException {
1137         InputStream in = null;
1138         try {
1139             in = openInputStream(file);
1140             return IOUtils.toByteArray(in);
1141         } finally {
1142             IOUtils.closeQuietly(in);
1143         }
1144     }
1145 
1146     /**
1147      * Reads the contents of a file line by line to a List of Strings.
1148      * The file is always closed.
1149      *
1150      * @param file  the file to read, must not be <code>null</code>
1151      * @param encoding  the encoding to use, <code>null</code> means platform default
1152      * @return the list of Strings representing each line in the file, never <code>null</code>
1153      * @throws IOException in case of an I/O error
1154      * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
1155      * @since Commons IO 1.1
1156      */
readLines(File file, String encoding)1157     public static List<String> readLines(File file, String encoding) throws IOException {
1158         InputStream in = null;
1159         try {
1160             in = openInputStream(file);
1161             return IOUtils.readLines(in, encoding);
1162         } finally {
1163             IOUtils.closeQuietly(in);
1164         }
1165     }
1166 
1167     /**
1168      * Reads the contents of a file line by line to a List of Strings using the default encoding for the VM.
1169      * The file is always closed.
1170      *
1171      * @param file  the file to read, must not be <code>null</code>
1172      * @return the list of Strings representing each line in the file, never <code>null</code>
1173      * @throws IOException in case of an I/O error
1174      * @since Commons IO 1.3
1175      */
readLines(File file)1176     public static List<String> readLines(File file) throws IOException {
1177         return readLines(file, null);
1178     }
1179 
1180     /**
1181      * Returns an Iterator for the lines in a <code>File</code>.
1182      * <p>
1183      * This method opens an <code>InputStream</code> for the file.
1184      * When you have finished with the iterator you should close the stream
1185      * to free internal resources. This can be done by calling the
1186      * {@link LineIterator#close()} or
1187      * {@link LineIterator#closeQuietly(LineIterator)} method.
1188      * <p>
1189      * The recommended usage pattern is:
1190      * <pre>
1191      * LineIterator it = FileUtils.lineIterator(file, "UTF-8");
1192      * try {
1193      *   while (it.hasNext()) {
1194      *     String line = it.nextLine();
1195      *     /// do something with line
1196      *   }
1197      * } finally {
1198      *   LineIterator.closeQuietly(iterator);
1199      * }
1200      * </pre>
1201      * <p>
1202      * If an exception occurs during the creation of the iterator, the
1203      * underlying stream is closed.
1204      *
1205      * @param file  the file to open for input, must not be <code>null</code>
1206      * @param encoding  the encoding to use, <code>null</code> means platform default
1207      * @return an Iterator of the lines in the file, never <code>null</code>
1208      * @throws IOException in case of an I/O error (file closed)
1209      * @since Commons IO 1.2
1210      */
lineIterator(File file, String encoding)1211     public static LineIterator lineIterator(File file, String encoding) throws IOException {
1212         InputStream in = null;
1213         try {
1214             in = openInputStream(file);
1215             return IOUtils.lineIterator(in, encoding);
1216         } catch (IOException ex) {
1217             IOUtils.closeQuietly(in);
1218             throw ex;
1219         } catch (RuntimeException ex) {
1220             IOUtils.closeQuietly(in);
1221             throw ex;
1222         }
1223     }
1224 
1225     /**
1226      * Returns an Iterator for the lines in a <code>File</code> using the default encoding for the VM.
1227      *
1228      * @param file  the file to open for input, must not be <code>null</code>
1229      * @return an Iterator of the lines in the file, never <code>null</code>
1230      * @throws IOException in case of an I/O error (file closed)
1231      * @since Commons IO 1.3
1232      * @see #lineIterator(File, String)
1233      */
lineIterator(File file)1234     public static LineIterator lineIterator(File file) throws IOException {
1235         return lineIterator(file, null);
1236     }
1237 
1238     //-----------------------------------------------------------------------
1239     /**
1240      * Writes a String to a file creating the file if it does not exist.
1241      *
1242      * NOTE: As from v1.3, the parent directories of the file will be created
1243      * if they do not exist.
1244      *
1245      * @param file  the file to write
1246      * @param data  the content to write to the file
1247      * @param encoding  the encoding to use, <code>null</code> means platform default
1248      * @throws IOException in case of an I/O error
1249      * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
1250      */
writeStringToFile(File file, String data, String encoding)1251     public static void writeStringToFile(File file, String data, String encoding) throws IOException {
1252         OutputStream out = null;
1253         try {
1254             out = openOutputStream(file);
1255             IOUtils.write(data, out, encoding);
1256         } finally {
1257             IOUtils.closeQuietly(out);
1258         }
1259     }
1260 
1261     /**
1262      * Writes a String to a file creating the file if it does not exist using the default encoding for the VM.
1263      *
1264      * @param file  the file to write
1265      * @param data  the content to write to the file
1266      * @throws IOException in case of an I/O error
1267      */
writeStringToFile(File file, String data)1268     public static void writeStringToFile(File file, String data) throws IOException {
1269         writeStringToFile(file, data, null);
1270     }
1271 
1272     /**
1273      * Writes a byte array to a file creating the file if it does not exist.
1274      * <p>
1275      * NOTE: As from v1.3, the parent directories of the file will be created
1276      * if they do not exist.
1277      *
1278      * @param file  the file to write to
1279      * @param data  the content to write to the file
1280      * @throws IOException in case of an I/O error
1281      * @since Commons IO 1.1
1282      */
writeByteArrayToFile(File file, byte[] data)1283     public static void writeByteArrayToFile(File file, byte[] data) throws IOException {
1284         OutputStream out = null;
1285         try {
1286             out = openOutputStream(file);
1287             out.write(data);
1288         } finally {
1289             IOUtils.closeQuietly(out);
1290         }
1291     }
1292 
1293     /**
1294      * Writes the <code>toString()</code> value of each item in a collection to
1295      * the specified <code>File</code> line by line.
1296      * The specified character encoding and the default line ending will be used.
1297      * <p>
1298      * NOTE: As from v1.3, the parent directories of the file will be created
1299      * if they do not exist.
1300      *
1301      * @param file  the file to write to
1302      * @param encoding  the encoding to use, <code>null</code> means platform default
1303      * @param lines  the lines to write, <code>null</code> entries produce blank lines
1304      * @throws IOException in case of an I/O error
1305      * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
1306      * @since Commons IO 1.1
1307      */
writeLines(File file, String encoding, Collection<Object> lines)1308     public static void writeLines(File file, String encoding, Collection<Object> lines) throws IOException {
1309         writeLines(file, encoding, lines, null);
1310     }
1311 
1312     /**
1313      * Writes the <code>toString()</code> value of each item in a collection to
1314      * the specified <code>File</code> line by line.
1315      * The default VM encoding and the default line ending will be used.
1316      *
1317      * @param file  the file to write to
1318      * @param lines  the lines to write, <code>null</code> entries produce blank lines
1319      * @throws IOException in case of an I/O error
1320      * @since Commons IO 1.3
1321      */
writeLines(File file, Collection<Object> lines)1322     public static void writeLines(File file, Collection<Object> lines) throws IOException {
1323         writeLines(file, null, lines, null);
1324     }
1325 
1326     /**
1327      * Writes the <code>toString()</code> value of each item in a collection to
1328      * the specified <code>File</code> line by line.
1329      * The specified character encoding and the line ending will be used.
1330      * <p>
1331      * NOTE: As from v1.3, the parent directories of the file will be created
1332      * if they do not exist.
1333      *
1334      * @param file  the file to write to
1335      * @param encoding  the encoding to use, <code>null</code> means platform default
1336      * @param lines  the lines to write, <code>null</code> entries produce blank lines
1337      * @param lineEnding  the line separator to use, <code>null</code> is system default
1338      * @throws IOException in case of an I/O error
1339      * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
1340      * @since Commons IO 1.1
1341      */
writeLines(File file, String encoding, Collection<Object> lines, String lineEnding)1342     public static void writeLines(File file, String encoding, Collection<Object> lines, String lineEnding) throws IOException {
1343         OutputStream out = null;
1344         try {
1345             out = openOutputStream(file);
1346             IOUtils.writeLines(lines, lineEnding, out, encoding);
1347         } finally {
1348             IOUtils.closeQuietly(out);
1349         }
1350     }
1351 
1352     /**
1353      * Writes the <code>toString()</code> value of each item in a collection to
1354      * the specified <code>File</code> line by line.
1355      * The default VM encoding and the specified line ending will be used.
1356      *
1357      * @param file  the file to write to
1358      * @param lines  the lines to write, <code>null</code> entries produce blank lines
1359      * @param lineEnding  the line separator to use, <code>null</code> is system default
1360      * @throws IOException in case of an I/O error
1361      * @since Commons IO 1.3
1362      */
writeLines(File file, Collection<Object> lines, String lineEnding)1363     public static void writeLines(File file, Collection<Object> lines, String lineEnding) throws IOException {
1364         writeLines(file, null, lines, lineEnding);
1365     }
1366 
1367     //-----------------------------------------------------------------------
1368     /**
1369      * Deletes a file. If file is a directory, delete it and all sub-directories.
1370      * <p>
1371      * The difference between File.delete() and this method are:
1372      * <ul>
1373      * <li>A directory to be deleted does not have to be empty.</li>
1374      * <li>You get exceptions when a file or directory cannot be deleted.
1375      *      (java.io.File methods returns a boolean)</li>
1376      * </ul>
1377      *
1378      * @param file  file or directory to delete, must not be <code>null</code>
1379      * @throws NullPointerException if the directory is <code>null</code>
1380      * @throws FileNotFoundException if the file was not found
1381      * @throws IOException in case deletion is unsuccessful
1382      */
forceDelete(File file)1383     public static void forceDelete(File file) throws IOException {
1384         if (file.isDirectory()) {
1385             deleteDirectory(file);
1386         } else {
1387             boolean filePresent = file.exists();
1388             if (!file.delete()) {
1389                 if (!filePresent){
1390                     throw new FileNotFoundException("File does not exist: " + file);
1391                 }
1392                 String message =
1393                     "Unable to delete file: " + file;
1394                 throw new IOException(message);
1395             }
1396         }
1397     }
1398 
1399     /**
1400      * Schedules a file to be deleted when JVM exits.
1401      * If file is directory delete it and all sub-directories.
1402      *
1403      * @param file  file or directory to delete, must not be <code>null</code>
1404      * @throws NullPointerException if the file is <code>null</code>
1405      * @throws IOException in case deletion is unsuccessful
1406      */
forceDeleteOnExit(File file)1407     public static void forceDeleteOnExit(File file) throws IOException {
1408         if (file.isDirectory()) {
1409             deleteDirectoryOnExit(file);
1410         } else {
1411             file.deleteOnExit();
1412         }
1413     }
1414 
1415     /**
1416      * Schedules a directory recursively for deletion on JVM exit.
1417      *
1418      * @param directory  directory to delete, must not be <code>null</code>
1419      * @throws NullPointerException if the directory is <code>null</code>
1420      * @throws IOException in case deletion is unsuccessful
1421      */
deleteDirectoryOnExit(File directory)1422     private static void deleteDirectoryOnExit(File directory) throws IOException {
1423         if (!directory.exists()) {
1424             return;
1425         }
1426 
1427         cleanDirectoryOnExit(directory);
1428         directory.deleteOnExit();
1429     }
1430 
1431     /**
1432      * Cleans a directory without deleting it.
1433      *
1434      * @param directory  directory to clean, must not be <code>null</code>
1435      * @throws NullPointerException if the directory is <code>null</code>
1436      * @throws IOException in case cleaning is unsuccessful
1437      */
cleanDirectoryOnExit(File directory)1438     private static void cleanDirectoryOnExit(File directory) throws IOException {
1439         if (!directory.exists()) {
1440             String message = directory + " does not exist";
1441             throw new IllegalArgumentException(message);
1442         }
1443 
1444         if (!directory.isDirectory()) {
1445             String message = directory + " is not a directory";
1446             throw new IllegalArgumentException(message);
1447         }
1448 
1449         File[] files = directory.listFiles();
1450         if (files == null) {  // null if security restricted
1451             throw new IOException("Failed to list contents of " + directory);
1452         }
1453 
1454         IOException exception = null;
1455         for (int i = 0; i < files.length; i++) {
1456             File file = files[i];
1457             try {
1458                 forceDeleteOnExit(file);
1459             } catch (IOException ioe) {
1460                 exception = ioe;
1461             }
1462         }
1463 
1464         if (null != exception) {
1465             throw exception;
1466         }
1467     }
1468 
1469     /**
1470      * Makes a directory, including any necessary but nonexistent parent
1471      * directories. If there already exists a file with specified name or
1472      * the directory cannot be created then an exception is thrown.
1473      *
1474      * @param directory  directory to create, must not be <code>null</code>
1475      * @throws NullPointerException if the directory is <code>null</code>
1476      * @throws IOException if the directory cannot be created
1477      */
forceMkdir(File directory)1478     public static void forceMkdir(File directory) throws IOException {
1479         if (directory.exists()) {
1480             if (directory.isFile()) {
1481                 String message =
1482                     "File "
1483                         + directory
1484                         + " exists and is "
1485                         + "not a directory. Unable to create directory.";
1486                 throw new IOException(message);
1487             }
1488         } else {
1489             if (!directory.mkdirs()) {
1490                 String message =
1491                     "Unable to create directory " + directory;
1492                 throw new IOException(message);
1493             }
1494         }
1495     }
1496 
1497     //-----------------------------------------------------------------------
1498     /**
1499      * Counts the size of a directory recursively (sum of the length of all files).
1500      *
1501      * @param directory  directory to inspect, must not be <code>null</code>
1502      * @return size of directory in bytes, 0 if directory is security restricted
1503      * @throws NullPointerException if the directory is <code>null</code>
1504      */
sizeOfDirectory(File directory)1505     public static long sizeOfDirectory(File directory) {
1506         if (!directory.exists()) {
1507             String message = directory + " does not exist";
1508             throw new IllegalArgumentException(message);
1509         }
1510 
1511         if (!directory.isDirectory()) {
1512             String message = directory + " is not a directory";
1513             throw new IllegalArgumentException(message);
1514         }
1515 
1516         long size = 0;
1517 
1518         File[] files = directory.listFiles();
1519         if (files == null) {  // null if security restricted
1520             return 0L;
1521         }
1522         for (int i = 0; i < files.length; i++) {
1523             File file = files[i];
1524 
1525             if (file.isDirectory()) {
1526                 size += sizeOfDirectory(file);
1527             } else {
1528                 size += file.length();
1529             }
1530         }
1531 
1532         return size;
1533     }
1534 
1535     //-----------------------------------------------------------------------
1536     /**
1537      * Tests if the specified <code>File</code> is newer than the reference
1538      * <code>File</code>.
1539      *
1540      * @param file  the <code>File</code> of which the modification date must
1541      * be compared, must not be <code>null</code>
1542      * @param reference  the <code>File</code> of which the modification date
1543      * is used, must not be <code>null</code>
1544      * @return true if the <code>File</code> exists and has been modified more
1545      * recently than the reference <code>File</code>
1546      * @throws IllegalArgumentException if the file is <code>null</code>
1547      * @throws IllegalArgumentException if the reference file is <code>null</code> or doesn't exist
1548      */
isFileNewer(File file, File reference)1549      public static boolean isFileNewer(File file, File reference) {
1550         if (reference == null) {
1551             throw new IllegalArgumentException("No specified reference file");
1552         }
1553         if (!reference.exists()) {
1554             throw new IllegalArgumentException("The reference file '"
1555                     + file + "' doesn't exist");
1556         }
1557         return isFileNewer(file, reference.lastModified());
1558     }
1559 
1560     /**
1561      * Tests if the specified <code>File</code> is newer than the specified
1562      * <code>Date</code>.
1563      *
1564      * @param file  the <code>File</code> of which the modification date
1565      * must be compared, must not be <code>null</code>
1566      * @param date  the date reference, must not be <code>null</code>
1567      * @return true if the <code>File</code> exists and has been modified
1568      * after the given <code>Date</code>.
1569      * @throws IllegalArgumentException if the file is <code>null</code>
1570      * @throws IllegalArgumentException if the date is <code>null</code>
1571      */
isFileNewer(File file, Date date)1572     public static boolean isFileNewer(File file, Date date) {
1573         if (date == null) {
1574             throw new IllegalArgumentException("No specified date");
1575         }
1576         return isFileNewer(file, date.getTime());
1577     }
1578 
1579     /**
1580      * Tests if the specified <code>File</code> is newer than the specified
1581      * time reference.
1582      *
1583      * @param file  the <code>File</code> of which the modification date must
1584      * be compared, must not be <code>null</code>
1585      * @param timeMillis  the time reference measured in milliseconds since the
1586      * epoch (00:00:00 GMT, January 1, 1970)
1587      * @return true if the <code>File</code> exists and has been modified after
1588      * the given time reference.
1589      * @throws IllegalArgumentException if the file is <code>null</code>
1590      */
isFileNewer(File file, long timeMillis)1591      public static boolean isFileNewer(File file, long timeMillis) {
1592         if (file == null) {
1593             throw new IllegalArgumentException("No specified file");
1594         }
1595         if (!file.exists()) {
1596             return false;
1597         }
1598         return file.lastModified() > timeMillis;
1599     }
1600 
1601 
1602     //-----------------------------------------------------------------------
1603     /**
1604      * Tests if the specified <code>File</code> is older than the reference
1605      * <code>File</code>.
1606      *
1607      * @param file  the <code>File</code> of which the modification date must
1608      * be compared, must not be <code>null</code>
1609      * @param reference  the <code>File</code> of which the modification date
1610      * is used, must not be <code>null</code>
1611      * @return true if the <code>File</code> exists and has been modified before
1612      * the reference <code>File</code>
1613      * @throws IllegalArgumentException if the file is <code>null</code>
1614      * @throws IllegalArgumentException if the reference file is <code>null</code> or doesn't exist
1615      */
isFileOlder(File file, File reference)1616      public static boolean isFileOlder(File file, File reference) {
1617         if (reference == null) {
1618             throw new IllegalArgumentException("No specified reference file");
1619         }
1620         if (!reference.exists()) {
1621             throw new IllegalArgumentException("The reference file '"
1622                     + file + "' doesn't exist");
1623         }
1624         return isFileOlder(file, reference.lastModified());
1625     }
1626 
1627     /**
1628      * Tests if the specified <code>File</code> is older than the specified
1629      * <code>Date</code>.
1630      *
1631      * @param file  the <code>File</code> of which the modification date
1632      * must be compared, must not be <code>null</code>
1633      * @param date  the date reference, must not be <code>null</code>
1634      * @return true if the <code>File</code> exists and has been modified
1635      * before the given <code>Date</code>.
1636      * @throws IllegalArgumentException if the file is <code>null</code>
1637      * @throws IllegalArgumentException if the date is <code>null</code>
1638      */
isFileOlder(File file, Date date)1639     public static boolean isFileOlder(File file, Date date) {
1640         if (date == null) {
1641             throw new IllegalArgumentException("No specified date");
1642         }
1643         return isFileOlder(file, date.getTime());
1644     }
1645 
1646     /**
1647      * Tests if the specified <code>File</code> is older than the specified
1648      * time reference.
1649      *
1650      * @param file  the <code>File</code> of which the modification date must
1651      * be compared, must not be <code>null</code>
1652      * @param timeMillis  the time reference measured in milliseconds since the
1653      * epoch (00:00:00 GMT, January 1, 1970)
1654      * @return true if the <code>File</code> exists and has been modified before
1655      * the given time reference.
1656      * @throws IllegalArgumentException if the file is <code>null</code>
1657      */
isFileOlder(File file, long timeMillis)1658      public static boolean isFileOlder(File file, long timeMillis) {
1659         if (file == null) {
1660             throw new IllegalArgumentException("No specified file");
1661         }
1662         if (!file.exists()) {
1663             return false;
1664         }
1665         return file.lastModified() < timeMillis;
1666     }
1667 
1668     //-----------------------------------------------------------------------
1669     /**
1670      * Computes the checksum of a file using the CRC32 checksum routine.
1671      * The value of the checksum is returned.
1672      *
1673      * @param file  the file to checksum, must not be <code>null</code>
1674      * @return the checksum value
1675      * @throws NullPointerException if the file or checksum is <code>null</code>
1676      * @throws IllegalArgumentException if the file is a directory
1677      * @throws IOException if an IO error occurs reading the file
1678      * @since Commons IO 1.3
1679      */
checksumCRC32(File file)1680     public static long checksumCRC32(File file) throws IOException {
1681         CRC32 crc = new CRC32();
1682         checksum(file, crc);
1683         return crc.getValue();
1684     }
1685 
1686     /**
1687      * Computes the checksum of a file using the specified checksum object.
1688      * Multiple files may be checked using one <code>Checksum</code> instance
1689      * if desired simply by reusing the same checksum object.
1690      * For example:
1691      * <pre>
1692      *   long csum = FileUtils.checksum(file, new CRC32()).getValue();
1693      * </pre>
1694      *
1695      * @param file  the file to checksum, must not be <code>null</code>
1696      * @param checksum  the checksum object to be used, must not be <code>null</code>
1697      * @return the checksum specified, updated with the content of the file
1698      * @throws NullPointerException if the file or checksum is <code>null</code>
1699      * @throws IllegalArgumentException if the file is a directory
1700      * @throws IOException if an IO error occurs reading the file
1701      * @since Commons IO 1.3
1702      */
checksum(File file, Checksum checksum)1703     public static Checksum checksum(File file, Checksum checksum) throws IOException {
1704         if (file.isDirectory()) {
1705             throw new IllegalArgumentException("Checksums can't be computed on directories");
1706         }
1707         InputStream in = null;
1708         try {
1709             in = new CheckedInputStream(new FileInputStream(file), checksum);
1710             IOUtils.copy(in, new NullOutputStream());
1711         } finally {
1712             IOUtils.closeQuietly(in);
1713         }
1714         return checksum;
1715     }
1716 
1717     /**
1718      * Moves a directory.
1719      * <p>
1720      * When the destination directory is on another file system, do a "copy and delete".
1721      *
1722      * @param srcDir the directory to be moved
1723      * @param destDir the destination directory
1724      * @throws NullPointerException if source or destination is <code>null</code>
1725      * @throws IOException if source or destination is invalid
1726      * @throws IOException if an IO error occurs moving the file
1727      * @since Commons IO 1.4
1728      */
moveDirectory(File srcDir, File destDir)1729     public static void moveDirectory(File srcDir, File destDir) throws IOException {
1730         if (srcDir == null) {
1731             throw new NullPointerException("Source must not be null");
1732         }
1733         if (destDir == null) {
1734             throw new NullPointerException("Destination must not be null");
1735         }
1736         if (!srcDir.exists()) {
1737             throw new FileNotFoundException("Source '" + srcDir + "' does not exist");
1738         }
1739         if (!srcDir.isDirectory()) {
1740             throw new IOException("Source '" + srcDir + "' is not a directory");
1741         }
1742         if (destDir.exists()) {
1743             throw new IOException("Destination '" + destDir + "' already exists");
1744         }
1745         boolean rename = srcDir.renameTo(destDir);
1746         if (!rename) {
1747             copyDirectory( srcDir, destDir );
1748             deleteDirectory( srcDir );
1749             if (srcDir.exists()) {
1750                 throw new IOException("Failed to delete original directory '" + srcDir +
1751                         "' after copy to '" + destDir + "'");
1752             }
1753         }
1754     }
1755 
1756     /**
1757      * Moves a directory to another directory.
1758      *
1759      * @param src the file to be moved
1760      * @param destDir the destination file
1761      * @param createDestDir If <code>true</code> create the destination directory,
1762      * otherwise if <code>false</code> throw an IOException
1763      * @throws NullPointerException if source or destination is <code>null</code>
1764      * @throws IOException if source or destination is invalid
1765      * @throws IOException if an IO error occurs moving the file
1766      * @since Commons IO 1.4
1767      */
moveDirectoryToDirectory(File src, File destDir, boolean createDestDir)1768     public static void moveDirectoryToDirectory(File src, File destDir, boolean createDestDir) throws IOException {
1769         if (src == null) {
1770             throw new NullPointerException("Source must not be null");
1771         }
1772         if (destDir == null) {
1773             throw new NullPointerException("Destination directory must not be null");
1774         }
1775         if (!destDir.exists() && createDestDir) {
1776             destDir.mkdirs();
1777         }
1778         if (!destDir.exists()) {
1779             throw new FileNotFoundException("Destination directory '" + destDir +
1780                     "' does not exist [createDestDir=" + createDestDir +"]");
1781         }
1782         if (!destDir.isDirectory()) {
1783             throw new IOException("Destination '" + destDir + "' is not a directory");
1784         }
1785         moveDirectory(src, new File(destDir, src.getName()));
1786 
1787     }
1788 
1789     /**
1790      * Moves a file.
1791      * <p>
1792      * When the destination file is on another file system, do a "copy and delete".
1793      *
1794      * @param srcFile the file to be moved
1795      * @param destFile the destination file
1796      * @throws NullPointerException if source or destination is <code>null</code>
1797      * @throws IOException if source or destination is invalid
1798      * @throws IOException if an IO error occurs moving the file
1799      * @since Commons IO 1.4
1800      */
moveFile(File srcFile, File destFile)1801     public static void moveFile(File srcFile, File destFile) throws IOException {
1802         if (srcFile == null) {
1803             throw new NullPointerException("Source must not be null");
1804         }
1805         if (destFile == null) {
1806             throw new NullPointerException("Destination must not be null");
1807         }
1808         if (!srcFile.exists()) {
1809             throw new FileNotFoundException("Source '" + srcFile + "' does not exist");
1810         }
1811         if (srcFile.isDirectory()) {
1812             throw new IOException("Source '" + srcFile + "' is a directory");
1813         }
1814         if (destFile.exists()) {
1815             throw new IOException("Destination '" + destFile + "' already exists");
1816         }
1817         if (destFile.isDirectory()) {
1818             throw new IOException("Destination '" + destFile + "' is a directory");
1819         }
1820         boolean rename = srcFile.renameTo(destFile);
1821         if (!rename) {
1822             copyFile( srcFile, destFile );
1823             if (!srcFile.delete()) {
1824                 FileUtils.deleteQuietly(destFile);
1825                 throw new IOException("Failed to delete original file '" + srcFile +
1826                         "' after copy to '" + destFile + "'");
1827             }
1828         }
1829     }
1830 
1831     /**
1832      * Moves a file to a directory.
1833      *
1834      * @param srcFile the file to be moved
1835      * @param destDir the destination file
1836      * @param createDestDir If <code>true</code> create the destination directory,
1837      * otherwise if <code>false</code> throw an IOException
1838      * @throws NullPointerException if source or destination is <code>null</code>
1839      * @throws IOException if source or destination is invalid
1840      * @throws IOException if an IO error occurs moving the file
1841      * @since Commons IO 1.4
1842      */
moveFileToDirectory(File srcFile, File destDir, boolean createDestDir)1843     public static void moveFileToDirectory(File srcFile, File destDir, boolean createDestDir) throws IOException {
1844         if (srcFile == null) {
1845             throw new NullPointerException("Source must not be null");
1846         }
1847         if (destDir == null) {
1848             throw new NullPointerException("Destination directory must not be null");
1849         }
1850         if (!destDir.exists() && createDestDir) {
1851             destDir.mkdirs();
1852         }
1853         if (!destDir.exists()) {
1854             throw new FileNotFoundException("Destination directory '" + destDir +
1855                     "' does not exist [createDestDir=" + createDestDir +"]");
1856         }
1857         if (!destDir.isDirectory()) {
1858             throw new IOException("Destination '" + destDir + "' is not a directory");
1859         }
1860         moveFile(srcFile, new File(destDir, srcFile.getName()));
1861     }
1862 
1863     /**
1864      * Moves a file or directory to the destination directory.
1865      * <p>
1866      * When the destination is on another file system, do a "copy and delete".
1867      *
1868      * @param src the file or directory to be moved
1869      * @param destDir the destination directory
1870      * @param createDestDir If <code>true</code> create the destination directory,
1871      * otherwise if <code>false</code> throw an IOException
1872      * @throws NullPointerException if source or destination is <code>null</code>
1873      * @throws IOException if source or destination is invalid
1874      * @throws IOException if an IO error occurs moving the file
1875      * @since Commons IO 1.4
1876      */
moveToDirectory(File src, File destDir, boolean createDestDir)1877     public static void moveToDirectory(File src, File destDir, boolean createDestDir) throws IOException {
1878         if (src == null) {
1879             throw new NullPointerException("Source must not be null");
1880         }
1881         if (destDir == null) {
1882             throw new NullPointerException("Destination must not be null");
1883         }
1884         if (!src.exists()) {
1885             throw new FileNotFoundException("Source '" + src + "' does not exist");
1886         }
1887         if (src.isDirectory()) {
1888             moveDirectoryToDirectory(src, destDir, createDestDir);
1889         } else {
1890             moveFileToDirectory(src, destDir, createDestDir);
1891         }
1892     }
1893 
1894 }
1895