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