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