0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017: package org.apache.commons.io;
0018:
0019: import java.io.File;
0020: import java.io.FileFilter;
0021: import java.io.FileInputStream;
0022: import java.io.FileNotFoundException;
0023: import java.io.FileOutputStream;
0024: import java.io.IOException;
0025: import java.io.InputStream;
0026: import java.io.OutputStream;
0027: import java.net.URL;
0028: import java.util.Collection;
0029: import java.util.Date;
0030: import java.util.Iterator;
0031: import java.util.List;
0032: import java.util.zip.CRC32;
0033: import java.util.zip.CheckedInputStream;
0034: import java.util.zip.Checksum;
0035:
0036: import org.apache.commons.io.filefilter.DirectoryFileFilter;
0037: import org.apache.commons.io.filefilter.FalseFileFilter;
0038: import org.apache.commons.io.filefilter.FileFilterUtils;
0039: import org.apache.commons.io.filefilter.IOFileFilter;
0040: import org.apache.commons.io.filefilter.SuffixFileFilter;
0041: import org.apache.commons.io.filefilter.TrueFileFilter;
0042: import org.apache.commons.io.output.NullOutputStream;
0043:
0044: /**
0045: * General file manipulation utilities.
0046: * <p>
0047: * Facilities are provided in the following areas:
0048: * <ul>
0049: * <li>writing to a file
0050: * <li>reading from a file
0051: * <li>make a directory including parent directories
0052: * <li>copying files and directories
0053: * <li>deleting files and directories
0054: * <li>converting to and from a URL
0055: * <li>listing files and directories by filter and extension
0056: * <li>comparing file content
0057: * <li>file last changed date
0058: * <li>calculating a checksum
0059: * </ul>
0060: * <p>
0061: * Origin of code: Excalibur, Alexandria, Commons-Utils
0062: *
0063: * @author <a href="mailto:burton@relativity.yi.org">Kevin A. Burton</A>
0064: * @author <a href="mailto:sanders@apache.org">Scott Sanders</a>
0065: * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
0066: * @author <a href="mailto:Christoph.Reck@dlr.de">Christoph.Reck</a>
0067: * @author <a href="mailto:peter@apache.org">Peter Donald</a>
0068: * @author <a href="mailto:jefft@apache.org">Jeff Turner</a>
0069: * @author Matthew Hawthorne
0070: * @author <a href="mailto:jeremias@apache.org">Jeremias Maerki</a>
0071: * @author Stephen Colebourne
0072: * @author Ian Springer
0073: * @author Chris Eldredge
0074: * @author Jim Harrington
0075: * @author Niall Pemberton
0076: * @author Sandy McArthur
0077: * @version $Id: FileUtils.java 507684 2007-02-14 20:38:25Z bayard $
0078: */
0079: public class FileUtils {
0080:
0081: /**
0082: * Instances should NOT be constructed in standard programming.
0083: */
0084: public FileUtils() {
0085: super ();
0086: }
0087:
0088: /**
0089: * The number of bytes in a kilobyte.
0090: */
0091: public static final long ONE_KB = 1024;
0092:
0093: /**
0094: * The number of bytes in a megabyte.
0095: */
0096: public static final long ONE_MB = ONE_KB * ONE_KB;
0097:
0098: /**
0099: * The number of bytes in a gigabyte.
0100: */
0101: public static final long ONE_GB = ONE_KB * ONE_MB;
0102:
0103: /**
0104: * An empty array of type <code>File</code>.
0105: */
0106: public static final File[] EMPTY_FILE_ARRAY = new File[0];
0107:
0108: //-----------------------------------------------------------------------
0109: /**
0110: * Opens a {@link FileInputStream} for the specified file, providing better
0111: * error messages than simply calling <code>new FileInputStream(file)</code>.
0112: * <p>
0113: * At the end of the method either the stream will be successfully opened,
0114: * or an exception will have been thrown.
0115: * <p>
0116: * An exception is thrown if the file does not exist.
0117: * An exception is thrown if the file object exists but is a directory.
0118: * An exception is thrown if the file exists but cannot be read.
0119: *
0120: * @param file the file to open for input, must not be <code>null</code>
0121: * @return a new {@link FileInputStream} for the specified file
0122: * @throws FileNotFoundException if the file does not exist
0123: * @throws IOException if the file object is a directory
0124: * @throws IOException if the file cannot be read
0125: * @since Commons IO 1.3
0126: */
0127: public static FileInputStream openInputStream(File file)
0128: throws IOException {
0129: if (file.exists()) {
0130: if (file.isDirectory()) {
0131: throw new IOException("File '" + file
0132: + "' exists but is a directory");
0133: }
0134: if (file.canRead() == false) {
0135: throw new IOException("File '" + file
0136: + "' cannot be read");
0137: }
0138: } else {
0139: throw new FileNotFoundException("File '" + file
0140: + "' does not exist");
0141: }
0142: return new FileInputStream(file);
0143: }
0144:
0145: //-----------------------------------------------------------------------
0146: /**
0147: * Opens a {@link FileOutputStream} for the specified file, checking and
0148: * creating the parent directory if it does not exist.
0149: * <p>
0150: * At the end of the method either the stream will be successfully opened,
0151: * or an exception will have been thrown.
0152: * <p>
0153: * The parent directory will be created if it does not exist.
0154: * The file will be created if it does not exist.
0155: * An exception is thrown if the file object exists but is a directory.
0156: * An exception is thrown if the file exists but cannot be written to.
0157: * An exception is thrown if the parent directory cannot be created.
0158: *
0159: * @param file the file to open for output, must not be <code>null</code>
0160: * @return a new {@link FileOutputStream} for the specified file
0161: * @throws IOException if the file object is a directory
0162: * @throws IOException if the file cannot be written to
0163: * @throws IOException if a parent directory needs creating but that fails
0164: * @since Commons IO 1.3
0165: */
0166: public static FileOutputStream openOutputStream(File file)
0167: throws IOException {
0168: if (file.exists()) {
0169: if (file.isDirectory()) {
0170: throw new IOException("File '" + file
0171: + "' exists but is a directory");
0172: }
0173: if (file.canWrite() == false) {
0174: throw new IOException("File '" + file
0175: + "' cannot be written to");
0176: }
0177: } else {
0178: File parent = file.getParentFile();
0179: if (parent != null && parent.exists() == false) {
0180: if (parent.mkdirs() == false) {
0181: throw new IOException("File '" + file
0182: + "' could not be created");
0183: }
0184: }
0185: }
0186: return new FileOutputStream(file);
0187: }
0188:
0189: //-----------------------------------------------------------------------
0190: /**
0191: * Returns a human-readable version of the file size, where the input
0192: * represents a specific number of bytes.
0193: *
0194: * @param size the number of bytes
0195: * @return a human-readable display value (includes units)
0196: */
0197: public static String byteCountToDisplaySize(long size) {
0198: String displaySize;
0199:
0200: if (size / ONE_GB > 0) {
0201: displaySize = String.valueOf(size / ONE_GB) + " GB";
0202: } else if (size / ONE_MB > 0) {
0203: displaySize = String.valueOf(size / ONE_MB) + " MB";
0204: } else if (size / ONE_KB > 0) {
0205: displaySize = String.valueOf(size / ONE_KB) + " KB";
0206: } else {
0207: displaySize = String.valueOf(size) + " bytes";
0208: }
0209: return displaySize;
0210: }
0211:
0212: //-----------------------------------------------------------------------
0213: /**
0214: * Implements the same behaviour as the "touch" utility on Unix. It creates
0215: * a new file with size 0 or, if the file exists already, it is opened and
0216: * closed without modifying it, but updating the file date and time.
0217: * <p>
0218: * NOTE: As from v1.3, this method throws an IOException if the last
0219: * modified date of the file cannot be set. Also, as from v1.3 this method
0220: * creates parent directories if they do not exist.
0221: *
0222: * @param file the File to touch
0223: * @throws IOException If an I/O problem occurs
0224: */
0225: public static void touch(File file) throws IOException {
0226: if (!file.exists()) {
0227: OutputStream out = openOutputStream(file);
0228: IOUtils.closeQuietly(out);
0229: }
0230: boolean success = file.setLastModified(System
0231: .currentTimeMillis());
0232: if (!success) {
0233: throw new IOException(
0234: "Unable to set the last modification time for "
0235: + file);
0236: }
0237: }
0238:
0239: //-----------------------------------------------------------------------
0240: /**
0241: * Converts a Collection containing java.io.File instanced into array
0242: * representation. This is to account for the difference between
0243: * File.listFiles() and FileUtils.listFiles().
0244: *
0245: * @param files a Collection containing java.io.File instances
0246: * @return an array of java.io.File
0247: */
0248: public static File[] convertFileCollectionToFileArray(
0249: Collection files) {
0250: return (File[]) files.toArray(new File[files.size()]);
0251: }
0252:
0253: //-----------------------------------------------------------------------
0254: /**
0255: * Finds files within a given directory (and optionally its
0256: * subdirectories). All files found are filtered by an IOFileFilter.
0257: *
0258: * @param files the collection of files found.
0259: * @param directory the directory to search in.
0260: * @param filter the filter to apply to files and directories.
0261: */
0262: private static void innerListFiles(Collection files,
0263: File directory, IOFileFilter filter) {
0264: File[] found = directory.listFiles((FileFilter) filter);
0265: if (found != null) {
0266: for (int i = 0; i < found.length; i++) {
0267: if (found[i].isDirectory()) {
0268: innerListFiles(files, found[i], filter);
0269: } else {
0270: files.add(found[i]);
0271: }
0272: }
0273: }
0274: }
0275:
0276: /**
0277: * Finds files within a given directory (and optionally its
0278: * subdirectories). All files found are filtered by an IOFileFilter.
0279: * <p>
0280: * If your search should recurse into subdirectories you can pass in
0281: * an IOFileFilter for directories. You don't need to bind a
0282: * DirectoryFileFilter (via logical AND) to this filter. This method does
0283: * that for you.
0284: * <p>
0285: * An example: If you want to search through all directories called
0286: * "temp" you pass in <code>FileFilterUtils.NameFileFilter("temp")</code>
0287: * <p>
0288: * Another common usage of this method is find files in a directory
0289: * tree but ignoring the directories generated CVS. You can simply pass
0290: * in <code>FileFilterUtils.makeCVSAware(null)</code>.
0291: *
0292: * @param directory the directory to search in
0293: * @param fileFilter filter to apply when finding files.
0294: * @param dirFilter optional filter to apply when finding subdirectories.
0295: * If this parameter is <code>null</code>, subdirectories will not be included in the
0296: * search. Use TrueFileFilter.INSTANCE to match all directories.
0297: * @return an collection of java.io.File with the matching files
0298: * @see org.apache.commons.io.filefilter.FileFilterUtils
0299: * @see org.apache.commons.io.filefilter.NameFileFilter
0300: */
0301: public static Collection listFiles(File directory,
0302: IOFileFilter fileFilter, IOFileFilter dirFilter) {
0303: if (!directory.isDirectory()) {
0304: throw new IllegalArgumentException(
0305: "Parameter 'directory' is not a directory");
0306: }
0307: if (fileFilter == null) {
0308: throw new NullPointerException(
0309: "Parameter 'fileFilter' is null");
0310: }
0311:
0312: //Setup effective file filter
0313: IOFileFilter effFileFilter = FileFilterUtils.andFileFilter(
0314: fileFilter, FileFilterUtils
0315: .notFileFilter(DirectoryFileFilter.INSTANCE));
0316:
0317: //Setup effective directory filter
0318: IOFileFilter effDirFilter;
0319: if (dirFilter == null) {
0320: effDirFilter = FalseFileFilter.INSTANCE;
0321: } else {
0322: effDirFilter = FileFilterUtils.andFileFilter(dirFilter,
0323: DirectoryFileFilter.INSTANCE);
0324: }
0325:
0326: //Find files
0327: Collection files = new java.util.LinkedList();
0328: innerListFiles(files, directory, FileFilterUtils.orFileFilter(
0329: effFileFilter, effDirFilter));
0330: return files;
0331: }
0332:
0333: /**
0334: * Allows iteration over the files in given directory (and optionally
0335: * its subdirectories).
0336: * <p>
0337: * All files found are filtered by an IOFileFilter. This method is
0338: * based on {@link #listFiles(File, IOFileFilter, IOFileFilter)}.
0339: *
0340: * @param directory the directory to search in
0341: * @param fileFilter filter to apply when finding files.
0342: * @param dirFilter optional filter to apply when finding subdirectories.
0343: * If this parameter is <code>null</code>, subdirectories will not be included in the
0344: * search. Use TrueFileFilter.INSTANCE to match all directories.
0345: * @return an iterator of java.io.File for the matching files
0346: * @see org.apache.commons.io.filefilter.FileFilterUtils
0347: * @see org.apache.commons.io.filefilter.NameFileFilter
0348: * @since Commons IO 1.2
0349: */
0350: public static Iterator iterateFiles(File directory,
0351: IOFileFilter fileFilter, IOFileFilter dirFilter) {
0352: return listFiles(directory, fileFilter, dirFilter).iterator();
0353: }
0354:
0355: //-----------------------------------------------------------------------
0356: /**
0357: * Converts an array of file extensions to suffixes for use
0358: * with IOFileFilters.
0359: *
0360: * @param extensions an array of extensions. Format: {"java", "xml"}
0361: * @return an array of suffixes. Format: {".java", ".xml"}
0362: */
0363: private static String[] toSuffixes(String[] extensions) {
0364: String[] suffixes = new String[extensions.length];
0365: for (int i = 0; i < extensions.length; i++) {
0366: suffixes[i] = "." + extensions[i];
0367: }
0368: return suffixes;
0369: }
0370:
0371: /**
0372: * Finds files within a given directory (and optionally its subdirectories)
0373: * which match an array of extensions.
0374: *
0375: * @param directory the directory to search in
0376: * @param extensions an array of extensions, ex. {"java","xml"}. If this
0377: * parameter is <code>null</code>, all files are returned.
0378: * @param recursive if true all subdirectories are searched as well
0379: * @return an collection of java.io.File with the matching files
0380: */
0381: public static Collection listFiles(File directory,
0382: String[] extensions, boolean recursive) {
0383: IOFileFilter filter;
0384: if (extensions == null) {
0385: filter = TrueFileFilter.INSTANCE;
0386: } else {
0387: String[] suffixes = toSuffixes(extensions);
0388: filter = new SuffixFileFilter(suffixes);
0389: }
0390: return listFiles(directory, filter,
0391: (recursive ? TrueFileFilter.INSTANCE
0392: : FalseFileFilter.INSTANCE));
0393: }
0394:
0395: /**
0396: * Allows iteration over the files in a given directory (and optionally
0397: * its subdirectories) which match an array of extensions. This method
0398: * is based on {@link #listFiles(File, String[], boolean)}.
0399: *
0400: * @param directory the directory to search in
0401: * @param extensions an array of extensions, ex. {"java","xml"}. If this
0402: * parameter is <code>null</code>, all files are returned.
0403: * @param recursive if true all subdirectories are searched as well
0404: * @return an iterator of java.io.File with the matching files
0405: * @since Commons IO 1.2
0406: */
0407: public static Iterator iterateFiles(File directory,
0408: String[] extensions, boolean recursive) {
0409: return listFiles(directory, extensions, recursive).iterator();
0410: }
0411:
0412: //-----------------------------------------------------------------------
0413: /**
0414: * Compare the contents of two files to determine if they are equal or not.
0415: * <p>
0416: * This method checks to see if the two files are different lengths
0417: * or if they point to the same file, before resorting to byte-by-byte
0418: * comparison of the contents.
0419: * <p>
0420: * Code origin: Avalon
0421: *
0422: * @param file1 the first file
0423: * @param file2 the second file
0424: * @return true if the content of the files are equal or they both don't
0425: * exist, false otherwise
0426: * @throws IOException in case of an I/O error
0427: */
0428: public static boolean contentEquals(File file1, File file2)
0429: throws IOException {
0430: boolean file1Exists = file1.exists();
0431: if (file1Exists != file2.exists()) {
0432: return false;
0433: }
0434:
0435: if (!file1Exists) {
0436: // two not existing files are equal
0437: return true;
0438: }
0439:
0440: if (file1.isDirectory() || file2.isDirectory()) {
0441: // don't want to compare directory contents
0442: throw new IOException(
0443: "Can't compare directories, only files");
0444: }
0445:
0446: if (file1.length() != file2.length()) {
0447: // lengths differ, cannot be equal
0448: return false;
0449: }
0450:
0451: if (file1.getCanonicalFile().equals(file2.getCanonicalFile())) {
0452: // same file
0453: return true;
0454: }
0455:
0456: InputStream input1 = null;
0457: InputStream input2 = null;
0458: try {
0459: input1 = new FileInputStream(file1);
0460: input2 = new FileInputStream(file2);
0461: return IOUtils.contentEquals(input1, input2);
0462:
0463: } finally {
0464: IOUtils.closeQuietly(input1);
0465: IOUtils.closeQuietly(input2);
0466: }
0467: }
0468:
0469: //-----------------------------------------------------------------------
0470: /**
0471: * Convert from a <code>URL</code> to a <code>File</code>.
0472: * <p>
0473: * From version 1.1 this method will decode the URL.
0474: * Syntax such as <code>file:///my%20docs/file.txt</code> will be
0475: * correctly decoded to <code>/my docs/file.txt</code>.
0476: *
0477: * @param url the file URL to convert, <code>null</code> returns <code>null</code>
0478: * @return the equivalent <code>File</code> object, or <code>null</code>
0479: * if the URL's protocol is not <code>file</code>
0480: * @throws IllegalArgumentException if the file is incorrectly encoded
0481: */
0482: public static File toFile(URL url) {
0483: if (url == null || !url.getProtocol().equals("file")) {
0484: return null;
0485: } else {
0486: String filename = url.getFile().replace('/',
0487: File.separatorChar);
0488: int pos = 0;
0489: while ((pos = filename.indexOf('%', pos)) >= 0) {
0490: if (pos + 2 < filename.length()) {
0491: String hexStr = filename
0492: .substring(pos + 1, pos + 3);
0493: char ch = (char) Integer.parseInt(hexStr, 16);
0494: filename = filename.substring(0, pos) + ch
0495: + filename.substring(pos + 3);
0496: }
0497: }
0498: return new File(filename);
0499: }
0500: }
0501:
0502: /**
0503: * Converts each of an array of <code>URL</code> to a <code>File</code>.
0504: * <p>
0505: * Returns an array of the same size as the input.
0506: * If the input is <code>null</code>, an empty array is returned.
0507: * If the input contains <code>null</code>, the output array contains <code>null</code> at the same
0508: * index.
0509: * <p>
0510: * This method will decode the URL.
0511: * Syntax such as <code>file:///my%20docs/file.txt</code> will be
0512: * correctly decoded to <code>/my docs/file.txt</code>.
0513: *
0514: * @param urls the file URLs to convert, <code>null</code> returns empty array
0515: * @return a non-<code>null</code> array of Files matching the input, with a <code>null</code> item
0516: * if there was a <code>null</code> at that index in the input array
0517: * @throws IllegalArgumentException if any file is not a URL file
0518: * @throws IllegalArgumentException if any file is incorrectly encoded
0519: * @since Commons IO 1.1
0520: */
0521: public static File[] toFiles(URL[] urls) {
0522: if (urls == null || urls.length == 0) {
0523: return EMPTY_FILE_ARRAY;
0524: }
0525: File[] files = new File[urls.length];
0526: for (int i = 0; i < urls.length; i++) {
0527: URL url = urls[i];
0528: if (url != null) {
0529: if (url.getProtocol().equals("file") == false) {
0530: throw new IllegalArgumentException(
0531: "URL could not be converted to a File: "
0532: + url);
0533: }
0534: files[i] = toFile(url);
0535: }
0536: }
0537: return files;
0538: }
0539:
0540: /**
0541: * Converts each of an array of <code>File</code> to a <code>URL</code>.
0542: * <p>
0543: * Returns an array of the same size as the input.
0544: *
0545: * @param files the files to convert
0546: * @return an array of URLs matching the input
0547: * @throws IOException if a file cannot be converted
0548: */
0549: public static URL[] toURLs(File[] files) throws IOException {
0550: URL[] urls = new URL[files.length];
0551:
0552: for (int i = 0; i < urls.length; i++) {
0553: urls[i] = files[i].toURL();
0554: }
0555:
0556: return urls;
0557: }
0558:
0559: //-----------------------------------------------------------------------
0560: /**
0561: * Copies a file to a directory preserving the file date.
0562: * <p>
0563: * This method copies the contents of the specified source file
0564: * to a file of the same name in the specified destination directory.
0565: * The destination directory is created if it does not exist.
0566: * If the destination file exists, then this method will overwrite it.
0567: *
0568: * @param srcFile an existing file to copy, must not be <code>null</code>
0569: * @param destDir the directory to place the copy in, must not be <code>null</code>
0570: *
0571: * @throws NullPointerException if source or destination is null
0572: * @throws IOException if source or destination is invalid
0573: * @throws IOException if an IO error occurs during copying
0574: * @see #copyFile(File, File, boolean)
0575: */
0576: public static void copyFileToDirectory(File srcFile, File destDir)
0577: throws IOException {
0578: copyFileToDirectory(srcFile, destDir, true);
0579: }
0580:
0581: /**
0582: * Copies a file to a directory optionally preserving the file date.
0583: * <p>
0584: * This method copies the contents of the specified source file
0585: * to a file of the same name in the specified destination directory.
0586: * The destination directory is created if it does not exist.
0587: * If the destination file exists, then this method will overwrite it.
0588: *
0589: * @param srcFile an existing file to copy, must not be <code>null</code>
0590: * @param destDir the directory to place the copy in, must not be <code>null</code>
0591: * @param preserveFileDate true if the file date of the copy
0592: * should be the same as the original
0593: *
0594: * @throws NullPointerException if source or destination is <code>null</code>
0595: * @throws IOException if source or destination is invalid
0596: * @throws IOException if an IO error occurs during copying
0597: * @see #copyFile(File, File, boolean)
0598: * @since Commons IO 1.3
0599: */
0600: public static void copyFileToDirectory(File srcFile, File destDir,
0601: boolean preserveFileDate) throws IOException {
0602: if (destDir == null) {
0603: throw new NullPointerException(
0604: "Destination must not be null");
0605: }
0606: if (destDir.exists() && destDir.isDirectory() == false) {
0607: throw new IllegalArgumentException("Destination '"
0608: + destDir + "' is not a directory");
0609: }
0610: copyFile(srcFile, new File(destDir, srcFile.getName()),
0611: preserveFileDate);
0612: }
0613:
0614: /**
0615: * Copies a file to a new location preserving the file date.
0616: * <p>
0617: * This method copies the contents of the specified source file to the
0618: * specified destination file. The directory holding the destination file is
0619: * created if it does not exist. If the destination file exists, then this
0620: * method will overwrite it.
0621: *
0622: * @param srcFile an existing file to copy, must not be <code>null</code>
0623: * @param destFile the new file, must not be <code>null</code>
0624: *
0625: * @throws NullPointerException if source or destination is <code>null</code>
0626: * @throws IOException if source or destination is invalid
0627: * @throws IOException if an IO error occurs during copying
0628: * @see #copyFileToDirectory(File, File)
0629: */
0630: public static void copyFile(File srcFile, File destFile)
0631: throws IOException {
0632: copyFile(srcFile, destFile, true);
0633: }
0634:
0635: /**
0636: * Copies a file to a new location.
0637: * <p>
0638: * This method copies the contents of the specified source file
0639: * to the specified destination file.
0640: * The directory holding the destination file is created if it does not exist.
0641: * If the destination file exists, then this method will overwrite it.
0642: *
0643: * @param srcFile an existing file to copy, must not be <code>null</code>
0644: * @param destFile the new file, must not be <code>null</code>
0645: * @param preserveFileDate true if the file date of the copy
0646: * should be the same as the original
0647: *
0648: * @throws NullPointerException if source or destination is <code>null</code>
0649: * @throws IOException if source or destination is invalid
0650: * @throws IOException if an IO error occurs during copying
0651: * @see #copyFileToDirectory(File, File, boolean)
0652: */
0653: public static void copyFile(File srcFile, File destFile,
0654: boolean preserveFileDate) throws IOException {
0655: if (srcFile == null) {
0656: throw new NullPointerException("Source must not be null");
0657: }
0658: if (destFile == null) {
0659: throw new NullPointerException(
0660: "Destination must not be null");
0661: }
0662: if (srcFile.exists() == false) {
0663: throw new FileNotFoundException("Source '" + srcFile
0664: + "' does not exist");
0665: }
0666: if (srcFile.isDirectory()) {
0667: throw new IOException("Source '" + srcFile
0668: + "' exists but is a directory");
0669: }
0670: if (srcFile.getCanonicalPath().equals(
0671: destFile.getCanonicalPath())) {
0672: throw new IOException("Source '" + srcFile
0673: + "' and destination '" + destFile
0674: + "' are the same");
0675: }
0676: if (destFile.getParentFile() != null
0677: && destFile.getParentFile().exists() == false) {
0678: if (destFile.getParentFile().mkdirs() == false) {
0679: throw new IOException("Destination '" + destFile
0680: + "' directory cannot be created");
0681: }
0682: }
0683: if (destFile.exists() && destFile.canWrite() == false) {
0684: throw new IOException("Destination '" + destFile
0685: + "' exists but is read-only");
0686: }
0687: doCopyFile(srcFile, destFile, preserveFileDate);
0688: }
0689:
0690: /**
0691: * Internal copy file method.
0692: *
0693: * @param srcFile the validated source file, must not be <code>null</code>
0694: * @param destFile the validated destination file, must not be <code>null</code>
0695: * @param preserveFileDate whether to preserve the file date
0696: * @throws IOException if an error occurs
0697: */
0698: private static void doCopyFile(File srcFile, File destFile,
0699: boolean preserveFileDate) throws IOException {
0700: if (destFile.exists() && destFile.isDirectory()) {
0701: throw new IOException("Destination '" + destFile
0702: + "' exists but is a directory");
0703: }
0704:
0705: FileInputStream input = new FileInputStream(srcFile);
0706: try {
0707: FileOutputStream output = new FileOutputStream(destFile);
0708: try {
0709: IOUtils.copy(input, output);
0710: } finally {
0711: IOUtils.closeQuietly(output);
0712: }
0713: } finally {
0714: IOUtils.closeQuietly(input);
0715: }
0716:
0717: if (srcFile.length() != destFile.length()) {
0718: throw new IOException("Failed to copy full contents from '"
0719: + srcFile + "' to '" + destFile + "'");
0720: }
0721: if (preserveFileDate) {
0722: destFile.setLastModified(srcFile.lastModified());
0723: }
0724: }
0725:
0726: //-----------------------------------------------------------------------
0727: /**
0728: * Copies a directory to within another directory preserving the file dates.
0729: * <p>
0730: * This method copies the source directory and all its contents to a
0731: * directory of the same name in the specified destination directory.
0732: * <p>
0733: * The destination directory is created if it does not exist.
0734: * If the destination directory did exist, then this method merges
0735: * the source with the destination, with the source taking precedence.
0736: *
0737: * @param srcDir an existing directory to copy, must not be <code>null</code>
0738: * @param destDir the directory to place the copy in, must not be <code>null</code>
0739: *
0740: * @throws NullPointerException if source or destination is <code>null</code>
0741: * @throws IOException if source or destination is invalid
0742: * @throws IOException if an IO error occurs during copying
0743: * @since Commons IO 1.2
0744: */
0745: public static void copyDirectoryToDirectory(File srcDir,
0746: File destDir) throws IOException {
0747: if (srcDir == null) {
0748: throw new NullPointerException("Source must not be null");
0749: }
0750: if (srcDir.exists() && srcDir.isDirectory() == false) {
0751: throw new IllegalArgumentException("Source '" + destDir
0752: + "' is not a directory");
0753: }
0754: if (destDir == null) {
0755: throw new NullPointerException(
0756: "Destination must not be null");
0757: }
0758: if (destDir.exists() && destDir.isDirectory() == false) {
0759: throw new IllegalArgumentException("Destination '"
0760: + destDir + "' is not a directory");
0761: }
0762: copyDirectory(srcDir, new File(destDir, srcDir.getName()), true);
0763: }
0764:
0765: /**
0766: * Copies a whole directory to a new location preserving the file dates.
0767: * <p>
0768: * This method copies the specified directory and all its child
0769: * directories and files to the specified destination.
0770: * The destination is the new location and name of the directory.
0771: * <p>
0772: * The destination directory is created if it does not exist.
0773: * If the destination directory did exist, then this method merges
0774: * the source with the destination, with the source taking precedence.
0775: *
0776: * @param srcDir an existing directory to copy, must not be <code>null</code>
0777: * @param destDir the new directory, must not be <code>null</code>
0778: *
0779: * @throws NullPointerException if source or destination is <code>null</code>
0780: * @throws IOException if source or destination is invalid
0781: * @throws IOException if an IO error occurs during copying
0782: * @since Commons IO 1.1
0783: */
0784: public static void copyDirectory(File srcDir, File destDir)
0785: throws IOException {
0786: copyDirectory(srcDir, destDir, true);
0787: }
0788:
0789: /**
0790: * Copies a whole directory to a new location.
0791: * <p>
0792: * This method copies the contents of the specified source directory
0793: * to within the specified destination directory.
0794: * <p>
0795: * The destination directory is created if it does not exist.
0796: * If the destination directory did exist, then this method merges
0797: * the source with the destination, with the source taking precedence.
0798: *
0799: * @param srcDir an existing directory to copy, must not be <code>null</code>
0800: * @param destDir the new directory, must not be <code>null</code>
0801: * @param preserveFileDate true if the file date of the copy
0802: * should be the same as the original
0803: *
0804: * @throws NullPointerException if source or destination is <code>null</code>
0805: * @throws IOException if source or destination is invalid
0806: * @throws IOException if an IO error occurs during copying
0807: * @since Commons IO 1.1
0808: */
0809: public static void copyDirectory(File srcDir, File destDir,
0810: boolean preserveFileDate) throws IOException {
0811: if (srcDir == null) {
0812: throw new NullPointerException("Source must not be null");
0813: }
0814: if (destDir == null) {
0815: throw new NullPointerException(
0816: "Destination must not be null");
0817: }
0818: if (srcDir.exists() == false) {
0819: throw new FileNotFoundException("Source '" + srcDir
0820: + "' does not exist");
0821: }
0822: if (srcDir.isDirectory() == false) {
0823: throw new IOException("Source '" + srcDir
0824: + "' exists but is not a directory");
0825: }
0826: if (srcDir.getCanonicalPath()
0827: .equals(destDir.getCanonicalPath())) {
0828: throw new IOException("Source '" + srcDir
0829: + "' and destination '" + destDir
0830: + "' are the same");
0831: }
0832: doCopyDirectory(srcDir, destDir, preserveFileDate);
0833: }
0834:
0835: /**
0836: * Internal copy directory method.
0837: *
0838: * @param srcDir the validated source directory, must not be <code>null</code>
0839: * @param destDir the validated destination directory, must not be <code>null</code>
0840: * @param preserveFileDate whether to preserve the file date
0841: * @throws IOException if an error occurs
0842: * @since Commons IO 1.1
0843: */
0844: private static void doCopyDirectory(File srcDir, File destDir,
0845: boolean preserveFileDate) throws IOException {
0846: if (destDir.exists()) {
0847: if (destDir.isDirectory() == false) {
0848: throw new IOException("Destination '" + destDir
0849: + "' exists but is not a directory");
0850: }
0851: } else {
0852: if (destDir.mkdirs() == false) {
0853: throw new IOException("Destination '" + destDir
0854: + "' directory cannot be created");
0855: }
0856: if (preserveFileDate) {
0857: destDir.setLastModified(srcDir.lastModified());
0858: }
0859: }
0860: if (destDir.canWrite() == false) {
0861: throw new IOException("Destination '" + destDir
0862: + "' cannot be written to");
0863: }
0864: // recurse
0865: File[] files = srcDir.listFiles();
0866: if (files == null) { // null if security restricted
0867: throw new IOException("Failed to list contents of "
0868: + srcDir);
0869: }
0870: for (int i = 0; i < files.length; i++) {
0871: File copiedFile = new File(destDir, files[i].getName());
0872: if (files[i].isDirectory()) {
0873: doCopyDirectory(files[i], copiedFile, preserveFileDate);
0874: } else {
0875: doCopyFile(files[i], copiedFile, preserveFileDate);
0876: }
0877: }
0878: }
0879:
0880: //-----------------------------------------------------------------------
0881: /**
0882: * Copies bytes from the URL <code>source</code> to a file
0883: * <code>destination</code>. The directories up to <code>destination</code>
0884: * will be created if they don't already exist. <code>destination</code>
0885: * will be overwritten if it already exists.
0886: *
0887: * @param source the <code>URL</code> to copy bytes from, must not be <code>null</code>
0888: * @param destination the non-directory <code>File</code> to write bytes to
0889: * (possibly overwriting), must not be <code>null</code>
0890: * @throws IOException if <code>source</code> URL cannot be opened
0891: * @throws IOException if <code>destination</code> is a directory
0892: * @throws IOException if <code>destination</code> cannot be written
0893: * @throws IOException if <code>destination</code> needs creating but can't be
0894: * @throws IOException if an IO error occurs during copying
0895: */
0896: public static void copyURLToFile(URL source, File destination)
0897: throws IOException {
0898: InputStream input = source.openStream();
0899: try {
0900: FileOutputStream output = openOutputStream(destination);
0901: try {
0902: IOUtils.copy(input, output);
0903: } finally {
0904: IOUtils.closeQuietly(output);
0905: }
0906: } finally {
0907: IOUtils.closeQuietly(input);
0908: }
0909: }
0910:
0911: //-----------------------------------------------------------------------
0912: /**
0913: * Recursively delete a directory.
0914: *
0915: * @param directory directory to delete
0916: * @throws IOException in case deletion is unsuccessful
0917: */
0918: public static void deleteDirectory(File directory)
0919: throws IOException {
0920: if (!directory.exists()) {
0921: return;
0922: }
0923:
0924: cleanDirectory(directory);
0925: if (!directory.delete()) {
0926: String message = "Unable to delete directory " + directory
0927: + ".";
0928: throw new IOException(message);
0929: }
0930: }
0931:
0932: /**
0933: * Clean a directory without deleting it.
0934: *
0935: * @param directory directory to clean
0936: * @throws IOException in case cleaning is unsuccessful
0937: */
0938: public static void cleanDirectory(File directory)
0939: throws IOException {
0940: if (!directory.exists()) {
0941: String message = directory + " does not exist";
0942: throw new IllegalArgumentException(message);
0943: }
0944:
0945: if (!directory.isDirectory()) {
0946: String message = directory + " is not a directory";
0947: throw new IllegalArgumentException(message);
0948: }
0949:
0950: File[] files = directory.listFiles();
0951: if (files == null) { // null if security restricted
0952: throw new IOException("Failed to list contents of "
0953: + directory);
0954: }
0955:
0956: IOException exception = null;
0957: for (int i = 0; i < files.length; i++) {
0958: File file = files[i];
0959: try {
0960: forceDelete(file);
0961: } catch (IOException ioe) {
0962: exception = ioe;
0963: }
0964: }
0965:
0966: if (null != exception) {
0967: throw exception;
0968: }
0969: }
0970:
0971: //-----------------------------------------------------------------------
0972: /**
0973: * Waits for NFS to propagate a file creation, imposing a timeout.
0974: * <p>
0975: * This method repeatedly tests {@link File#exists()} until it returns
0976: * true up to the maximum time specified in seconds.
0977: *
0978: * @param file the file to check, must not be <code>null</code>
0979: * @param seconds the maximum time in seconds to wait
0980: * @return true if file exists
0981: * @throws NullPointerException if the file is <code>null</code>
0982: */
0983: public static boolean waitFor(File file, int seconds) {
0984: int timeout = 0;
0985: int tick = 0;
0986: while (!file.exists()) {
0987: if (tick++ >= 10) {
0988: tick = 0;
0989: if (timeout++ > seconds) {
0990: return false;
0991: }
0992: }
0993: try {
0994: Thread.sleep(100);
0995: } catch (InterruptedException ignore) {
0996: // ignore exception
0997: } catch (Exception ex) {
0998: break;
0999: }
1000: }
1001: return true;
1002: }
1003:
1004: //-----------------------------------------------------------------------
1005: /**
1006: * Reads the contents of a file into a String.
1007: * The file is always closed.
1008: *
1009: * @param file the file to read, must not be <code>null</code>
1010: * @param encoding the encoding to use, <code>null</code> means platform default
1011: * @return the file contents, never <code>null</code>
1012: * @throws IOException in case of an I/O error
1013: * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
1014: */
1015: public static String readFileToString(File file, String encoding)
1016: throws IOException {
1017: InputStream in = null;
1018: try {
1019: in = openInputStream(file);
1020: return IOUtils.toString(in, encoding);
1021: } finally {
1022: IOUtils.closeQuietly(in);
1023: }
1024: }
1025:
1026: /**
1027: * Reads the contents of a file into a String using the default encoding for the VM.
1028: * The file is always closed.
1029: *
1030: * @param file the file to read, must not be <code>null</code>
1031: * @return the file contents, never <code>null</code>
1032: * @throws IOException in case of an I/O error
1033: * @since Commons IO 1.3.1
1034: */
1035: public static String readFileToString(File file) throws IOException {
1036: return readFileToString(file, null);
1037: }
1038:
1039: /**
1040: * Reads the contents of a file into a byte array.
1041: * The file is always closed.
1042: *
1043: * @param file the file to read, must not be <code>null</code>
1044: * @return the file contents, never <code>null</code>
1045: * @throws IOException in case of an I/O error
1046: * @since Commons IO 1.1
1047: */
1048: public static byte[] readFileToByteArray(File file)
1049: throws IOException {
1050: InputStream in = null;
1051: try {
1052: in = openInputStream(file);
1053: return IOUtils.toByteArray(in);
1054: } finally {
1055: IOUtils.closeQuietly(in);
1056: }
1057: }
1058:
1059: /**
1060: * Reads the contents of a file line by line to a List of Strings.
1061: * The file is always closed.
1062: *
1063: * @param file the file to read, must not be <code>null</code>
1064: * @param encoding the encoding to use, <code>null</code> means platform default
1065: * @return the list of Strings representing each line in the file, never <code>null</code>
1066: * @throws IOException in case of an I/O error
1067: * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
1068: * @since Commons IO 1.1
1069: */
1070: public static List readLines(File file, String encoding)
1071: throws IOException {
1072: InputStream in = null;
1073: try {
1074: in = openInputStream(file);
1075: return IOUtils.readLines(in, encoding);
1076: } finally {
1077: IOUtils.closeQuietly(in);
1078: }
1079: }
1080:
1081: /**
1082: * Reads the contents of a file line by line to a List of Strings using the default encoding for the VM.
1083: * The file is always closed.
1084: *
1085: * @param file the file to read, must not be <code>null</code>
1086: * @return the list of Strings representing each line in the file, never <code>null</code>
1087: * @throws IOException in case of an I/O error
1088: * @since Commons IO 1.3
1089: */
1090: public static List readLines(File file) throws IOException {
1091: return readLines(file, null);
1092: }
1093:
1094: /**
1095: * Return an Iterator for the lines in a <code>File</code>.
1096: * <p>
1097: * This method opens an <code>InputStream</code> for the file.
1098: * When you have finished with the iterator you should close the stream
1099: * to free internal resources. This can be done by calling the
1100: * {@link LineIterator#close()} or
1101: * {@link LineIterator#closeQuietly(LineIterator)} method.
1102: * <p>
1103: * The recommended usage pattern is:
1104: * <pre>
1105: * LineIterator it = FileUtils.lineIterator(file, "UTF-8");
1106: * try {
1107: * while (it.hasNext()) {
1108: * String line = it.nextLine();
1109: * /// do something with line
1110: * }
1111: * } finally {
1112: * LineIterator.closeQuietly(iterator);
1113: * }
1114: * </pre>
1115: * <p>
1116: * If an exception occurs during the creation of the iterator, the
1117: * underlying stream is closed.
1118: *
1119: * @param file the file to open for input, must not be <code>null</code>
1120: * @param encoding the encoding to use, <code>null</code> means platform default
1121: * @return an Iterator of the lines in the file, never <code>null</code>
1122: * @throws IOException in case of an I/O error (file closed)
1123: * @since Commons IO 1.2
1124: */
1125: public static LineIterator lineIterator(File file, String encoding)
1126: throws IOException {
1127: InputStream in = null;
1128: try {
1129: in = openInputStream(file);
1130: return IOUtils.lineIterator(in, encoding);
1131: } catch (IOException ex) {
1132: IOUtils.closeQuietly(in);
1133: throw ex;
1134: } catch (RuntimeException ex) {
1135: IOUtils.closeQuietly(in);
1136: throw ex;
1137: }
1138: }
1139:
1140: /**
1141: * Return an Iterator for the lines in a <code>File</code> using the default encoding for the VM.
1142: *
1143: * @param file the file to open for input, must not be <code>null</code>
1144: * @return an Iterator of the lines in the file, never <code>null</code>
1145: * @throws IOException in case of an I/O error (file closed)
1146: * @since Commons IO 1.3
1147: * @see #lineIterator(File, String)
1148: */
1149: public static LineIterator lineIterator(File file)
1150: throws IOException {
1151: return lineIterator(file, null);
1152: }
1153:
1154: //-----------------------------------------------------------------------
1155: /**
1156: * Writes a String to a file creating the file if it does not exist.
1157: *
1158: * NOTE: As from v1.3, the parent directories of the file will be created
1159: * if they do not exist.
1160: *
1161: * @param file the file to write
1162: * @param data the content to write to the file
1163: * @param encoding the encoding to use, <code>null</code> means platform default
1164: * @throws IOException in case of an I/O error
1165: * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
1166: */
1167: public static void writeStringToFile(File file, String data,
1168: String encoding) throws IOException {
1169: OutputStream out = null;
1170: try {
1171: out = openOutputStream(file);
1172: IOUtils.write(data, out, encoding);
1173: } finally {
1174: IOUtils.closeQuietly(out);
1175: }
1176: }
1177:
1178: /**
1179: * Writes a String to a file creating the file if it does not exist using the default encoding for the VM.
1180: *
1181: * @param file the file to write
1182: * @param data the content to write to the file
1183: * @throws IOException in case of an I/O error
1184: */
1185: public static void writeStringToFile(File file, String data)
1186: throws IOException {
1187: writeStringToFile(file, data, null);
1188: }
1189:
1190: /**
1191: * Writes a byte array to a file creating the file if it does not exist.
1192: * <p>
1193: * NOTE: As from v1.3, the parent directories of the file will be created
1194: * if they do not exist.
1195: *
1196: * @param file the file to write to
1197: * @param data the content to write to the file
1198: * @throws IOException in case of an I/O error
1199: * @since Commons IO 1.1
1200: */
1201: public static void writeByteArrayToFile(File file, byte[] data)
1202: throws IOException {
1203: OutputStream out = null;
1204: try {
1205: out = openOutputStream(file);
1206: out.write(data);
1207: } finally {
1208: IOUtils.closeQuietly(out);
1209: }
1210: }
1211:
1212: /**
1213: * Writes the <code>toString()</code> value of each item in a collection to
1214: * the specified <code>File</code> line by line.
1215: * The specified character encoding and the default line ending will be used.
1216: * <p>
1217: * NOTE: As from v1.3, the parent directories of the file will be created
1218: * if they do not exist.
1219: *
1220: * @param file the file to write to
1221: * @param encoding the encoding to use, <code>null</code> means platform default
1222: * @param lines the lines to write, <code>null</code> entries produce blank lines
1223: * @throws IOException in case of an I/O error
1224: * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
1225: * @since Commons IO 1.1
1226: */
1227: public static void writeLines(File file, String encoding,
1228: Collection lines) throws IOException {
1229: writeLines(file, encoding, lines, null);
1230: }
1231:
1232: /**
1233: * Writes the <code>toString()</code> value of each item in a collection to
1234: * the specified <code>File</code> line by line.
1235: * The default VM encoding and the default line ending will be used.
1236: *
1237: * @param file the file to write to
1238: * @param lines the lines to write, <code>null</code> entries produce blank lines
1239: * @throws IOException in case of an I/O error
1240: * @since Commons IO 1.3
1241: */
1242: public static void writeLines(File file, Collection lines)
1243: throws IOException {
1244: writeLines(file, null, lines, null);
1245: }
1246:
1247: /**
1248: * Writes the <code>toString()</code> value of each item in a collection to
1249: * the specified <code>File</code> line by line.
1250: * The specified character encoding and the line ending will be used.
1251: * <p>
1252: * NOTE: As from v1.3, the parent directories of the file will be created
1253: * if they do not exist.
1254: *
1255: * @param file the file to write to
1256: * @param encoding the encoding to use, <code>null</code> means platform default
1257: * @param lines the lines to write, <code>null</code> entries produce blank lines
1258: * @param lineEnding the line separator to use, <code>null</code> is system default
1259: * @throws IOException in case of an I/O error
1260: * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
1261: * @since Commons IO 1.1
1262: */
1263: public static void writeLines(File file, String encoding,
1264: Collection lines, String lineEnding) throws IOException {
1265: OutputStream out = null;
1266: try {
1267: out = openOutputStream(file);
1268: IOUtils.writeLines(lines, lineEnding, out, encoding);
1269: } finally {
1270: IOUtils.closeQuietly(out);
1271: }
1272: }
1273:
1274: /**
1275: * Writes the <code>toString()</code> value of each item in a collection to
1276: * the specified <code>File</code> line by line.
1277: * The default VM encoding and the specified line ending will be used.
1278: *
1279: * @param file the file to write to
1280: * @param lines the lines to write, <code>null</code> entries produce blank lines
1281: * @param lineEnding the line separator to use, <code>null</code> is system default
1282: * @throws IOException in case of an I/O error
1283: * @since Commons IO 1.3
1284: */
1285: public static void writeLines(File file, Collection lines,
1286: String lineEnding) throws IOException {
1287: writeLines(file, null, lines, lineEnding);
1288: }
1289:
1290: //-----------------------------------------------------------------------
1291: /**
1292: * Delete a file. If file is a directory, delete it and all sub-directories.
1293: * <p>
1294: * The difference between File.delete() and this method are:
1295: * <ul>
1296: * <li>A directory to be deleted does not have to be empty.</li>
1297: * <li>You get exceptions when a file or directory cannot be deleted.
1298: * (java.io.File methods returns a boolean)</li>
1299: * </ul>
1300: *
1301: * @param file file or directory to delete, must not be <code>null</code>
1302: * @throws NullPointerException if the directory is <code>null</code>
1303: * @throws IOException in case deletion is unsuccessful
1304: */
1305: public static void forceDelete(File file) throws IOException {
1306: if (file.isDirectory()) {
1307: deleteDirectory(file);
1308: } else {
1309: if (!file.exists()) {
1310: throw new FileNotFoundException("File does not exist: "
1311: + file);
1312: }
1313: if (!file.delete()) {
1314: String message = "Unable to delete file: " + file;
1315: throw new IOException(message);
1316: }
1317: }
1318: }
1319:
1320: /**
1321: * Schedule a file to be deleted when JVM exits.
1322: * If file is directory delete it and all sub-directories.
1323: *
1324: * @param file file or directory to delete, must not be <code>null</code>
1325: * @throws NullPointerException if the file is <code>null</code>
1326: * @throws IOException in case deletion is unsuccessful
1327: */
1328: public static void forceDeleteOnExit(File file) throws IOException {
1329: if (file.isDirectory()) {
1330: deleteDirectoryOnExit(file);
1331: } else {
1332: file.deleteOnExit();
1333: }
1334: }
1335:
1336: /**
1337: * Recursively schedule directory for deletion on JVM exit.
1338: *
1339: * @param directory directory to delete, must not be <code>null</code>
1340: * @throws NullPointerException if the directory is <code>null</code>
1341: * @throws IOException in case deletion is unsuccessful
1342: */
1343: private static void deleteDirectoryOnExit(File directory)
1344: throws IOException {
1345: if (!directory.exists()) {
1346: return;
1347: }
1348:
1349: cleanDirectoryOnExit(directory);
1350: directory.deleteOnExit();
1351: }
1352:
1353: /**
1354: * Clean a directory without deleting it.
1355: *
1356: * @param directory directory to clean, must not be <code>null</code>
1357: * @throws NullPointerException if the directory is <code>null</code>
1358: * @throws IOException in case cleaning is unsuccessful
1359: */
1360: private static void cleanDirectoryOnExit(File directory)
1361: throws IOException {
1362: if (!directory.exists()) {
1363: String message = directory + " does not exist";
1364: throw new IllegalArgumentException(message);
1365: }
1366:
1367: if (!directory.isDirectory()) {
1368: String message = directory + " is not a directory";
1369: throw new IllegalArgumentException(message);
1370: }
1371:
1372: File[] files = directory.listFiles();
1373: if (files == null) { // null if security restricted
1374: throw new IOException("Failed to list contents of "
1375: + directory);
1376: }
1377:
1378: IOException exception = null;
1379: for (int i = 0; i < files.length; i++) {
1380: File file = files[i];
1381: try {
1382: forceDeleteOnExit(file);
1383: } catch (IOException ioe) {
1384: exception = ioe;
1385: }
1386: }
1387:
1388: if (null != exception) {
1389: throw exception;
1390: }
1391: }
1392:
1393: /**
1394: * Make a directory, including any necessary but nonexistent parent
1395: * directories. If there already exists a file with specified name or
1396: * the directory cannot be created then an exception is thrown.
1397: *
1398: * @param directory directory to create, must not be <code>null</code>
1399: * @throws NullPointerException if the directory is <code>null</code>
1400: * @throws IOException if the directory cannot be created
1401: */
1402: public static void forceMkdir(File directory) throws IOException {
1403: if (directory.exists()) {
1404: if (directory.isFile()) {
1405: String message = "File "
1406: + directory
1407: + " exists and is "
1408: + "not a directory. Unable to create directory.";
1409: throw new IOException(message);
1410: }
1411: } else {
1412: if (!directory.mkdirs()) {
1413: String message = "Unable to create directory "
1414: + directory;
1415: throw new IOException(message);
1416: }
1417: }
1418: }
1419:
1420: //-----------------------------------------------------------------------
1421: /**
1422: * Recursively count size of a directory (sum of the length of all files).
1423: *
1424: * @param directory directory to inspect, must not be <code>null</code>
1425: * @return size of directory in bytes, 0 if directory is security restricted
1426: * @throws NullPointerException if the directory is <code>null</code>
1427: */
1428: public static long sizeOfDirectory(File directory) {
1429: if (!directory.exists()) {
1430: String message = directory + " does not exist";
1431: throw new IllegalArgumentException(message);
1432: }
1433:
1434: if (!directory.isDirectory()) {
1435: String message = directory + " is not a directory";
1436: throw new IllegalArgumentException(message);
1437: }
1438:
1439: long size = 0;
1440:
1441: File[] files = directory.listFiles();
1442: if (files == null) { // null if security restricted
1443: return 0L;
1444: }
1445: for (int i = 0; i < files.length; i++) {
1446: File file = files[i];
1447:
1448: if (file.isDirectory()) {
1449: size += sizeOfDirectory(file);
1450: } else {
1451: size += file.length();
1452: }
1453: }
1454:
1455: return size;
1456: }
1457:
1458: //-----------------------------------------------------------------------
1459: /**
1460: * Tests if the specified <code>File</code> is newer than the reference
1461: * <code>File</code>.
1462: *
1463: * @param file the <code>File</code> of which the modification date must
1464: * be compared, must not be <code>null</code>
1465: * @param reference the <code>File</code> of which the modification date
1466: * is used, must not be <code>null</code>
1467: * @return true if the <code>File</code> exists and has been modified more
1468: * recently than the reference <code>File</code>
1469: * @throws IllegalArgumentException if the file is <code>null</code>
1470: * @throws IllegalArgumentException if the reference file is <code>null</code> or doesn't exist
1471: */
1472: public static boolean isFileNewer(File file, File reference) {
1473: if (reference == null) {
1474: throw new IllegalArgumentException(
1475: "No specified reference file");
1476: }
1477: if (!reference.exists()) {
1478: throw new IllegalArgumentException("The reference file '"
1479: + file + "' doesn't exist");
1480: }
1481: return isFileNewer(file, reference.lastModified());
1482: }
1483:
1484: /**
1485: * Tests if the specified <code>File</code> is newer than the specified
1486: * <code>Date</code>.
1487: *
1488: * @param file the <code>File</code> of which the modification date
1489: * must be compared, must not be <code>null</code>
1490: * @param date the date reference, must not be <code>null</code>
1491: * @return true if the <code>File</code> exists and has been modified
1492: * after the given <code>Date</code>.
1493: * @throws IllegalArgumentException if the file is <code>null</code>
1494: * @throws IllegalArgumentException if the date is <code>null</code>
1495: */
1496: public static boolean isFileNewer(File file, Date date) {
1497: if (date == null) {
1498: throw new IllegalArgumentException("No specified date");
1499: }
1500: return isFileNewer(file, date.getTime());
1501: }
1502:
1503: /**
1504: * Tests if the specified <code>File</code> is newer than the specified
1505: * time reference.
1506: *
1507: * @param file the <code>File</code> of which the modification date must
1508: * be compared, must not be <code>null</code>
1509: * @param timeMillis the time reference measured in milliseconds since the
1510: * epoch (00:00:00 GMT, January 1, 1970)
1511: * @return true if the <code>File</code> exists and has been modified after
1512: * the given time reference.
1513: * @throws IllegalArgumentException if the file is <code>null</code>
1514: */
1515: public static boolean isFileNewer(File file, long timeMillis) {
1516: if (file == null) {
1517: throw new IllegalArgumentException("No specified file");
1518: }
1519: if (!file.exists()) {
1520: return false;
1521: }
1522: return file.lastModified() > timeMillis;
1523: }
1524:
1525: //-----------------------------------------------------------------------
1526: /**
1527: * Tests if the specified <code>File</code> is older than the reference
1528: * <code>File</code>.
1529: *
1530: * @param file the <code>File</code> of which the modification date must
1531: * be compared, must not be <code>null</code>
1532: * @param reference the <code>File</code> of which the modification date
1533: * is used, must not be <code>null</code>
1534: * @return true if the <code>File</code> exists and has been modified before
1535: * the reference <code>File</code>
1536: * @throws IllegalArgumentException if the file is <code>null</code>
1537: * @throws IllegalArgumentException if the reference file is <code>null</code> or doesn't exist
1538: */
1539: public static boolean isFileOlder(File file, File reference) {
1540: if (reference == null) {
1541: throw new IllegalArgumentException(
1542: "No specified reference file");
1543: }
1544: if (!reference.exists()) {
1545: throw new IllegalArgumentException("The reference file '"
1546: + file + "' doesn't exist");
1547: }
1548: return isFileOlder(file, reference.lastModified());
1549: }
1550:
1551: /**
1552: * Tests if the specified <code>File</code> is older than the specified
1553: * <code>Date</code>.
1554: *
1555: * @param file the <code>File</code> of which the modification date
1556: * must be compared, must not be <code>null</code>
1557: * @param date the date reference, must not be <code>null</code>
1558: * @return true if the <code>File</code> exists and has been modified
1559: * before the given <code>Date</code>.
1560: * @throws IllegalArgumentException if the file is <code>null</code>
1561: * @throws IllegalArgumentException if the date is <code>null</code>
1562: */
1563: public static boolean isFileOlder(File file, Date date) {
1564: if (date == null) {
1565: throw new IllegalArgumentException("No specified date");
1566: }
1567: return isFileOlder(file, date.getTime());
1568: }
1569:
1570: /**
1571: * Tests if the specified <code>File</code> is older than the specified
1572: * time reference.
1573: *
1574: * @param file the <code>File</code> of which the modification date must
1575: * be compared, must not be <code>null</code>
1576: * @param timeMillis the time reference measured in milliseconds since the
1577: * epoch (00:00:00 GMT, January 1, 1970)
1578: * @return true if the <code>File</code> exists and has been modified before
1579: * the given time reference.
1580: * @throws IllegalArgumentException if the file is <code>null</code>
1581: */
1582: public static boolean isFileOlder(File file, long timeMillis) {
1583: if (file == null) {
1584: throw new IllegalArgumentException("No specified file");
1585: }
1586: if (!file.exists()) {
1587: return false;
1588: }
1589: return file.lastModified() < timeMillis;
1590: }
1591:
1592: //-----------------------------------------------------------------------
1593: /**
1594: * Computes the checksum of a file using the CRC32 checksum routine.
1595: * The value of the checksum is returned.
1596: *
1597: * @param file the file to checksum, must not be <code>null</code>
1598: * @return the checksum value
1599: * @throws NullPointerException if the file or checksum is <code>null</code>
1600: * @throws IllegalArgumentException if the file is a directory
1601: * @throws IOException if an IO error occurs reading the file
1602: * @since Commons IO 1.3
1603: */
1604: public static long checksumCRC32(File file) throws IOException {
1605: CRC32 crc = new CRC32();
1606: checksum(file, crc);
1607: return crc.getValue();
1608: }
1609:
1610: /**
1611: * Computes the checksum of a file using the specified checksum object.
1612: * Multiple files may be checked using one <code>Checksum</code> instance
1613: * if desired simply by reusing the same checksum object.
1614: * For example:
1615: * <pre>
1616: * long csum = FileUtils.checksum(file, new CRC32()).getValue();
1617: * </pre>
1618: *
1619: * @param file the file to checksum, must not be <code>null</code>
1620: * @param checksum the checksum object to be used, must not be <code>null</code>
1621: * @return the checksum specified, updated with the content of the file
1622: * @throws NullPointerException if the file or checksum is <code>null</code>
1623: * @throws IllegalArgumentException if the file is a directory
1624: * @throws IOException if an IO error occurs reading the file
1625: * @since Commons IO 1.3
1626: */
1627: public static Checksum checksum(File file, Checksum checksum)
1628: throws IOException {
1629: if (file.isDirectory()) {
1630: throw new IllegalArgumentException(
1631: "Checksums can't be computed on directories");
1632: }
1633: InputStream in = null;
1634: try {
1635: in = new CheckedInputStream(new FileInputStream(file),
1636: checksum);
1637: IOUtils.copy(in, new NullOutputStream());
1638: } finally {
1639: IOUtils.closeQuietly(in);
1640: }
1641: return checksum;
1642: }
1643:
1644: }
|