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