This is an automated email from the ASF dual-hosted git repository. ggregory pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-io.git
The following commit(s) were added to refs/heads/master by this push: new 0cee29a FileUtils throws IllegalArgumentException for (most) illegal input to methods. 0cee29a is described below commit 0cee29aa4c1818963ed1a55058219282e89d7488 Author: Gary Gregory <gardgreg...@gmail.com> AuthorDate: Mon Jan 11 01:45:34 2021 -0500 FileUtils throws IllegalArgumentException for (most) illegal input to methods. - There are cases where FileNotFoundException is more appropriate and cases where we cannot throw FileNotFoundException in order to maintain BC. - Refactor to reuse private require*() methods. - Check for illegal inputs early in methods with Objects.requireNonNull() and custom private require() methods. - Convert a few unit tests to assertThrows(). - Fix [IO-699] Wrong logging in FileUtils.setLastModified. --- src/changes/changes.xml | 3 + src/main/java/org/apache/commons/io/FileUtils.java | 891 +++++++++++---------- .../FileUtilsCopyDirectoryToDirectoryTestCase.java | 6 +- .../org/apache/commons/io/FileUtilsTestCase.java | 258 ++---- 4 files changed, 550 insertions(+), 608 deletions(-) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 2396442..24802d0 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -84,6 +84,9 @@ The <action> type attribute can be add,update,fix,remove. <action issue="IO-600" dev="ggregory" type="fix" due-to="Abhyankar Chaubey, Gary Gregory"> Fix getPrefixLength method for Linux filename #179. </action> + <action issue="IO-699" dev="ggregory" type="fix" due-to="tza, Gary Gregory"> + Wrong logging in FileUtils.setLastModified. + </action> <!-- ADD --> <action dev="ggregory" type="add" due-to="Gary Gregory"> Add FileSystemProviders class. diff --git a/src/main/java/org/apache/commons/io/FileUtils.java b/src/main/java/org/apache/commons/io/FileUtils.java index 4a64f2d..df42b84 100644 --- a/src/main/java/org/apache/commons/io/FileUtils.java +++ b/src/main/java/org/apache/commons/io/FileUtils.java @@ -33,6 +33,7 @@ import java.net.URLConnection; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.nio.charset.UnsupportedCharsetException; import java.nio.file.CopyOption; import java.nio.file.Files; import java.nio.file.Path; @@ -85,9 +86,11 @@ import org.apache.commons.io.filefilter.TrueFileFilter; * <li>calculating a checksum * </ul> * <p> - * Note that a specific charset should be specified whenever possible. - * Relying on the platform default means that the code is Locale-dependent. - * Only use the default if the files are known to always use the platform default. + * Note that a specific charset should be specified whenever possible. Relying on the platform default means that the + * code is Locale-dependent. Only use the default if the files are known to always use the platform default. + * </p> + * <p> + * {@link SecurityException} are not documented in the Javadoc. * </p> * <p> * Origin of code: Excalibur, Alexandria, Commons-Utils @@ -193,11 +196,13 @@ public class FileUtils { * * @param size the number of bytes * @return a human-readable display value (includes units - EB, PB, TB, GB, MB, KB or bytes) + * @throws NullPointerException if the given {@code BigInteger} is {@code null}. * @see <a href="https://issues.apache.org/jira/browse/IO-226">IO-226 - should the rounding be changed?</a> * @since 2.4 */ // See https://issues.apache.org/jira/browse/IO-226 - should the rounding be changed? public static String byteCountToDisplaySize(final BigInteger size) { + Objects.requireNonNull(size, "size"); final String displaySize; if (size.divide(ONE_EB_BI).compareTo(BigInteger.ZERO) > 0) { @@ -238,24 +243,26 @@ public class FileUtils { } /** - * Computes the checksum of a file using the specified checksum object. - * Multiple files may be checked using one <code>Checksum</code> instance - * if desired simply by reusing the same checksum object. - * For example: + * Computes the checksum of a file using the specified checksum object. Multiple files may be checked using one + * <code>Checksum</code> instance if desired simply by reusing the same checksum object. For example: + * * <pre> - * long csum = FileUtils.checksum(file, new CRC32()).getValue(); + * long checksum = FileUtils.checksum(file, new CRC32()).getValue(); * </pre> * - * @param file the file to checksum, must not be {@code null} + * @param file the file to checksum, must not be {@code null} * @param checksum the checksum object to be used, must not be {@code null} * @return the checksum specified, updated with the content of the file - * @throws NullPointerException if the file or checksum is {@code null} - * @throws IllegalArgumentException if the file is a directory - * @throws IOException if an IO error occurs reading the file + * @throws NullPointerException if the given {@code File} is {@code null}. + * @throws NullPointerException if the given {@code Checksum} is {@code null}. + * @throws IllegalArgumentException if the given {@code File} does not exist or is not a file. + * @throws IOException if an IO error occurs reading the file. * @since 1.3 */ public static Checksum checksum(final File file, final Checksum checksum) throws IOException { + requireExistsChecked(file, "file"); requireFile(file, "file"); + Objects.requireNonNull(checksum, "checksum"); try (InputStream in = new CheckedInputStream(new FileInputStream(file), checksum)) { IOUtils.consume(in); } @@ -268,9 +275,9 @@ public class FileUtils { * * @param file the file to checksum, must not be {@code null} * @return the checksum value - * @throws NullPointerException if the file or checksum is {@code null} - * @throws IllegalArgumentException if the file is a directory - * @throws IOException if an IO error occurs reading the file + * @throws NullPointerException if the given {@code File} is {@code null}. + * @throws IllegalArgumentException if the given {@code File} does not exist or is not a file. + * @throws IOException if an IO error occurs reading the file. * @since 1.3 */ public static long checksumCRC32(final File file) throws IOException { @@ -281,12 +288,13 @@ public class FileUtils { * Cleans a directory without deleting it. * * @param directory directory to clean - * @throws IOException in case cleaning is unsuccessful - * @throws IllegalArgumentException if {@code directory} does not exist or is not a directory + * @throws NullPointerException if the given {@code File} is {@code null}. + * @throws IllegalArgumentException if directory does not exist or is not a directory. + * @throws IOException if an I/O error occurs. * @see #forceDelete(File) */ public static void cleanDirectory(final File directory) throws IOException { - final File[] files = verifiedListFiles(directory); + final File[] files = listFiles(directory, null); final List<Exception> causeList = new ArrayList<>(); for (final File file : files) { @@ -306,12 +314,13 @@ public class FileUtils { * Cleans a directory without deleting it. * * @param directory directory to clean, must not be {@code null} - * @throws NullPointerException if the directory is {@code null} - * @throws IOException in case cleaning is unsuccessful + * @throws NullPointerException if the given {@code File} is {@code null}. + * @throws IllegalArgumentException if directory does not exist or is not a directory. + * @throws IOException if an I/O error occurs. * @see #forceDeleteOnExit(File) */ private static void cleanDirectoryOnExit(final File directory) throws IOException { - final File[] files = verifiedListFiles(directory); + final File[] files = listFiles(directory, null); final List<Exception> causeList = new ArrayList<>(); for (final File file : files) { @@ -328,11 +337,10 @@ public class FileUtils { } /** - * Compares the contents of two files to determine if they are equal or not. + * Tests whether the contents of two files are equal. * <p> - * This method checks to see if the two files are different lengths - * or if they point to the same file, before resorting to byte-by-byte - * comparison of the contents. + * This method checks to see if the two files are different lengths or if they point to the same file, before + * resorting to byte-by-byte comparison of the contents. * </p> * <p> * Code origin: Avalon @@ -340,16 +348,16 @@ public class FileUtils { * * @param file1 the first file * @param file2 the second file - * @return true if the content of the files are equal or they both don't - * exist, false otherwise - * @throws IOException in case of an I/O error + * @return true if the content of the files are equal or they both don't exist, false otherwise + * @throws IllegalArgumentException when an input is not a file. + * @throws IOException If an I/O error occurs. * @see org.apache.commons.io.file.PathUtils#fileContentEquals(Path,Path,java.nio.file.LinkOption[],java.nio.file.OpenOption...) */ public static boolean contentEquals(final File file1, final File file2) throws IOException { if (file1 == null && file2 == null) { return true; } - if (file1 == null ^ file2 == null) { + if (file1 == null || file2 == null) { return false; } final boolean file1Exists = file1.exists(); @@ -362,10 +370,8 @@ public class FileUtils { return true; } - if (file1.isDirectory() || file2.isDirectory()) { - // don't want to compare directory contents - throw new IOException("Can't compare directories, only files"); - } + requireFile(file1, "file1"); + requireFile(file2, "file2"); if (file1.length() != file2.length()) { // lengths differ, cannot be equal @@ -377,8 +383,7 @@ public class FileUtils { return true; } - try (InputStream input1 = new FileInputStream(file1); - InputStream input2 = new FileInputStream(file2)) { + try (InputStream input1 = new FileInputStream(file1); InputStream input2 = new FileInputStream(file2)) { return IOUtils.contentEquals(input1, input2); } } @@ -396,7 +401,9 @@ public class FileUtils { * May be null, in which case the platform default is used * @return true if the content of the files are equal or neither exists, * false otherwise - * @throws IOException in case of an I/O error + * @throws IllegalArgumentException when an input is not a file. + * @throws IOException in case of an I/O error. + * @throws UnsupportedCharsetException If the named charset is unavailable (unchecked exception). * @see IOUtils#contentEqualsIgnoreEOL(Reader, Reader) * @since 2.2 */ @@ -405,7 +412,7 @@ public class FileUtils { if (file1 == null && file2 == null) { return true; } - if (file1 == null ^ file2 == null) { + if (file1 == null || file2 == null) { return false; } final boolean file1Exists = file1.exists(); @@ -418,18 +425,17 @@ public class FileUtils { return true; } - if (file1.isDirectory() || file2.isDirectory()) { - // don't want to compare directory contents - throw new IOException("Can't compare directories, only files"); - } + requireFile(file1, "file1"); + requireFile(file2, "file2"); if (file1.getCanonicalFile().equals(file2.getCanonicalFile())) { // same file return true; } - try (Reader input1 = new InputStreamReader(new FileInputStream(file1), Charsets.toCharset(charsetName)); - Reader input2 = new InputStreamReader(new FileInputStream(file2), Charsets.toCharset(charsetName))) { + final Charset charset = Charsets.toCharset(charsetName); + try (Reader input1 = new InputStreamReader(new FileInputStream(file1), charset); + Reader input2 = new InputStreamReader(new FileInputStream(file2), charset)) { return IOUtils.contentEqualsIgnoreEOL(input1, input2); } } @@ -443,7 +449,7 @@ public class FileUtils { * @return an array of java.io.File */ public static File[] convertFileCollectionToFileArray(final Collection<File> files) { - return files.toArray(new File[files.size()]); + return files.toArray(EMPTY_FILE_ARRAY); } /** @@ -464,9 +470,9 @@ public class FileUtils { * * @param srcDir an existing directory to copy, must not be {@code null}. * @param destDir the new directory, must not be {@code null}. - * - * @throws NullPointerException if source or destination is {@code null}. - * @throws IOException if source or destination is invalid. + * @throws NullPointerException if any of the given {@code File}s are {@code null}. + * @throws IllegalArgumentException if the source or destination is invalid. + * @throws FileNotFoundException if the source does not exist. * @throws IOException if an error occurs or setting the last-modified time didn't succeeded. * @since 1.1 */ @@ -492,9 +498,9 @@ public class FileUtils { * @param srcDir an existing directory to copy, must not be {@code null}. * @param destDir the new directory, must not be {@code null}. * @param preserveFileDate true if the file date of the copy should be the same as the original. - * - * @throws NullPointerException if source or destination is {@code null}. - * @throws IOException if source or destination is invalid. + * @throws NullPointerException if any of the given {@code File}s are {@code null}. + * @throws IllegalArgumentException if the source or destination is invalid. + * @throws FileNotFoundException if the source does not exist. * @throws IOException if an error occurs or setting the last-modified time didn't succeeded. * @since 1.1 */ @@ -541,14 +547,14 @@ public class FileUtils { * @param srcDir an existing directory to copy, must not be {@code null}. * @param destDir the new directory, must not be {@code null}. * @param filter the filter to apply, null means copy all directories and files should be the same as the original. - * - * @throws NullPointerException if source or destination is {@code null}. - * @throws IOException if source or destination is invalid. + * @throws NullPointerException if any of the given {@code File}s are {@code null}. + * @throws IllegalArgumentException if the source or destination is invalid. + * @throws FileNotFoundException if the source does not exist. * @throws IOException if an error occurs or setting the last-modified time didn't succeeded. * @since 1.4 */ - public static void copyDirectory(final File srcDir, final File destDir, - final FileFilter filter) throws IOException { + public static void copyDirectory(final File srcDir, final File destDir, final FileFilter filter) + throws IOException { copyDirectory(srcDir, destDir, filter, true); } @@ -591,9 +597,9 @@ public class FileUtils { * @param destDir the new directory, must not be {@code null}. * @param filter the filter to apply, null means copy all directories and files. * @param preserveFileDate true if the file date of the copy should be the same as the original. - * - * @throws NullPointerException if source or destination is {@code null}. - * @throws IOException if source or destination is invalid. + * @throws NullPointerException if any of the given {@code File}s are {@code null}. + * @throws IllegalArgumentException if the source or destination is invalid. + * @throws FileNotFoundException if the source does not exist. * @throws IOException if an error occurs or setting the last-modified time didn't succeeded. * @since 1.4 */ @@ -639,31 +645,27 @@ public class FileUtils { * * @param srcDir an existing directory to copy, must not be {@code null} * @param destDir the new directory, must not be {@code null} - * @param filter the filter to apply, null means copy all directories and files + * @param fileFilter the filter to apply, null means copy all directories and files * @param preserveFileDate true if the file date of the copy should be the same as the original * @param copyOptions options specifying how the copy should be done, for example {@link StandardCopyOption}. - * - * @throws NullPointerException if source or destination is {@code null}. - * @throws IOException if source or destination is invalid. + * @throws NullPointerException if any of the given {@code File}s are {@code null}. + * @throws IllegalArgumentException if the source or destination is invalid. + * @throws FileNotFoundException if the source does not exist. * @throws IOException if an error occurs or setting the last-modified time didn't succeeded. * @since 2.8.0 */ - public static void copyDirectory(final File srcDir, final File destDir, final FileFilter filter, + public static void copyDirectory(final File srcDir, final File destDir, final FileFilter fileFilter, final boolean preserveFileDate, final CopyOption... copyOptions) throws IOException { - requireFileRequirements(srcDir, destDir); - if (!srcDir.isDirectory()) { - throw new IOException("Source '" + srcDir + "' exists but is not a directory"); - } - final String srcDirCanonicalPath = srcDir.getCanonicalPath(); - final String destDirCanonicalPath = destDir.getCanonicalPath(); - if (srcDirCanonicalPath.equals(destDirCanonicalPath)) { - throw new IOException("Source '" + srcDir + "' and destination '" + destDir + "' are the same"); - } + requireFileCopy(srcDir, destDir); + requireDirectory(srcDir, "srcDir"); + requireCanonicalPathsNotEquals(srcDir, destDir); // Cater for destination being directory within the source directory (see IO-141) List<String> exclusionList = null; + final String srcDirCanonicalPath = srcDir.getCanonicalPath(); + final String destDirCanonicalPath = destDir.getCanonicalPath(); if (destDirCanonicalPath.startsWith(srcDirCanonicalPath)) { - final File[] srcFiles = filter == null ? srcDir.listFiles() : srcDir.listFiles(filter); + final File[] srcFiles = listFiles(srcDir, fileFilter); if (srcFiles != null && srcFiles.length > 0) { exclusionList = new ArrayList<>(srcFiles.length); for (final File srcFile : srcFiles) { @@ -672,7 +674,7 @@ public class FileUtils { } } } - doCopyDirectory(srcDir, destDir, filter, preserveFileDate, exclusionList, copyOptions); + doCopyDirectory(srcDir, destDir, fileFilter, preserveFileDate, exclusionList, copyOptions); } /** @@ -693,22 +695,15 @@ public class FileUtils { * * @param sourceDir an existing directory to copy, must not be {@code null}. * @param destinationDir the directory to place the copy in, must not be {@code null}. - * - * @throws NullPointerException if source or destination is {@code null}. - * @throws IllegalArgumentException if {@code srcDir} or {@code destDir} is not a directory. - * @throws IOException if source or destination is invalid. + * @throws NullPointerException if any of the given {@code File}s are {@code null}. + * @throws IllegalArgumentException if the source or destination is invalid. + * @throws FileNotFoundException if the source does not exist. * @throws IOException if an error occurs or setting the last-modified time didn't succeeded. * @since 1.2 */ public static void copyDirectoryToDirectory(final File sourceDir, final File destinationDir) throws IOException { - Objects.requireNonNull(sourceDir, "sourceDir"); - if (sourceDir.exists() && sourceDir.isDirectory() == false) { - throw new IllegalArgumentException("Source '" + sourceDir + "' is not a directory"); - } - Objects.requireNonNull(destinationDir, "destinationDir"); - if (destinationDir.exists() && destinationDir.isDirectory() == false) { - throw new IllegalArgumentException("Destination '" + destinationDir + "' is not a directory"); - } + requireDirectoryIfExists(sourceDir, "sourceDir"); + requireDirectoryIfExists(destinationDir, "destinationDir"); copyDirectory(sourceDir, new File(destinationDir, sourceDir.getName()), true); } @@ -727,8 +722,7 @@ public class FileUtils { * * @param srcFile an existing file to copy, must not be {@code null}. * @param destFile the new file, must not be {@code null}. - * - * @throws NullPointerException if source or destination is {@code null}. + * @throws NullPointerException if any of the given {@code File}s are {@code null}. * @throws IOException if source or destination is invalid. * @throws IOException if an error occurs or setting the last-modified time didn't succeeded. * @throws IOException if the output file length is not the same as the input file length after the copy completes. @@ -740,7 +734,7 @@ public class FileUtils { } /** - * Copies a file to a new location. + * Copies an existing file to a new file location. * <p> * This method copies the contents of the specified source file to the specified destination file. The directory * holding the destination file is created if it does not exist. If the destination file exists, then this method @@ -755,12 +749,11 @@ public class FileUtils { * @param srcFile an existing file to copy, must not be {@code null}. * @param destFile the new file, must not be {@code null}. * @param preserveFileDate true if the file date of the copy should be the same as the original. - * - * @throws NullPointerException if source or destination is {@code null}. + * @throws NullPointerException if any of the given {@code File}s are {@code null}. * @throws IOException if source or destination is invalid. * @throws IOException if an error occurs or setting the last-modified time didn't succeeded. * @throws IOException if the output file length is not the same as the input file length after the copy completes - * @see #copyFileToDirectory(File, File, boolean) + * @see #copyFile(File, File, boolean, CopyOption...) */ public static void copyFile(final File srcFile, final File destFile, final boolean preserveFileDate) throws IOException { @@ -784,30 +777,23 @@ public class FileUtils { * @param destFile the new file, must not be {@code null}. * @param preserveFileDate true if the file date of the copy should be the same as the original. * @param copyOptions options specifying how the copy should be done, for example {@link StandardCopyOption}.. - * @throws NullPointerException if source or destination is {@code null}. - * @throws IOException if source or destination is invalid. + * @throws NullPointerException if any of the given {@code File}s are {@code null}. + * @throws FileNotFoundException if the source does not exist. + * @throws IllegalArgumentException if source is not a file. * @throws IOException if the output file length is not the same as the input file length after the copy completes. - * @throws IOException if an error occurs or setting the last-modified time didn't succeeded. + * @throws IOException if an I/O error occurs, or setting the last-modified time didn't succeeded. * @see #copyFileToDirectory(File, File, boolean) * @since 2.8.0 */ public static void copyFile(final File srcFile, final File destFile, final boolean preserveFileDate, final CopyOption... copyOptions) throws IOException { - requireFileRequirements(srcFile, destFile); - if (srcFile.isDirectory()) { - throw new IOException("Source '" + srcFile + "' exists but is a directory"); - } - if (srcFile.getCanonicalPath().equals(destFile.getCanonicalPath())) { - throw new IOException("Source '" + srcFile + "' and destination '" + destFile + "' are the same"); - } - final File parentFile = destFile.getParentFile(); - if (parentFile != null) { - if (!parentFile.mkdirs() && !parentFile.isDirectory()) { - throw new IOException("Destination '" + parentFile + "' directory cannot be created"); - } - } - if (destFile.exists() && destFile.canWrite() == false) { - throw new IOException("Destination '" + destFile + "' exists but is read-only"); + requireFileCopy(srcFile, destFile); + requireFile(srcFile, "srcFile"); + requireCanonicalPathsNotEquals(srcFile, destFile); + createParentDirectories(destFile); + + if (destFile.exists()) { + requireCanWrite(destFile, "destFile"); } doCopyFile(srcFile, destFile, preserveFileDate, copyOptions); } @@ -821,7 +807,8 @@ public class FileUtils { * @param input the <code>File</code> to read from * @param output the <code>OutputStream</code> to write to * @return the number of bytes copied - * @throws NullPointerException if the input or output is null + * @throws NullPointerException if the File is {@code null}. + * @throws NullPointerException if the OutputStream is {@code null}. * @throws IOException if an I/O error occurs * @since 2.1 */ @@ -846,9 +833,8 @@ public class FileUtils { * * @param srcFile an existing file to copy, must not be {@code null}. * @param destDir the directory to place the copy in, must not be {@code null}. - * - * @throws NullPointerException if source or destination is null. - * @throws IOException if source or destination is invalid. + * @throws NullPointerException if any of the given {@code File}s are {@code null}. + * @throws IllegalArgumentException if source or destination is invalid. * @throws IOException if an error occurs or setting the last-modified time didn't succeeded. * @see #copyFile(File, File, boolean) */ @@ -872,9 +858,7 @@ public class FileUtils { * @param sourceFile an existing file to copy, must not be {@code null}. * @param destinationDir the directory to place the copy in, must not be {@code null}. * @param preserveFileDate true if the file date of the copy should be the same as the original. - * - * @throws NullPointerException if source or destination is {@code null}. - * @throws IOException if source or destination is invalid. + * @throws NullPointerException if any of the given {@code File}s are {@code null}. * @throws IOException if an error occurs or setting the last-modified time didn't succeeded. * @throws IOException if the output file length is not the same as the input file length after the copy completes. * @see #copyFile(File, File, boolean) @@ -882,10 +866,7 @@ public class FileUtils { */ public static void copyFileToDirectory(final File sourceFile, final File destinationDir, final boolean preserveFileDate) throws IOException { - Objects.requireNonNull(destinationDir, "destinationDir"); - if (destinationDir.exists() && destinationDir.isDirectory() == false) { - throw new IllegalArgumentException("Destination '" + destinationDir + "' is not a directory"); - } + requireDirectoryIfExists(destinationDir, "destinationDir"); final File destFile = new File(destinationDir, sourceFile.getName()); copyFile(sourceFile, destFile, preserveFileDate); } @@ -895,8 +876,12 @@ public class FileUtils { * <code>destination</code>. The directories up to <code>destination</code> * will be created if they don't already exist. <code>destination</code> * will be overwritten if it already exists. - * The {@code source} stream is closed. + * <p> + * <em>The {@code source} stream is closed.</em> + * </p> + * <p> * See {@link #copyToFile(InputStream, File)} for a method that does not close the input stream. + * </p> * * @param source the <code>InputStream</code> to copy bytes from, must not be {@code null}, will be closed * @param destination the non-directory <code>File</code> to write bytes to @@ -931,9 +916,9 @@ public class FileUtils { * * @param sourceFile an existing file or directory to copy, must not be {@code null}. * @param destinationDir the directory to place the copy in, must not be {@code null}. - * - * @throws NullPointerException if source or destination is {@code null}. - * @throws IOException if source or destination is invalid. + * @throws NullPointerException if any of the given {@code File}s are {@code null}. + * @throws IllegalArgumentException if the source or destination is invalid. + * @throws FileNotFoundException if the source does not exist. * @throws IOException if an error occurs or setting the last-modified time didn't succeeded. * @see #copyDirectoryToDirectory(File, File) * @see #copyFileToDirectory(File, File) @@ -946,7 +931,7 @@ public class FileUtils { } else if (sourceFile.isDirectory()) { copyDirectoryToDirectory(sourceFile, destinationDir); } else { - throw new IOException("The source " + sourceFile + " does not exist"); + throw new FileNotFoundException("The source " + sourceFile + " does not exist"); } } @@ -967,8 +952,7 @@ public class FileUtils { * * @param sourceIterable a existing files to copy, must not be {@code null}. * @param destinationDir the directory to place the copy in, must not be {@code null}. - * - * @throws NullPointerException if source or destination is null. + * @throws NullPointerException if any of the given {@code File}s are {@code null}. * @throws IOException if source or destination is invalid. * @throws IOException if an error occurs or setting the last-modified time didn't succeeded. * @see #copyFileToDirectory(File, File) @@ -1030,20 +1014,18 @@ public class FileUtils { } } - /** - * Copies bytes from the URL <code>source</code> to a file - * <code>destination</code>. The directories up to <code>destination</code> - * will be created if they don't already exist. <code>destination</code> - * will be overwritten if it already exists. + * Copies bytes from the URL <code>source</code> to a file <code>destination</code>. The directories up to + * <code>destination</code> will be created if they don't already exist. <code>destination</code> will be + * overwritten if it already exists. * - * @param source the <code>URL</code> to copy bytes from, must not be {@code null} - * @param destination the non-directory <code>File</code> to write bytes to - * (possibly overwriting), must not be {@code null} - * @param connectionTimeout the number of milliseconds until this method - * will timeout if no connection could be established to the <code>source</code> - * @param readTimeout the number of milliseconds until this method will - * timeout if no data could be read from the <code>source</code> + * @param source the <code>URL</code> to copy bytes from, must not be {@code null} + * @param destination the non-directory <code>File</code> to write bytes to (possibly overwriting), must not be + * {@code null} + * @param connectionTimeoutMillis the number of milliseconds until this method will timeout if no connection could + * be established to the <code>source</code> + * @param readTimeoutMillis the number of milliseconds until this method will timeout if no data could be read from + * the <code>source</code> * @throws IOException if <code>source</code> URL cannot be opened * @throws IOException if <code>destination</code> is a directory * @throws IOException if <code>destination</code> cannot be written @@ -1052,15 +1034,29 @@ public class FileUtils { * @since 2.0 */ public static void copyURLToFile(final URL source, final File destination, - final int connectionTimeout, final int readTimeout) throws IOException { + final int connectionTimeoutMillis, final int readTimeoutMillis) throws IOException { final URLConnection connection = source.openConnection(); - connection.setConnectTimeout(connectionTimeout); - connection.setReadTimeout(readTimeout); + connection.setConnectTimeout(connectionTimeoutMillis); + connection.setReadTimeout(readTimeoutMillis); try (final InputStream stream = connection.getInputStream()) { copyInputStreamToFile(stream, destination); } } + + /** + * Creates all parent directories for a File object. + * + * @param file the File that may need parents, may be null. + * @return The parent directory, or {@code null} if the given file does not name a parent + * @throws IOException if the directory was not created along with all its parent directories. + * @throws IOException if the given file object is not null and not a directory. + * @since 2.9.0 + */ + public static File createParentDirectories(final File file) throws IOException { + return mkdirs(getParentFile(file)); + } + /** * Decodes the specified URL as per RFC 3986, i.e. transforms * percent-encoded octets to characters by decoding with the UTF-8 character @@ -1109,7 +1105,8 @@ public class FileUtils { } /** - * Deletes the given File but throws IOException if it cannot, unlike {@link File#delete()}. + * Deletes the given File but throws an IOException if it cannot, unlike {@link File#delete()} which returns a + * boolean. * * @param file The file to delete. * @return the given file. @@ -1118,6 +1115,7 @@ public class FileUtils { * @since 2.9.0 */ public static File delete(final File file) throws IOException { + Objects.requireNonNull(file, "file"); if (!file.delete()) { throw new IOException("Unable to delete " + file); } @@ -1132,14 +1130,13 @@ public class FileUtils { * @throws IllegalArgumentException if {@code directory} does not exist or is not a directory */ public static void deleteDirectory(final File directory) throws IOException { + Objects.requireNonNull(directory, "directory"); if (!directory.exists()) { return; } - if (!isSymlink(directory)) { cleanDirectory(directory); } - delete(directory); } @@ -1154,7 +1151,6 @@ public class FileUtils { if (!directory.exists()) { return; } - directory.deleteOnExit(); if (!isSymlink(directory)) { cleanDirectoryOnExit(directory); @@ -1219,7 +1215,7 @@ public class FileUtils { * @since 2.2 */ public static boolean directoryContains(final File directory, final File child) throws IOException { - requireDirectory(directory, "directory"); + requireDirectoryExists(directory, "directory"); if (child == null) { return false; @@ -1241,37 +1237,26 @@ public class FileUtils { * * @param srcDir the validated source directory, must not be {@code null}. * @param destDir the validated destination directory, must not be {@code null}. - * @param filter the filter to apply, null means copy all directories and files. + * @param fileFilter the filter to apply, null means copy all directories and files. * @param preserveFileDate whether to preserve the file date. * @param exclusionList List of files and directories to exclude from the copy, may be null. * @param copyOptions options specifying how the copy should be done, for example {@link StandardCopyOption}. - * @throws IOException if an error occurs or setting the last-modified time didn't succeeded. + * @throws IOException if the directory was not created along with all its parent directories. + * @throws IOException if the given file object is not a directory. */ - private static void doCopyDirectory(final File srcDir, final File destDir, final FileFilter filter, + private static void doCopyDirectory(final File srcDir, final File destDir, final FileFilter fileFilter, final boolean preserveFileDate, final List<String> exclusionList, final CopyOption... copyOptions) throws IOException { // recurse - final File[] srcFiles = filter == null ? srcDir.listFiles() : srcDir.listFiles(filter); - if (srcFiles == null) { // null if abstract pathname does not denote a directory, or if an I/O error occurs - throw new IOException("Failed to list contents of " + srcDir); - } - if (destDir.exists()) { - if (destDir.isDirectory() == false) { - throw new IOException("Destination '" + destDir + "' exists but is not a directory"); - } - } else { - if (!destDir.mkdirs() && !destDir.isDirectory()) { - throw new IOException("Destination '" + destDir + "' directory cannot be created"); - } - } - if (destDir.canWrite() == false) { - throw new IOException("Destination '" + destDir + "' cannot be written to"); - } + final File[] srcFiles = listFiles(srcDir, fileFilter); + requireDirectoryIfExists(destDir, "destDir"); + mkdirs(destDir); + requireCanWrite(destDir, "destDir"); for (final File srcFile : srcFiles) { final File dstFile = new File(destDir, srcFile.getName()); if (exclusionList == null || !exclusionList.contains(srcFile.getCanonicalPath())) { if (srcFile.isDirectory()) { - doCopyDirectory(srcFile, dstFile, filter, preserveFileDate, exclusionList, copyOptions); + doCopyDirectory(srcFile, dstFile, fileFilter, preserveFileDate, exclusionList, copyOptions); } else { doCopyFile(srcFile, dstFile, preserveFileDate, copyOptions); } @@ -1301,9 +1286,8 @@ public class FileUtils { */ private static void doCopyFile(final File srcFile, final File destFile, final boolean preserveFileDate, final CopyOption... copyOptions) throws IOException { - if (destFile.exists() && destFile.isDirectory()) { - throw new IOException("Destination '" + destFile + "' exists but is a directory"); - } + Objects.requireNonNull(srcFile, "srcFile"); + requireFileIfExists(destFile, "destFile"); final Path srcPath = srcFile.toPath(); final Path destPath = destFile.toPath(); @@ -1311,8 +1295,6 @@ public class FileUtils { Files.copy(srcPath, destPath, copyOptions); // TODO IO-386: Do we still need this check? - requireEqualSizes(srcFile, destFile, Files.size(srcPath), Files.size(destPath)); - // TODO IO-386: Do we still need this check? requireEqualSizes(srcFile, destFile, srcFile.length(), destFile.length()); if (preserveFileDate) { @@ -1336,12 +1318,13 @@ public class FileUtils { * @throws IOException in case deletion is unsuccessful */ public static void forceDelete(final File file) throws IOException { + Objects.requireNonNull(file, "file"); final Counters.PathCounters deleteCounters; try { deleteCounters = PathUtils.delete(file.toPath(), PathUtils.EMPTY_LINK_OPTION_ARRAY, StandardDeleteOption.OVERRIDE_READ_ONLY); } catch (final IOException e) { - throw new IOException("Unable to delete file: " + file, e); + throw new IOException("Cannot delete file: " + file, e); } if (deleteCounters.getFileCounter().get() < 1 && deleteCounters.getDirectoryCounter().get() < 1) { @@ -1359,6 +1342,7 @@ public class FileUtils { * @throws IOException in case deletion is unsuccessful */ public static void forceDeleteOnExit(final File file) throws IOException { + Objects.requireNonNull(file, "file"); if (file.isDirectory()) { deleteDirectoryOnExit(file); } else { @@ -1374,26 +1358,12 @@ public class FileUtils { * then an IOException is thrown. * * @param directory directory to create, must not be {@code null} - * @throws NullPointerException if the directory is {@code null} - * @throws IOException if the directory cannot be created or the file already exists but is not a directory + * @throws IOException if the directory was not created along with all its parent directories. + * @throws IOException if the given file object is not a directory. + * @throws SecurityException See {@link File#mkdirs()}. */ public static void forceMkdir(final File directory) throws IOException { - if (directory.exists()) { - if (!directory.isDirectory()) { - throw new IOException("File " - + directory - + " exists and is " - + "not a directory. Unable to create directory."); - } - } else { - if (!directory.mkdirs()) { - // Double-check that some other thread or process hasn't made - // the directory in the background - if (!directory.isDirectory()) { - throw new IOException("Unable to create directory " + directory); - } - } - } + mkdirs(directory); } /** @@ -1406,7 +1376,8 @@ public class FileUtils { * @since 2.5 */ public static void forceMkdirParent(final File file) throws IOException { - final File parent = file.getParentFile(); + Objects.requireNonNull(file, "file"); + final File parent = getParentFile(file); if (parent == null) { return; } @@ -1418,7 +1389,7 @@ public class FileUtils { * * @param directory the parent directory * @param names the name elements - * @return the file + * @return the new file * @since 2.1 */ public static File getFile(final File directory, final String... names) { @@ -1452,6 +1423,16 @@ public class FileUtils { } /** + * Gets the parent of the given file. The given file may be bull and a file's parent may as well be null. + * + * @param file The file to query. + * @return The parent file or {@code null}. + */ + private static File getParentFile(final File file) { + return file == null ? null : file.getParentFile(); + } + + /** * Returns a {@link File} representing the system temporary directory. * * @return the system temporary directory. @@ -1798,7 +1779,6 @@ public class FileUtils { * @param instant the date reference * @return true if the {@code File} exists and has been modified before the given {@code Instant}. * @throws NullPointerException if the file or instant is {@code null} - * * @since 2.8.0 */ public static boolean isFileOlder(final File file, final Instant instant) { @@ -1813,7 +1793,7 @@ public class FileUtils { * @param timeMillis the time reference measured in milliseconds since the * epoch (00:00:00 GMT, January 1, 1970) * @return true if the {@code File} exists and has been modified before the given time reference. - * @throws NullPointerException if the file is {@code null} + * @throws NullPointerException if the file is {@code null}. */ public static boolean isFileOlder(final File file, final long timeMillis) { Objects.requireNonNull(file, "file"); @@ -1824,31 +1804,18 @@ public class FileUtils { } /** - * Determines whether the specified file is a Symbolic Link rather than an actual file. + * Tests whether the specified file is a symbolic link rather than an actual file. * <p> - * Will not return true if there is a Symbolic Link anywhere in the path, - * only if the specific file is. + * This method delegates to {@link Files#isSymbolicLink(Path path)} * </p> - * <p> - * When using jdk1.7, this method delegates to {@code boolean java.nio.file.Files.isSymbolicLink(Path path)} - * </p> - * - * <p> - * <b>Note:</b> the current implementation always returns {@code false} if running on - * jkd1.6 and the system is detected as Windows using {@link FilenameUtils#isSystemWindows()} - * </p> - * <p> - * For code that runs on Java 1.7 or later, use the following method instead: - * </p> - * - * {@code boolean java.nio.file.Files.isSymbolicLink(Path path)} - * @param file the file to check - * @return true if the file is a Symbolic Link + * + * @param file the file to test. + * @return true if the file is a symbolic link, see {@link Files#isSymbolicLink(Path path)}. * @since 2.0 + * @see Files#isSymbolicLink(Path) */ public static boolean isSymlink(final File file) { - Objects.requireNonNull(file, "file"); - return Files.isSymbolicLink(file.toPath()); + return file != null ? Files.isSymbolicLink(file.toPath()) : false; } /** @@ -1933,7 +1900,10 @@ public class FileUtils { * * @param file the file to open for input, must not be {@code null} * @return an Iterator of the lines in the file, never {@code null} - * @throws IOException in case of an I/O error (file closed) + * @throws NullPointerException if file is {@code null}. + * @throws FileNotFoundException if the file does not exist, is a directory rather than a regular file, or for some + * other reason cannot be opened for reading. + * @throws IOException if an I/O error occurs. * @see #lineIterator(File, String) * @since 1.3 */ @@ -1972,7 +1942,10 @@ public class FileUtils { * @param file the file to open for input, must not be {@code null} * @param charsetName the name of the requested charset, {@code null} means platform default * @return an Iterator of the lines in the file, never {@code null} - * @throws IOException in case of an I/O error (file closed) + * @throws NullPointerException if file is {@code null}. + * @throws FileNotFoundException if the file does not exist, is a directory rather than a regular file, or for some + * other reason cannot be opened for reading. + * @throws IOException if an I/O error occurs. * @since 1.2 */ public static LineIterator lineIterator(final File file, final String charsetName) throws IOException { @@ -1998,6 +1971,26 @@ public class FileUtils { } /** + * Lists files in a directory, asserting that the supplied directory exists and is a directory. + * + * @param directory The directory to list + * @param fileFilter Optional file filter, may be null. + * @return The files in the directory, never {@code null}. + * @throws NullPointerException if directory is {@code null}. + * @throws IllegalArgumentException if directory does not exist or is not a directory. + * @throws IOException if an I/O error occurs. + */ + private static File[] listFiles(final File directory, FileFilter fileFilter) throws IOException { + requireDirectoryExists(directory, "directory"); + final File[] files = fileFilter == null ? directory.listFiles() : directory.listFiles(fileFilter); + if (files == null) { + // null if the directory does not denote a directory, or if an I/O error occurs. + throw new IOException("Unknown I/O error listing contents of directory: " + directory); + } + return files; + } + + /** * Finds files within a given directory (and optionally its * subdirectories). All files found are filtered by an IOFileFilter. * <p> @@ -2086,6 +2079,25 @@ public class FileUtils { } /** + * Calls {@link File#mkdirs()} and throws an exception on failure. + * + * @param directory the receiver for {@code mkdirs()}, may be null. + * @return the given file, may be null. + * @throws IOException if the directory was not created along with all its parent directories. + * @throws IOException if the given file object is not a directory. + * @throws SecurityException See {@link File#mkdirs()}. + * @see @see File#mkdirs() + */ + private static File mkdirs(final File directory) throws IOException { + if (directory != null) { + if (!directory.mkdirs() && !directory.isDirectory()) { + throw new IOException("Cannot create directory '" + directory + "'."); + } + } + return directory; + } + + /** * Moves a directory. * <p> * When the destination directory is on another file system, do a "copy and delete". @@ -2093,22 +2105,17 @@ public class FileUtils { * * @param srcDir the directory to be moved. * @param destDir the destination directory. - * @throws NullPointerException if source or destination is {@code null}. - * @throws FileExistsException if the destination directory exists. - * @throws IOException if source or destination is invalid. + * @throws NullPointerException if any of the given {@code File}s are {@code null}. + * @throws IllegalArgumentException if the source or destination is invalid. + * @throws FileNotFoundException if the source does not exist. * @throws IOException if an error occurs or setting the last-modified time didn't succeeded. * @since 1.4 */ public static void moveDirectory(final File srcDir, final File destDir) throws IOException { validateMoveParameters(srcDir, destDir); - if (!srcDir.isDirectory()) { - throw new IOException("Source '" + srcDir + "' is not a directory"); - } - if (destDir.exists()) { - throw new FileExistsException("Destination '" + destDir + "' already exists"); - } - final boolean rename = srcDir.renameTo(destDir); - if (!rename) { + requireDirectory(srcDir, "srcDir"); + requireAbsent(destDir, "destDir"); + if (!srcDir.renameTo(destDir)) { if (destDir.getCanonicalPath().startsWith(srcDir.getCanonicalPath() + File.separator)) { throw new IOException("Cannot move directory: " + srcDir + " to a subdirectory of itself: " + destDir); } @@ -2128,9 +2135,9 @@ public class FileUtils { * @param destDir the destination file. * @param createDestDir If {@code true} create the destination directory, otherwise if {@code false} throw an * IOException. - * @throws NullPointerException if source or destination is {@code null}. - * @throws FileExistsException if the directory exists in the destination directory. - * @throws IOException if source or destination is invalid. + * @throws NullPointerException if any of the given {@code File}s are {@code null}. + * @throws IllegalArgumentException if the source or destination is invalid. + * @throws FileNotFoundException if the source does not exist. * @throws IOException if an error occurs or setting the last-modified time didn't succeeded. * @since 1.4 */ @@ -2141,9 +2148,7 @@ public class FileUtils { if (destDir.exists()) { throw new IOException("Destination '" + destDir + "' is not a directory"); } else if (createDestDir) { - if (!destDir.mkdirs()) { - throw new IOException("Could not create destination directories '" + destDir + "'"); - } + mkdirs(destDir); } else { throw new FileNotFoundException("Destination directory '" + destDir + "' does not exist [createDestDir=" + createDestDir + "]"); @@ -2160,7 +2165,7 @@ public class FileUtils { * * @param srcFile the file to be moved. * @param destFile the destination file. - * @throws NullPointerException if source or destination is {@code null}. + * @throws NullPointerException if any of the given {@code File}s are {@code null}. * @throws FileExistsException if the destination file exists. * @throws IOException if source or destination is invalid. * @throws IOException if an error occurs or setting the last-modified time didn't succeeded. @@ -2168,15 +2173,8 @@ public class FileUtils { */ public static void moveFile(final File srcFile, final File destFile) throws IOException { validateMoveParameters(srcFile, destFile); - if (srcFile.isDirectory()) { - throw new IOException("Source '" + srcFile + "' is a directory"); - } - if (destFile.exists()) { - throw new FileExistsException("Destination '" + destFile + "' already exists"); - } - if (destFile.isDirectory()) { - throw new IOException("Destination '" + destFile + "' is a directory"); - } + requireFile(srcFile, "srcFile"); + requireAbsent(destFile, null); final boolean rename = srcFile.renameTo(destFile); if (!rename) { copyFile(srcFile, destFile); @@ -2195,7 +2193,7 @@ public class FileUtils { * @param destDir the destination file. * @param createDestDir If {@code true} create the destination directory, otherwise if {@code false} throw an * IOException. - * @throws NullPointerException if source or destination is {@code null}. + * @throws NullPointerException if any of the given {@code File}s are {@code null}. * @throws FileExistsException if the destination file exists. * @throws IOException if source or destination is invalid. * @throws IOException if an error occurs or setting the last-modified time didn't succeeded. @@ -2205,17 +2203,10 @@ public class FileUtils { throws IOException { validateMoveParameters(srcFile, destDir); if (!destDir.exists() && createDestDir) { - if (!destDir.mkdirs()) { - throw new IOException("Could not create destination directories '" + destDir + "'"); - } - } - if (!destDir.exists()) { - throw new FileNotFoundException("Destination directory '" + destDir + - "' does not exist [createDestDir=" + createDestDir + "]"); - } - if (!destDir.isDirectory()) { - throw new IOException("Destination '" + destDir + "' is not a directory"); + mkdirs(destDir); } + requireExistsChecked(destDir, "destDir"); + requireDirectory(destDir, "destDir"); moveFile(srcFile, new File(destDir, srcFile.getName())); } @@ -2229,7 +2220,7 @@ public class FileUtils { * @param destDir the destination directory. * @param createDestDir If {@code true} create the destination directory, otherwise if {@code false} throw an * IOException. - * @throws NullPointerException if source or destination is {@code null}. + * @throws NullPointerException if any of the given {@code File}s are {@code null}. * @throws FileExistsException if the directory or file exists in the destination directory. * @throws IOException if source or destination is invalid. * @throws IOException if an error occurs or setting the last-modified time didn't succeeded. @@ -2246,36 +2237,26 @@ public class FileUtils { } /** - * Opens a {@link FileInputStream} for the specified file, providing better - * error messages than simply calling <code>new FileInputStream(file)</code>. + * Opens a {@link FileInputStream} for the specified file, providing better error messages than simply calling + * <code>new FileInputStream(file)</code>. * <p> - * At the end of the method either the stream will be successfully opened, - * or an exception will have been thrown. + * At the end of the method either the stream will be successfully opened, or an exception will have been thrown. * </p> * <p> - * An exception is thrown if the file does not exist. - * An exception is thrown if the file object exists but is a directory. - * An exception is thrown if the file exists but cannot be read. + * An exception is thrown if the file does not exist. An exception is thrown if the file object exists but is a + * directory. An exception is thrown if the file exists but cannot be read. * </p> * * @param file the file to open for input, must not be {@code null} * @return a new {@link FileInputStream} for the specified file - * @throws FileNotFoundException if the file does not exist - * @throws IOException if the file object is a directory - * @throws IOException if the file cannot be read + * @throws NullPointerException if file is {@code null}. + * @throws FileNotFoundException if the file does not exist, is a directory rather than a regular file, or for some + * other reason cannot be opened for reading. + * @throws IOException See FileNotFoundException above, FileNotFoundException is a subclass of IOException. * @since 1.3 */ public static FileInputStream openInputStream(final File file) throws IOException { - if (file.exists()) { - if (file.isDirectory()) { - throw new IOException("File '" + file + "' exists but is a directory"); - } - if (file.canRead() == false) { - throw new IOException("File '" + file + "' cannot be read"); - } - } else { - throw new FileNotFoundException("File '" + file + "' does not exist"); - } + Objects.requireNonNull(file, "file"); return new FileInputStream(file); } @@ -2324,26 +2305,19 @@ public class FileUtils { * @param append if {@code true}, then bytes will be added to the * end of the file rather than overwriting * @return a new {@link FileOutputStream} for the specified file - * @throws IOException if the file object is a directory - * @throws IOException if the file cannot be written to - * @throws IOException if a parent directory needs creating but that fails + * @throws NullPointerException if the file object is {@code null}. + * @throws IllegalArgumentException if the file object is a directory + * @throws IllegalArgumentException if the file is not writable. + * @throws IOException if the directories could not be created. * @since 2.1 */ public static FileOutputStream openOutputStream(final File file, final boolean append) throws IOException { + Objects.requireNonNull(file, "file"); if (file.exists()) { - if (file.isDirectory()) { - throw new IOException("File '" + file + "' exists but is a directory"); - } - if (file.canWrite() == false) { - throw new IOException("File '" + file + "' cannot be written to"); - } + requireFile(file, "file"); + requireCanWrite(file, "file"); } else { - final File parent = file.getParentFile(); - if (parent != null) { - if (!parent.mkdirs() && !parent.isDirectory()) { - throw new IOException("Directory '" + parent + "' could not be created"); - } - } + createParentDirectories(file); } return new FileOutputStream(file, append); } @@ -2354,7 +2328,10 @@ public class FileUtils { * * @param file the file to read, must not be {@code null} * @return the file contents, never {@code null} - * @throws IOException in case of an I/O error + * @throws NullPointerException if file is {@code null}. + * @throws FileNotFoundException if the file does not exist, is a directory rather than a regular file, or for some + * other reason cannot be opened for reading. + * @throws IOException if an I/O error occurs. * @since 1.1 */ public static byte[] readFileToByteArray(final File file) throws IOException { @@ -2371,7 +2348,10 @@ public class FileUtils { * * @param file the file to read, must not be {@code null} * @return the file contents, never {@code null} - * @throws IOException in case of an I/O error + * @throws NullPointerException if file is {@code null}. + * @throws FileNotFoundException if the file does not exist, is a directory rather than a regular file, or for some + * other reason cannot be opened for reading. + * @throws IOException if an I/O error occurs. * @since 1.3.1 * @deprecated 2.5 use {@link #readFileToString(File, Charset)} instead (and specify the appropriate encoding) */ @@ -2387,7 +2367,10 @@ public class FileUtils { * @param file the file to read, must not be {@code null} * @param charsetName the name of the requested charset, {@code null} means platform default * @return the file contents, never {@code null} - * @throws IOException in case of an I/O error + * @throws NullPointerException if file is {@code null}. + * @throws FileNotFoundException if the file does not exist, is a directory rather than a regular file, or for some + * other reason cannot be opened for reading. + * @throws IOException if an I/O error occurs. * @since 2.3 */ public static String readFileToString(final File file, final Charset charsetName) throws IOException { @@ -2402,9 +2385,12 @@ public class FileUtils { * @param file the file to read, must not be {@code null} * @param charsetName the name of the requested charset, {@code null} means platform default * @return the file contents, never {@code null} - * @throws IOException in case of an I/O error + * @throws NullPointerException if file is {@code null}. + * @throws FileNotFoundException if the file does not exist, is a directory rather than a regular file, or for some + * other reason cannot be opened for reading. + * @throws IOException if an I/O error occurs. * @throws java.nio.charset.UnsupportedCharsetException thrown instead of {@link java.io - * .UnsupportedEncodingException} in version 2.2 if the encoding is not supported. + * .UnsupportedEncodingException} in version 2.2 if the named charset is unavailable. * @since 2.3 */ public static String readFileToString(final File file, final String charsetName) throws IOException { @@ -2417,7 +2403,10 @@ public class FileUtils { * * @param file the file to read, must not be {@code null} * @return the list of Strings representing each line in the file, never {@code null} - * @throws IOException in case of an I/O error + * @throws NullPointerException if file is {@code null}. + * @throws FileNotFoundException if the file does not exist, is a directory rather than a regular file, or for some + * other reason cannot be opened for reading. + * @throws IOException if an I/O error occurs. * @since 1.3 * @deprecated 2.5 use {@link #readLines(File, Charset)} instead (and specify the appropriate encoding) */ @@ -2426,7 +2415,6 @@ public class FileUtils { return readLines(file, Charset.defaultCharset()); } - /** * Reads the contents of a file line by line to a List of Strings. * The file is always closed. @@ -2434,7 +2422,10 @@ public class FileUtils { * @param file the file to read, must not be {@code null} * @param charset the charset to use, {@code null} means platform default * @return the list of Strings representing each line in the file, never {@code null} - * @throws IOException in case of an I/O error + * @throws NullPointerException if file is {@code null}. + * @throws FileNotFoundException if the file does not exist, is a directory rather than a regular file, or for some + * other reason cannot be opened for reading. + * @throws IOException if an I/O error occurs. * @since 2.3 */ public static List<String> readLines(final File file, final Charset charset) throws IOException { @@ -2449,27 +2440,101 @@ public class FileUtils { * @param file the file to read, must not be {@code null} * @param charsetName the name of the requested charset, {@code null} means platform default * @return the list of Strings representing each line in the file, never {@code null} - * @throws IOException in case of an I/O error + * @throws NullPointerException if file is {@code null}. + * @throws FileNotFoundException if the file does not exist, is a directory rather than a regular file, or for some + * other reason cannot be opened for reading. + * @throws IOException if an I/O error occurs. * @throws java.nio.charset.UnsupportedCharsetException thrown instead of {@link java.io - * .UnsupportedEncodingException} in version 2.2 if the encoding is not supported. + * .UnsupportedEncodingException} in version 2.2 if the named charset is unavailable. * @since 1.1 */ public static List<String> readLines(final File file, final String charsetName) throws IOException { return readLines(file, Charsets.toCharset(charsetName)); } + private static void requireAbsent(final File file, String name) throws FileExistsException { + if (file.exists()) { + throw new FileExistsException( + String.format("File element in parameter '%s' already exists: '%s'", name, file)); + } + } + + /** - * Requires that the given {@code File} exists and is a directory. + * Throws IllegalArgumentException if the given files' canonical representations are equal. + * + * @param file1 The first file to compare. + * @param file2 The second file to compare. + * @throws IllegalArgumentException if the given files' canonical representations are equal. + */ + private static void requireCanonicalPathsNotEquals(final File file1, final File file2) throws IOException { + final String canonicalPath = file1.getCanonicalPath(); + if (canonicalPath.equals(file2.getCanonicalPath())) { + throw new IllegalArgumentException(String + .format("File canonical paths are equal: '%s' (file1='%s', file2='%s')", canonicalPath, file1, file2)); + } + } + + /** + * Throws an {@link IllegalArgumentException} if the file is not writable. + * + * @param file The file to test. + * @param name The parameter name to use in the exception message. + * @throws NullPointerException if the given {@code File} is {@code null}. + * @throws IllegalArgumentException if the file is not writable. + */ + private static void requireCanWrite(final File file, String name) { + Objects.requireNonNull(file, "file"); + if (!file.canWrite()) { + throw new IllegalArgumentException("File parameter '" + name + " is non-writable: '" + file + "'"); + } + } + + /** + * Requires that the given {@code File} is a directory. * * @param directory The {@code File} to check. - * @param param The param name to use in the exception message in case of null input. + * @param name * @return the given directory. + * @throws NullPointerException if the given {@code File} is {@code null}. * @throws IllegalArgumentException if the given {@code File} does not exist or is not a directory. */ - private static File requireDirectory(final File directory, String param) { - requireExists(directory, param); + private static File requireDirectory(final File directory, String name) { + Objects.requireNonNull(directory, name); if (!directory.isDirectory()) { - throw new IllegalArgumentException(directory + " is not a directory"); + throw new IllegalArgumentException("Parameter '" + name + "' is not a directory: '" + directory + "'"); + } + return directory; + } + + /** + * Requires that the given {@code File} exists and is a directory. + * + * @param directory The {@code File} to check. + * @param name The parameter name to use in the exception message in case of null input. + * @return the given directory. + * @throws NullPointerException if the given {@code File} is {@code null}. + * @throws IllegalArgumentException if the given {@code File} does not exist or is not a directory. + */ + private static File requireDirectoryExists(final File directory, String name) { + requireExists(directory, name); + requireDirectory(directory, name); + return directory; + } + + /** + * Requires that the given {@code File} is a directory if it exists. + * + * @param directory The {@code File} to check. + * @param name The parameter name to use in the exception message in case of null input. + * @return the given directory. + * @throws NullPointerException if the given {@code File} is {@code null}. + * @throws IllegalArgumentException if the given {@code File} exists but is not a directory. + */ + private static File requireDirectoryIfExists(final File directory, String name) { + Objects.requireNonNull(directory, name); + if (directory.exists()) { + requireDirectory(directory, name); } return directory; } @@ -2492,50 +2557,83 @@ public class FileUtils { } /** - * Requires that the given {@code File} exists. + * Requires that the given {@code File} exists and throws an {@link IllegalArgumentException} if it doesn't. * * @param file The {@code File} to check. - * @param param The param name to use in the exception message in case of null input. + * @param fileParamName The parameter name to use in the exception message in case of {@code null} input. * @return the given file. - * @throws IllegalArgumentException if the given {@code File} does not exist or is not a directory. + * @throws NullPointerException if the given {@code File} is {@code null}. + * @throws IllegalArgumentException if the given {@code File} does not exist. */ - private static File requireExists(final File file, String param) { - Objects.requireNonNull(file, param); + private static File requireExists(final File file, String fileParamName) { + Objects.requireNonNull(file, fileParamName); if (!file.exists()) { - throw new IllegalArgumentException(file + " does not exist"); + throw new IllegalArgumentException( + "File system element for parameter '" + fileParamName + "' does not exist: '" + file + "'"); } return file; } /** - * Requires that the given {@code File} exists and is a file. + * Requires that the given {@code File} exists and throws an {@link FileNotFoundException} if it doesn't. * * @param file The {@code File} to check. - * @param param The param name to use in the exception message in case of null input. + * @param fileParamName The parameter name to use in the exception message in case of {@code null} input. * @return the given file. + * @throws NullPointerException if the given {@code File} is {@code null}. + * @throws FileNotFoundException if the given {@code File} does not exist. + */ + private static File requireExistsChecked(final File file, String fileParamName) throws FileNotFoundException { + Objects.requireNonNull(file, fileParamName); + if (!file.exists()) { + throw new FileNotFoundException( + "File system element for parameter '" + fileParamName + "' does not exist: '" + file + "'"); + } + return file; + } + + /** + * Requires that the given {@code File} is a file. + * + * @param file The {@code File} to check. + * @param name The parameter name to use in the exception message. + * @return the given file. + * @throws NullPointerException if the given {@code File} is {@code null}. * @throws IllegalArgumentException if the given {@code File} does not exist or is not a directory. */ - private static File requireFile(final File file, String param) { - requireExists(file, param); + private static File requireFile(final File file, String name) { + Objects.requireNonNull(file, name); if (!file.isFile()) { - throw new IllegalArgumentException(file + " is not a file"); + throw new IllegalArgumentException("Parameter '" + name + "' is not a file: " + file); } return file; } /** - * Requires requirements for file copy. + * Requires parameter attributes for a file copy operation. * * @param source the source file * @param destination the destination - * @throws FileNotFoundException if the destination does not exist + * @throws NullPointerException if any of the given {@code File}s are {@code null}. + * @throws FileNotFoundException if the source does not exist. */ - private static void requireFileRequirements(final File source, final File destination) throws FileNotFoundException { - Objects.requireNonNull(source, "source"); - Objects.requireNonNull(destination, "target"); - if (!source.exists()) { - throw new FileNotFoundException("Source '" + source + "' does not exist"); - } + private static void requireFileCopy(final File source, final File destination) throws FileNotFoundException { + requireExistsChecked(source, "source"); + Objects.requireNonNull(destination, "destination"); + } + + /** + * Requires that the given {@code File} is a file if it exists. + * + * @param file The {@code File} to check. + * @param name The parameter name to use in the exception message in case of null input. + * @return the given directory. + * @throws NullPointerException if the given {@code File} is {@code null}. + * @throws IllegalArgumentException if the given {@code File} does exists but is not a directory. + */ + private static File requireFileIfExists(final File file, String name) { + Objects.requireNonNull(file, name); + return file.exists() ? requireFile(file, name) : file; } /** @@ -2543,11 +2641,27 @@ public class FileUtils { * * @param sourceFile The source file to query. * @param targetFile The target file to set. - * @throws IOException if an error occurs or setting the last-modified time didn't succeeded. + * @throws NullPointerException if sourceFile is {@code null}. + * @throws NullPointerException if targetFile is {@code null}. + * @throws IOException if setting the last-modified time failed. */ private static void setLastModified(final File sourceFile, final File targetFile) throws IOException { - if (!targetFile.setLastModified(sourceFile.lastModified())) { - throw new IOException("Failed setLastModified on " + sourceFile); + Objects.requireNonNull(sourceFile, "sourceFile"); + setLastModified(targetFile, sourceFile.lastModified()); + } + + /** + * Sets the given {@code targetFile}'s last modified date to the given value. + * + * @param file The source file to query. + * @param timeMillis The new last-modified time, measured in milliseconds since the epoch 01-01-1970 GMT. + * @throws NullPointerException if file is {@code null}. + * @throws IOException if setting the last-modified time failed. + */ + private static void setLastModified(final File file, final long timeMillis) throws IOException { + Objects.requireNonNull(file, "file"); + if (!file.setLastModified(timeMillis)) { + throw new IOException(String.format("Failed setLastModified(%s) on '%s'", timeMillis, file)); } } @@ -2576,18 +2690,18 @@ public class FileUtils { */ public static long sizeOf(final File file) { requireExists(file, "file"); - if (file.isDirectory()) { - return sizeOfDirectory0(file); // private method; expects directory - } - return file.length(); + return file.isDirectory() ? sizeOfDirectory0(file) : file.length(); } /** - * the size of a file - * @param file the file to check - * @return the size of the file + * Gets the size of a file. + * + * @param file the file to check. + * @return the size of the file. + * @throws NullPointerException if the file is {@code null}. */ private static long sizeOf0(final File file) { + Objects.requireNonNull(file, "file"); if (file.isDirectory()) { return sizeOfDirectory0(file); } @@ -2614,22 +2728,18 @@ public class FileUtils { */ public static BigInteger sizeOfAsBigInteger(final File file) { requireExists(file, "file"); - if (file.isDirectory()) { - return sizeOfDirectoryBig0(file); // internal method - } - return BigInteger.valueOf(file.length()); + return file.isDirectory() ? sizeOfDirectoryBig0(file) : BigInteger.valueOf(file.length()); } /** - * Returns the size of a file - * @param fileOrDir The file + * Returns the size of a file or directory. + * + * @param file The file or directory. * @return the size */ - private static BigInteger sizeOfBig0(final File fileOrDir) { - if (fileOrDir.isDirectory()) { - return sizeOfDirectoryBig0(fileOrDir); - } - return BigInteger.valueOf(fileOrDir.length()); + private static BigInteger sizeOfBig0(final File file) { + Objects.requireNonNull(file, "fileOrDir"); + return file.isDirectory() ? sizeOfDirectoryBig0(file) : BigInteger.valueOf(file.length()); } /** @@ -2646,15 +2756,18 @@ public class FileUtils { * @throws NullPointerException if the directory is {@code null}. */ public static long sizeOfDirectory(final File directory) { - return sizeOfDirectory0(requireDirectory(directory, "directory")); + return sizeOfDirectory0(requireDirectoryExists(directory, "directory")); } /** - * the size of a director + * Gets the size of a directory. + * * @param directory the directory to check * @return the size + * @throws NullPointerException if the directory is {@code null}. */ private static long sizeOfDirectory0(final File directory) { + Objects.requireNonNull(directory, "directory"); final File[] files = directory.listFiles(); if (files == null) { // null if security restricted return 0L; @@ -2663,7 +2776,7 @@ public class FileUtils { for (final File file : files) { if (!isSymlink(file)) { - size += sizeOf0(file); // internal method + size += sizeOf0(file); if (size < 0) { break; } @@ -2682,18 +2795,20 @@ public class FileUtils { * @since 2.4 */ public static BigInteger sizeOfDirectoryAsBigInteger(final File directory) { - return sizeOfDirectoryBig0(requireDirectory(directory, "directory")); + return sizeOfDirectoryBig0(requireDirectoryExists(directory, "directory")); } /** - * Finds the size of a directory + * Computes the size of a directory. * - * @param directory The directory - * @return the size + * @param directory The directory. + * @return the size. */ private static BigInteger sizeOfDirectoryBig0(final File directory) { + Objects.requireNonNull(directory, "directory"); final File[] files = directory.listFiles(); - if (files == null) { // null if security restricted + if (files == null) { + // null if security restricted return BigInteger.ZERO; } BigInteger size = BigInteger.ZERO; @@ -2721,18 +2836,13 @@ public class FileUtils { */ public static Stream<File> streamFiles(final File directory, final boolean recursive, final String... extensions) throws IOException { - final IOFileFilter filter; - if (extensions == null) { - filter = FileFileFilter.INSTANCE; - } else { - filter = FileFileFilter.INSTANCE.and(new SuffixFileFilter(toSuffixes(extensions))); - } - // We use filters that do not need file attributes so pass false. + final IOFileFilter filter = extensions == null ? FileFileFilter.INSTANCE + : FileFileFilter.INSTANCE.and(new SuffixFileFilter(toSuffixes(extensions))); return PathUtils.walk(directory.toPath(), filter, toMaxDepth(recursive), false).map(Path::toFile); } /** - * Convert from a <code>URL</code> to a <code>File</code>. + * Converts from a <code>URL</code> to a <code>File</code>. * <p> * From version 1.1 this method will decode the URL. * Syntax such as <code>file:///my%20docs/file.txt</code> will be @@ -2750,9 +2860,8 @@ public class FileUtils { if (url == null || !"file".equalsIgnoreCase(url.getProtocol())) { return null; } - String filename = url.getFile().replace('/', File.separatorChar); - filename = decodeUrl(filename); - return new File(filename); + final String filename = url.getFile().replace('/', File.separatorChar); + return new File(decodeUrl(filename)); } /** @@ -2784,9 +2893,8 @@ public class FileUtils { for (int i = 0; i < urls.length; i++) { final URL url = urls[i]; if (url != null) { - if (url.getProtocol().equals("file") == false) { - throw new IllegalArgumentException( - "URL could not be converted to a File: " + url); + if (!"file".equalsIgnoreCase(url.getProtocol())) { + throw new IllegalArgumentException("Can only convert file URL to a File: " + url); } files[i] = toFile(url); } @@ -2809,13 +2917,14 @@ public class FileUtils { } /** - * Converts an array of file extensions to suffixes for use - * with IOFileFilters. + * Converts an array of file extensions to suffixes. * * @param extensions an array of extensions. Format: {"java", "xml"} * @return an array of suffixes. Format: {".java", ".xml"} + * @throws NullPointerException */ private static String[] toSuffixes(final String... extensions) { + Objects.requireNonNull(extensions, "extensions"); final String[] suffixes = new String[extensions.length]; for (int i = 0; i < extensions.length; i++) { suffixes[i] = "." + extensions[i]; @@ -2833,17 +2942,16 @@ public class FileUtils { * creates parent directories if they do not exist. * </p> * - * @param file the File to touch - * @throws IOException If an I/O problem occurs + * @param file the File to touch. + * @throws IOException if an I/O problem occurs. + * @throws IOException if setting the last-modified time failed. */ public static void touch(final File file) throws IOException { + Objects.requireNonNull(file, "file"); if (!file.exists()) { openOutputStream(file).close(); } - final boolean success = file.setLastModified(System.currentTimeMillis()); - if (!success) { - throw new IOException("Unable to set the last modification time for " + file); - } + setLastModified(file, System.currentTimeMillis()); } /** @@ -2858,12 +2966,11 @@ public class FileUtils { * @throws NullPointerException if the parameter is null */ public static URL[] toURLs(final File... files) throws IOException { + Objects.requireNonNull(files, "files"); final URL[] urls = new URL[files.length]; - for (int i = 0; i < urls.length; i++) { urls[i] = files[i].toURI().toURL(); } - return urls; } @@ -2888,22 +2995,6 @@ public class FileUtils { } /** - * Lists files in a directory, asserting that the supplied directory satisfies exists and is a directory. - * - * @param directory The directory to list - * @return The files in the directory, never null. - * @throws IOException if an I/O error occurs - */ - private static File[] verifiedListFiles(final File directory) throws IOException { - requireDirectory(directory, "directory"); - final File[] files = directory.listFiles(); - if (files == null) { // null if security restricted - throw new IOException("Failed to list contents of " + directory); - } - return files; - } - - /** * Waits for NFS to propagate a file creation, imposing a timeout. * <p> * This method repeatedly tests {@link File#exists()} until it returns @@ -2916,6 +3007,7 @@ public class FileUtils { * @throws NullPointerException if the file is {@code null} */ public static boolean waitFor(final File file, final int seconds) { + Objects.requireNonNull(file, "file"); final long finishAt = System.currentTimeMillis() + (seconds * 1000L); boolean wasInterrupted = false; try { @@ -2996,8 +3088,7 @@ public class FileUtils { */ public static void write(final File file, final CharSequence data, final Charset charset, final boolean append) throws IOException { - final String str = data == null ? null : data.toString(); - writeStringToFile(file, str, charset, append); + writeStringToFile(file, Objects.toString(data, null), charset, append); } // Private method, must be invoked will a directory parameter @@ -3016,8 +3107,6 @@ public class FileUtils { write(file, data, charsetName, false); } - // Internal method - does not check existence - /** * Writes a CharSequence to a file creating the file if it does not exist. * @@ -3069,8 +3158,6 @@ public class FileUtils { writeByteArrayToFile(file, data, 0, data.length, append); } - // internal method; if file does not exist will return 0 - /** * Writes {@code len} bytes from the specified byte array starting * at offset {@code off} to a file, creating the file if it does @@ -3361,7 +3448,9 @@ public class FileUtils { /** * Instances should NOT be constructed in standard programming. + * @deprecated Will be private in 3.0. */ + @Deprecated public FileUtils() { //NOSONAR } diff --git a/src/test/java/org/apache/commons/io/FileUtilsCopyDirectoryToDirectoryTestCase.java b/src/test/java/org/apache/commons/io/FileUtilsCopyDirectoryToDirectoryTestCase.java index 3f94b39..da6bfc4 100644 --- a/src/test/java/org/apache/commons/io/FileUtilsCopyDirectoryToDirectoryTestCase.java +++ b/src/test/java/org/apache/commons/io/FileUtilsCopyDirectoryToDirectoryTestCase.java @@ -66,7 +66,8 @@ public class FileUtilsCopyDirectoryToDirectoryTestCase { srcDir.mkdir(); final File destDir = new File(temporaryFolder, "notadirectory"); destDir.createNewFile(); - final String expectedMessage = String.format("Destination '%s' is not a directory", destDir); + final String expectedMessage = String.format("Parameter 'destinationDir' is not a directory: '%s'", + destDir); assertExceptionTypeAndMessage(srcDir, destDir, IllegalArgumentException.class, expectedMessage); } @@ -76,7 +77,8 @@ public class FileUtilsCopyDirectoryToDirectoryTestCase { final File srcDir = File.createTempFile("notadireotry", null, temporaryFolder); final File destDir = new File(temporaryFolder, "destinationDirectory"); destDir.mkdirs(); - final String expectedMessage = String.format("Source '%s' is not a directory", srcDir); + final String expectedMessage = String.format("Parameter 'sourceDir' is not a directory: '%s'", + srcDir); assertExceptionTypeAndMessage(srcDir, destDir, IllegalArgumentException.class, expectedMessage); } diff --git a/src/test/java/org/apache/commons/io/FileUtilsTestCase.java b/src/test/java/org/apache/commons/io/FileUtilsTestCase.java index b8a3798..dad0628 100644 --- a/src/test/java/org/apache/commons/io/FileUtilsTestCase.java +++ b/src/test/java/org/apache/commons/io/FileUtilsTestCase.java @@ -327,11 +327,7 @@ public class FileUtilsTestCase { public void test_openInputStream_existsButIsDirectory() throws Exception { final File directory = new File(temporaryFolder, "subdir"); directory.mkdirs(); - try (FileInputStream in = FileUtils.openInputStream(directory)) { - fail(); - } catch (final IOException ioe) { - // expected - } + assertThrows(IOException.class, () -> FileUtils.openInputStream(directory)); } @Test @@ -358,11 +354,7 @@ public class FileUtilsTestCase { public void test_openOutputStream_existsButIsDirectory() throws Exception { final File directory = new File(temporaryFolder, "subdir"); directory.mkdirs(); - try (FileOutputStream out = FileUtils.openOutputStream(directory)) { - fail(); - } catch (final IOException ioe) { - // expected - } + assertThrows(IllegalArgumentException.class, () -> FileUtils.openOutputStream(directory)); } @Test @@ -593,12 +585,7 @@ public class FileUtilsTestCase { assertTrue(FileUtils.contentEquals(file2, file)); // Directories - try { - FileUtils.contentEquals(temporaryFolder, temporaryFolder); - fail("Comparing directories should fail with an IOException"); - } catch (final IOException ioe) { - //expected - } + assertThrows(IllegalArgumentException.class, () -> FileUtils.contentEquals(temporaryFolder, temporaryFolder)); // Different files final File objFile1 = @@ -652,12 +639,8 @@ public class FileUtilsTestCase { assertTrue(FileUtils.contentEqualsIgnoreEOL(file2, file1, null)); // Directories - try { - FileUtils.contentEqualsIgnoreEOL(temporaryFolder, temporaryFolder, null); - fail("Comparing directories should fail with an IOException"); - } catch (final IOException ioe) { - //expected - } + assertThrows(IllegalArgumentException.class, + () -> FileUtils.contentEqualsIgnoreEOL(temporaryFolder, temporaryFolder, null)); // Different files final File tfile1 = new File(temporaryFolder, getName() + ".txt1"); @@ -720,42 +703,22 @@ public class FileUtilsTestCase { } @Test - public void testCopyDirectoryErrors() throws Exception { - try { - FileUtils.copyDirectory(null, null); - fail(); - } catch (final NullPointerException ignore) { - } - try { - FileUtils.copyDirectory(new File("a"), null); - fail(); - } catch (final NullPointerException ignore) { - } - try { - FileUtils.copyDirectory(null, new File("a")); - fail(); - } catch (final NullPointerException ignore) { - } - try { - FileUtils.copyDirectory(new File("doesnt-exist"), new File("a")); - fail(); - } catch (final IOException ignore) { - } - try { - FileUtils.copyDirectory(testFile1, new File("a")); - fail(); - } catch (final IOException ignore) { - } - try { - FileUtils.copyDirectory(temporaryFolder, testFile1); - fail(); - } catch (final IOException ignore) { - } - try { - FileUtils.copyDirectory(temporaryFolder, temporaryFolder); - fail(); - } catch (final IOException ignore) { - } + public void testCopyDirectoryExceptions() throws Exception { + // + // NullPointerException + assertThrows(NullPointerException.class, () -> FileUtils.copyDirectory(null, null)); + assertThrows(NullPointerException.class, () -> FileUtils.copyDirectory(null, testFile1)); + assertThrows(NullPointerException.class, () -> FileUtils.copyDirectory(testFile1, null)); + assertThrows(NullPointerException.class, () -> FileUtils.copyDirectory(null, new File("a"))); + // + // IllegalArgumentException + assertThrows(IllegalArgumentException.class, () -> FileUtils.copyDirectory(testFile1, new File("a"))); + assertThrows(IllegalArgumentException.class, () -> FileUtils.copyDirectory(testFile1, new File("a"))); + assertThrows(IllegalArgumentException.class, () -> FileUtils.copyDirectory(temporaryFolder, temporaryFolder)); + // + // IOException + assertThrows(IOException.class, () -> FileUtils.copyDirectory(new File("doesnt-exist"), new File("a"))); + assertThrows(IllegalArgumentException.class, () -> FileUtils.copyDirectory(temporaryFolder, testFile1)); } @Test @@ -1057,8 +1020,7 @@ public class FileUtilsTestCase { assertEquals(testFile1Size, destination.length(), "Check Full copy"); assertEquals(testFile1.lastModified(), destination.lastModified(), "Check last modified date preserved"); - assertThrows(IOException.class, - () -> FileUtils.copyFileToDirectory(destination, directory), + assertThrows(IllegalArgumentException.class, () -> FileUtils.copyFileToDirectory(destination, directory), "Should not be able to copy a file into the same directory as itself"); } @@ -1245,13 +1207,7 @@ public class FileUtilsTestCase { final File destination = new File(temporaryFolder, "copy3.txt"); //Prepare a test file FileUtils.copyFile(testFile1, destination); - - try { - FileUtils.copyFile(destination, destination); - fail("file copy to self should not be possible"); - } catch (final IOException ioe) { - //we want the exception, copy to self should be illegal - } + assertThrows(IllegalArgumentException.class, () -> FileUtils.copyFile(destination, destination)); } @Test @@ -1501,11 +1457,7 @@ public class FileUtilsTestCase { assertTrue(testFile.exists(), "Test file does not exist."); // Tests with existing file - try { - FileUtils.forceMkdir(testFile); - fail("Exception expected."); - } catch (final IOException ignore) { - } + assertThrows(IOException.class, () -> FileUtils.forceMkdir(testFile)); testFile.delete(); @@ -1774,6 +1726,7 @@ public class FileUtilsTestCase { assertTrue(FileUtils.isFileOlder(newFile, localDatePlusDay, localTime), "New File - Older - LocalDate plus one day,LocalTime"); assertFalse(FileUtils.isFileOlder(invalidFile, reference), "Invalid - Older - File"); + assertThrows(IllegalArgumentException.class, () -> FileUtils.isFileOlder(newFile, invalidFile)); try { FileUtils.isFileOlder(newFile, invalidFile); fail("Should have cause IllegalArgumentException"); @@ -1785,71 +1738,29 @@ public class FileUtilsTestCase { // ----- Test isFileNewer() exceptions ----- // Null File - try { - FileUtils.isFileNewer(null, now); - fail("Newer Null, expected NullPointerException"); - } catch (final NullPointerException expected) { - // expected result - } + assertThrows(NullPointerException.class, () -> FileUtils.isFileNewer(null, now)); // Null reference File - try { - FileUtils.isFileNewer(oldFile, (File) null); - fail("Newer Null reference, expected NullPointerException"); - } catch (final NullPointerException ignore) { - // expected result - } + assertThrows(NullPointerException.class, () -> FileUtils.isFileNewer(oldFile, (File) null)); // Invalid reference File - try { - FileUtils.isFileNewer(oldFile, invalidFile); - fail("Newer invalid reference, expected IllegalArgumentException"); - } catch (final IllegalArgumentException ignore) { - // expected result - } + assertThrows(IllegalArgumentException.class, () -> FileUtils.isFileNewer(oldFile, invalidFile)); // Null reference Date - try { - FileUtils.isFileNewer(oldFile, (Date) null); - fail("Newer Null date, expected NullPointerException"); - } catch (final NullPointerException ignore) { - // expected result - } - + assertThrows(NullPointerException.class, () -> FileUtils.isFileNewer(oldFile, (Date) null)); // ----- Test isFileOlder() exceptions ----- // Null File - try { - FileUtils.isFileOlder(null, now); - fail("Older Null, expected NullPointerException"); - } catch (final NullPointerException ignore) { - // expected result - } + assertThrows(NullPointerException.class, () -> FileUtils.isFileOlder(null, now)); // Null reference File - try { - FileUtils.isFileOlder(oldFile, (File) null); - fail("Older Null reference, expected NullPointerException"); - } catch (final NullPointerException ignore) { - // expected result - } - - // Invalid reference File - try { - FileUtils.isFileOlder(oldFile, invalidFile); - fail("Older invalid reference, expected IllegalArgumentException"); - } catch (final IllegalArgumentException ignore) { - // expected result - } + assertThrows(NullPointerException.class, () -> FileUtils.isFileOlder(oldFile, (File) null)); // Null reference Date - try { - FileUtils.isFileOlder(oldFile, (Date) null); - fail("Older Null date, expected NullPointerException"); - } catch (final NullPointerException ignore) { - // expected result - } + assertThrows(NullPointerException.class, () -> FileUtils.isFileOlder(oldFile, (Date) null)); + // Invalid reference File + assertThrows(IllegalArgumentException.class, () -> FileUtils.isFileOlder(oldFile, invalidFile)); } @Test @@ -2094,18 +2005,8 @@ public class FileUtilsTestCase { @Test public void testMoveDirectory_Errors() throws Exception { - try { - FileUtils.moveDirectory(null, new File("foo")); - fail("Expected NullPointerException when source is null"); - } catch (final NullPointerException e) { - // expected - } - try { - FileUtils.moveDirectory(new File("foo"), null); - fail("Expected NullPointerException when destination is null"); - } catch (final NullPointerException e) { - // expected - } + assertThrows(NullPointerException.class, () -> FileUtils.moveDirectory(null, new File("foo"))); + assertThrows(NullPointerException.class, () -> FileUtils.moveDirectory(new File("foo"), null)); try { FileUtils.moveDirectory(new File("nonexistant"), new File("foo")); fail("Expected FileNotFoundException for source"); @@ -2124,12 +2025,7 @@ public class FileUtilsTestCase { } finally { IOUtils.closeQuietly(output); } - try { - FileUtils.moveDirectory(testFile, new File("foo")); - fail("Expected IOException when source is not a directory"); - } catch (final IOException e) { - // expected - } + assertThrows(IllegalArgumentException.class, () -> FileUtils.moveDirectory(testFile, new File("foo"))); final File testSrcFile = new File(temporaryFolder, "testMoveDirectorySource"); final File testDestFile = new File(temporaryFolder, "testMoveDirectoryDest"); testSrcFile.mkdir(); @@ -2213,37 +2109,23 @@ public class FileUtilsTestCase { @Test public void testMoveDirectoryToDirectory_Errors() throws Exception { - try { - FileUtils.moveDirectoryToDirectory(null, new File("foo"), true); - fail("Expected NullPointerException when source is null"); - } catch (final NullPointerException e) { - // expected - } - try { - FileUtils.moveDirectoryToDirectory(new File("foo"), null, true); - fail("Expected NullPointerException when destination is null"); - } catch (final NullPointerException e) { - // expected - } + assertThrows(NullPointerException.class, () -> FileUtils.moveDirectoryToDirectory(null, new File("foo"), true)); + assertThrows(NullPointerException.class, () -> FileUtils.moveDirectoryToDirectory(new File("foo"), null, true)); final File testFile1 = new File(temporaryFolder, "testMoveFileFile1"); final File testFile2 = new File(temporaryFolder, "testMoveFileFile2"); if (!testFile1.getParentFile().exists()) { - throw new IOException("Cannot create file " + testFile1 - + " as the parent directory does not exist"); + throw new IOException("Cannot create file " + testFile1 + " as the parent directory does not exist"); } - final BufferedOutputStream output1 = - new BufferedOutputStream(new FileOutputStream(testFile1)); + final BufferedOutputStream output1 = new BufferedOutputStream(new FileOutputStream(testFile1)); try { TestUtils.generateTestData(output1, 0); } finally { IOUtils.closeQuietly(output1); } if (!testFile2.getParentFile().exists()) { - throw new IOException("Cannot create file " + testFile2 - + " as the parent directory does not exist"); + throw new IOException("Cannot create file " + testFile2 + " as the parent directory does not exist"); } - final BufferedOutputStream output = - new BufferedOutputStream(new FileOutputStream(testFile2)); + final BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(testFile2)); try { TestUtils.generateTestData(output, 0); } finally { @@ -2303,42 +2185,23 @@ public class FileUtilsTestCase { } }; - try { - FileUtils.moveFile(src, destination); - fail("move should have failed as src has not been deleted"); - } catch (final IOException e) { - // exepected - assertTrue(!destination.exists(), "Check Rollback"); - assertTrue(src.exists(), "Original exists"); - } + assertThrows(IOException.class, () -> FileUtils.moveFile(src, destination)); + // expected + assertTrue(!destination.exists(), "Check Rollback"); + assertTrue(src.exists(), "Original exists"); } @Test public void testMoveFile_Errors() throws Exception { - try { - FileUtils.moveFile(null, new File("foo")); - fail("Expected NullPointerException when source is null"); - } catch (final NullPointerException e) { - // expected - } - try { - FileUtils.moveFile(new File("foo"), null); - fail("Expected NullPointerException when destination is null"); - } catch (final NullPointerException e) { - // expected - } + assertThrows(NullPointerException.class, () -> FileUtils.moveFile(null, new File("foo"))); + assertThrows(NullPointerException.class, () -> FileUtils.moveFile(new File("foo"), null)); try { FileUtils.moveFile(new File("nonexistant"), new File("foo")); fail("Expected FileNotFoundException for source"); } catch (final FileNotFoundException e) { // expected } - try { - FileUtils.moveFile(temporaryFolder, new File("foo")); - fail("Expected IOException when source is a directory"); - } catch (final IOException e) { - // expected - } + assertThrows(IllegalArgumentException.class, () -> FileUtils.moveFile(temporaryFolder, new File("foo"))); final File testSourceFile = new File(temporaryFolder, "testMoveFileSource"); final File testDestFile = new File(temporaryFolder, "testMoveFileSource"); if (!testSourceFile.getParentFile().exists()) { @@ -2394,18 +2257,8 @@ public class FileUtilsTestCase { @Test public void testMoveFileToDirectory_Errors() throws Exception { - try { - FileUtils.moveFileToDirectory(null, new File("foo"), true); - fail("Expected NullPointerException when source is null"); - } catch (final NullPointerException e) { - // expected - } - try { - FileUtils.moveFileToDirectory(new File("foo"), null, true); - fail("Expected NullPointerException when destination is null"); - } catch (final NullPointerException e) { - // expected - } + assertThrows(NullPointerException.class, () -> FileUtils.moveFileToDirectory(null, new File("foo"), true)); + assertThrows(NullPointerException.class, () -> FileUtils.moveFileToDirectory(new File("foo"), null, true)); final File testFile1 = new File(temporaryFolder, "testMoveFileFile1"); final File testFile2 = new File(temporaryFolder, "testMoveFileFile2"); if (!testFile1.getParentFile().exists()) { @@ -2430,12 +2283,7 @@ public class FileUtilsTestCase { } finally { IOUtils.closeQuietly(output); } - try { - FileUtils.moveFileToDirectory(testFile1, testFile2, true); - fail("Expected IOException when dest not a directory"); - } catch (final IOException e) { - // expected - } + assertThrows(IllegalArgumentException.class, () -> FileUtils.moveFileToDirectory(testFile1, testFile2, true)); final File nonexistant = new File(temporaryFolder, "testMoveFileNonExistant"); try {