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