com.therouter.plugin.utils.FileUtils Maven / Gradle / Ivy
package com.therouter.plugin.utils;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
/**
* General file manipulation utilities.
*
* Facilities are provided in the following areas:
*
* - writing to a file
*
- reading from a file
*
- make a directory including parent directories
*
- copying files and directories
*
- deleting files and directories
*
- converting to and from a URL
*
- listing files and directories by filter and extension
*
- comparing file content
*
- file last changed date
*
- calculating a checksum
*
*
* 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.
*
* Origin of code: Excalibur, Alexandria, Commons-Utils
*/
public class FileUtils {
/**
* Instances should NOT be constructed in standard programming.
*/
public FileUtils() {
super();
}
/**
* The number of bytes in a kilobyte.
*/
public static final long ONE_KB = 1024;
/**
* The number of bytes in a megabyte.
*/
public static final long ONE_MB = ONE_KB * ONE_KB;
/**
* The file copy buffer size (30 MB)
*/
private static final long FILE_COPY_BUFFER_SIZE = ONE_MB * 30;
//-----------------------------------------------------------------------
/**
* Opens a {@link FileOutputStream} for the specified file, checking and
* creating the parent directory if it does not exist.
*
* At the end of the method either the stream will be successfully opened,
* or an exception will have been thrown.
*
* The parent directory will be created if it does not exist.
* The file will be created if it 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 written to.
* An exception is thrown if the parent directory cannot be created.
*
* @param file the file to open for output, must not be {@code null}
* @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
* @since 1.3
*/
public static FileOutputStream openOutputStream(final File file) throws IOException {
return openOutputStream(file, false);
}
/**
* Opens a {@link FileOutputStream} for the specified file, checking and
* creating the parent directory if it does not exist.
*
* At the end of the method either the stream will be successfully opened,
* or an exception will have been thrown.
*
* The parent directory will be created if it does not exist.
* The file will be created if it 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 written to.
* An exception is thrown if the parent directory cannot be created.
*
* @param file the file to open for output, must not be {@code null}
* @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
* @since 2.1
*/
public static FileOutputStream openOutputStream(final File file, final boolean append) throws IOException {
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");
}
} else {
final File parent = file.getParentFile();
if (parent != null) {
if (!parent.mkdirs() && !parent.isDirectory()) {
throw new IOException("Directory '" + parent + "' could not be created");
}
}
}
return new FileOutputStream(file, append);
}
//-----------------------------------------------------------------------
/**
* Decodes the specified URL as per RFC 3986, i.e. transforms
* percent-encoded octets to characters by decoding with the UTF-8 character
* set. This function is primarily intended for usage with
* {@link java.net.URL} which unfortunately does not enforce proper URLs. As
* such, this method will leniently accept invalid characters or malformed
* percent-encoded octets and simply pass them literally through to the
* result string. Except for rare edge cases, this will make unencoded URLs
* pass through unaltered.
*
* @param url The URL to decode, may be {@code null}.
* @return The decoded URL or {@code null} if the input was
* {@code null}.
*/
static String decodeUrl(final String url) {
String decoded = url;
if (url != null && url.indexOf('%') >= 0) {
final int n = url.length();
final StringBuilder buffer = new StringBuilder();
final ByteBuffer bytes = ByteBuffer.allocate(n);
for (int i = 0; i < n; ) {
if (url.charAt(i) == '%') {
try {
do {
final byte octet = (byte) Integer.parseInt(url.substring(i + 1, i + 3), 16);
bytes.put(octet);
i += 3;
} while (i < n && url.charAt(i) == '%');
continue;
} catch (final RuntimeException e) {
// malformed percent-encoded octet, fall through and
// append characters literally
} finally {
if (bytes.position() > 0) {
bytes.flip();
buffer.append(StandardCharsets.UTF_8.decode(bytes).toString());
bytes.clear();
}
}
}
buffer.append(url.charAt(i++));
}
decoded = buffer.toString();
}
return decoded;
}
//-----------------------------------------------------------------------
/**
* Copies a file to a directory preserving the file date.
*
* This method copies the contents of the specified source file
* to a file of the same name in the specified destination directory.
* The destination directory is created if it does not exist.
* If the destination file exists, then this method will overwrite it.
*
* Note: This method tries to preserve the file's last
* modified date/times using {@link File#setLastModified(long)}, however
* it is not guaranteed that the operation will succeed.
* If the modification operation fails, no indication is provided.
*
* @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 IOException if an IO error occurs during copying
* @see #copyFile(File, File, boolean)
*/
public static void copyFileToDirectory(final File srcFile, final File destDir) throws IOException {
copyFileToDirectory(srcFile, destDir, true);
}
/**
* Copies a file to a directory optionally preserving the file date.
*
* This method copies the contents of the specified source file
* to a file of the same name in the specified destination directory.
* The destination directory is created if it does not exist.
* If the destination file exists, then this method will overwrite it.
*
* Note: Setting preserveFileDate
to
* {@code true} tries to preserve the file's last modified
* date/times using {@link File#setLastModified(long)}, however it is
* not guaranteed that the operation will succeed.
* If the modification operation fails, no indication is provided.
*
* @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}
* @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 IOException if an IO error occurs during copying
* @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)
* @since 1.3
*/
public static void copyFileToDirectory(final File srcFile, final File destDir, final boolean preserveFileDate)
throws IOException {
if (destDir == null) {
throw new NullPointerException("Destination must not be null");
}
if (destDir.exists() && destDir.isDirectory() == false) {
throw new IllegalArgumentException("Destination '" + destDir + "' is not a directory");
}
final File destFile = new File(destDir, srcFile.getName());
copyFile(srcFile, destFile, preserveFileDate);
}
/**
* Copies a file to a new location preserving the file date.
*
* 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 will overwrite it.
*
* Note: This method tries to preserve the file's last
* modified date/times using {@link File#setLastModified(long)}, however
* it is not guaranteed that the operation will succeed.
* If the modification operation fails, no indication is provided.
*
* @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 IOException if source or destination is invalid
* @throws IOException if an IO error occurs during copying
* @throws IOException if the output file length is not the same as the input file length after the copy
* completes
* @see #copyFileToDirectory(File, File)
* @see #copyFile(File, File, boolean)
*/
public static void copyFile(final File srcFile, final File destFile) throws IOException {
copyFile(srcFile, destFile, true);
}
/**
* Copies a file to a new location.
*
* 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 will overwrite it.
*
* Note: Setting preserveFileDate
to
* {@code true} tries to preserve the file's last modified
* date/times using {@link File#setLastModified(long)}, however it is
* not guaranteed that the operation will succeed.
* If the modification operation fails, no indication is provided.
*
* @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 IOException if source or destination is invalid
* @throws IOException if an IO error occurs during copying
* @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 #doCopyFile(File, File, boolean)
*/
public static void copyFile(final File srcFile, final File destFile,
final boolean preserveFileDate) throws IOException {
checkFileRequirements(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");
}
doCopyFile(srcFile, destFile, preserveFileDate);
}
/**
* Internal copy file method.
* This caches the original file length, and throws an IOException
* if the output file length is different from the current input file length.
* So it may fail if the file changes size.
* It may also fail with "IllegalArgumentException: Negative size" if the input file is truncated part way
* through copying the data and the new file size is less than the current position.
*
* @param srcFile the validated source file, must not be {@code null}
* @param destFile the validated destination file, must not be {@code null}
* @param preserveFileDate whether to preserve the file date
* @throws IOException if an error occurs
* @throws IOException if the output file length is not the same as the input file length after the
* copy completes
* @throws IllegalArgumentException "Negative size" if the file is truncated so that the size is less than the
* position
*/
private static void doCopyFile(final File srcFile, final File destFile, final boolean preserveFileDate)
throws IOException {
if (destFile.exists() && destFile.isDirectory()) {
throw new IOException("Destination '" + destFile + "' exists but is a directory");
}
try (FileInputStream fis = new FileInputStream(srcFile);
FileChannel input = fis.getChannel();
FileOutputStream fos = new FileOutputStream(destFile);
FileChannel output = fos.getChannel()) {
final long size = input.size(); // TODO See IO-386
long pos = 0;
long count = 0;
while (pos < size) {
final long remain = size - pos;
count = remain > FILE_COPY_BUFFER_SIZE ? FILE_COPY_BUFFER_SIZE : remain;
final long bytesCopied = output.transferFrom(input, pos, count);
if (bytesCopied == 0) { // IO-385 - can happen if file is truncated after caching the size
break; // ensure we don't loop forever
}
pos += bytesCopied;
}
}
final long srcLen = srcFile.length(); // TODO See IO-386
final long dstLen = destFile.length(); // TODO See IO-386
if (srcLen != dstLen) {
throw new IOException("Failed to copy full contents from '" +
srcFile + "' to '" + destFile + "' Expected length: " + srcLen + " Actual: " + dstLen);
}
if (preserveFileDate) {
destFile.setLastModified(srcFile.lastModified());
}
}
//-----------------------------------------------------------------------
/**
* Copies a whole directory to a new location preserving the file dates.
*
* This method copies the specified directory and all its child
* directories and files to the specified destination.
* The destination is the new location and name of the directory.
*
* The destination directory is created if it does not exist.
* If the destination directory did exist, then this method merges
* the source with the destination, with the source taking precedence.
*
* Note: This method tries to preserve the files' last
* modified date/times using {@link File#setLastModified(long)}, however
* it is not guaranteed that those operations will succeed.
* If the modification operation fails, no indication is provided.
*
* @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 IOException if an IO error occurs during copying
* @since 1.1
*/
public static void copyDirectory(final File srcDir, final File destDir) throws IOException {
copyDirectory(srcDir, destDir, true);
}
/**
* Copies a whole directory to a new location.
*
* This method copies the contents of the specified source directory
* to within the specified destination directory.
*
* The destination directory is created if it does not exist.
* If the destination directory did exist, then this method merges
* the source with the destination, with the source taking precedence.
*
* Note: Setting preserveFileDate
to
* {@code true} tries to preserve the files' last modified
* date/times using {@link File#setLastModified(long)}, however it is
* not guaranteed that those operations will succeed.
* If the modification operation fails, no indication is provided.
*
* @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 IOException if an IO error occurs during copying
* @since 1.1
*/
public static void copyDirectory(final File srcDir, final File destDir,
final boolean preserveFileDate) throws IOException {
copyDirectory(srcDir, destDir, null, preserveFileDate);
}
/**
* Copies a filtered directory to a new location preserving the file dates.
*
* This method copies the contents of the specified source directory
* to within the specified destination directory.
*
* The destination directory is created if it does not exist.
* If the destination directory did exist, then this method merges
* the source with the destination, with the source taking precedence.
*
* Note: This method tries to preserve the files' last
* modified date/times using {@link File#setLastModified(long)}, however
* it is not guaranteed that those operations will succeed.
* If the modification operation fails, no indication is provided.
*
*
* // only copy the directory structure
* FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY);
*
*
*
* // Create a filter for ".txt" files
* IOFileFilter txtSuffixFilter = FileFilterUtils.suffixFileFilter(".txt");
* IOFileFilter txtFiles = FileFilterUtils.andFileFilter(FileFileFilter.FILE, txtSuffixFilter);
*
* // Create a filter for either directories or ".txt" files
* FileFilter filter = FileFilterUtils.orFileFilter(DirectoryFileFilter.DIRECTORY, txtFiles);
*
* // Copy using the filter
* FileUtils.copyDirectory(srcDir, destDir, filter);
*
*
* @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 IOException if an IO error occurs during copying
* @since 1.4
*/
public static void copyDirectory(final File srcDir, final File destDir,
final FileFilter filter) throws IOException {
copyDirectory(srcDir, destDir, filter, true);
}
/**
* Copies a filtered directory to a new location.
*
* This method copies the contents of the specified source directory
* to within the specified destination directory.
*
* The destination directory is created if it does not exist.
* If the destination directory did exist, then this method merges
* the source with the destination, with the source taking precedence.
*
* Note: Setting preserveFileDate
to
* {@code true} tries to preserve the files' last modified
* date/times using {@link File#setLastModified(long)}, however it is
* not guaranteed that those operations will succeed.
* If the modification operation fails, no indication is provided.
*
*
* // only copy the directory structure
* FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY, false);
*
*
*
* // Create a filter for ".txt" files
* IOFileFilter txtSuffixFilter = FileFilterUtils.suffixFileFilter(".txt");
* IOFileFilter txtFiles = FileFilterUtils.andFileFilter(FileFileFilter.FILE, txtSuffixFilter);
*
* // Create a filter for either directories or ".txt" files
* FileFilter filter = FileFilterUtils.orFileFilter(DirectoryFileFilter.DIRECTORY, txtFiles);
*
* // Copy using the filter
* FileUtils.copyDirectory(srcDir, destDir, filter, false);
*
*
* @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 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 IOException if an IO error occurs during copying
* @since 1.4
*/
public static void copyDirectory(final File srcDir, final File destDir,
final FileFilter filter, final boolean preserveFileDate) throws IOException {
checkFileRequirements(srcDir, destDir);
if (!srcDir.isDirectory()) {
throw new IOException("Source '" + srcDir + "' exists but is not a directory");
}
if (srcDir.getCanonicalPath().equals(destDir.getCanonicalPath())) {
throw new IOException("Source '" + srcDir + "' and destination '" + destDir + "' are the same");
}
// Cater for destination being directory within the source directory (see IO-141)
List exclusionList = null;
if (destDir.getCanonicalPath().startsWith(srcDir.getCanonicalPath())) {
final File[] srcFiles = filter == null ? srcDir.listFiles() : srcDir.listFiles(filter);
if (srcFiles != null && srcFiles.length > 0) {
exclusionList = new ArrayList<>(srcFiles.length);
for (final File srcFile : srcFiles) {
final File copiedFile = new File(destDir, srcFile.getName());
exclusionList.add(copiedFile.getCanonicalPath());
}
}
}
doCopyDirectory(srcDir, destDir, filter, preserveFileDate, exclusionList);
}
/**
* checks requirements for file copy
*
* @param src the source file
* @param dest the destination
* @throws FileNotFoundException if the destination does not exist
*/
private static void checkFileRequirements(final File src, final File dest) throws FileNotFoundException {
if (src == null) {
throw new NullPointerException("Source must not be null");
}
if (dest == null) {
throw new NullPointerException("Destination must not be null");
}
if (!src.exists()) {
throw new FileNotFoundException("Source '" + src + "' does not exist");
}
}
/**
* Internal copy directory method.
*
* @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 preserveFileDate whether to preserve the file date
* @param exclusionList List of files and directories to exclude from the copy, may be null
* @throws IOException if an error occurs
* @since 1.1
*/
private static void doCopyDirectory(final File srcDir, final File destDir, final FileFilter filter,
final boolean preserveFileDate, final List exclusionList)
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");
}
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);
} else {
doCopyFile(srcFile, dstFile, preserveFileDate);
}
}
}
// Do this last, as the above has probably affected directory metadata
if (preserveFileDate) {
destDir.setLastModified(srcDir.lastModified());
}
}
/**
* Deletes a directory recursively.
*
* @param directory directory to delete
* @throws IOException in case deletion is unsuccessful
* @throws IllegalArgumentException if {@code directory} does not exist or is not a directory
*/
public static void deleteDirectory(final File directory) throws IOException {
if (!directory.exists()) {
return;
}
if (!isSymlink(directory)) {
cleanDirectory(directory);
}
if (!directory.delete()) {
final String message =
"Unable to delete directory " + directory + ".";
throw new IOException(message);
}
}
/**
* 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
*/
public static void cleanDirectory(final File directory) throws IOException {
final File[] files = verifiedListFiles(directory);
IOException exception = null;
for (final File file : files) {
try {
forceDelete(file);
} catch (final IOException ioe) {
exception = ioe;
}
}
if (null != exception) {
throw exception;
}
}
/**
* 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 {
if (!directory.exists()) {
final String message = directory + " does not exist";
throw new IllegalArgumentException(message);
}
if (!directory.isDirectory()) {
final String message = directory + " is not a directory";
throw new IllegalArgumentException(message);
}
final File[] files = directory.listFiles();
if (files == null) { // null if security restricted
throw new IOException("Failed to list contents of " + directory);
}
return files;
}
/**
* Deletes a file. If file is a directory, delete it and all sub-directories.
*
* The difference between File.delete() and this method are:
*
* - A directory to be deleted does not have to be empty.
* - You get exceptions when a file or directory cannot be deleted.
* (java.io.File methods returns a boolean)
*
*
* @param file file or directory to delete, must not be {@code null}
* @throws NullPointerException if the directory is {@code null}
* @throws FileNotFoundException if the file was not found
* @throws IOException in case deletion is unsuccessful
*/
public static void forceDelete(final File file) throws IOException {
if (file.isDirectory()) {
deleteDirectory(file);
} else {
final boolean filePresent = file.exists();
if (!file.delete()) {
if (!filePresent) {
throw new FileNotFoundException("File does not exist: " + file);
}
final String message =
"Unable to delete file: " + file;
throw new IOException(message);
}
}
}
/**
* Schedules a file to be deleted when JVM exits.
* If file is directory delete it and all sub-directories.
*
* @param file file or directory to delete, must not be {@code null}
* @throws NullPointerException if the file is {@code null}
* @throws IOException in case deletion is unsuccessful
*/
public static void forceDeleteOnExit(final File file) throws IOException {
if (file.isDirectory()) {
deleteDirectoryOnExit(file);
} else {
file.deleteOnExit();
}
}
/**
* Schedules a directory recursively for deletion on JVM exit.
*
* @param directory directory to delete, must not be {@code null}
* @throws NullPointerException if the directory is {@code null}
* @throws IOException in case deletion is unsuccessful
*/
private static void deleteDirectoryOnExit(final File directory) throws IOException {
if (!directory.exists()) {
return;
}
directory.deleteOnExit();
if (!isSymlink(directory)) {
cleanDirectoryOnExit(directory);
}
}
/**
* 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
*/
private static void cleanDirectoryOnExit(final File directory) throws IOException {
final File[] files = verifiedListFiles(directory);
IOException exception = null;
for (final File file : files) {
try {
forceDeleteOnExit(file);
} catch (final IOException ioe) {
exception = ioe;
}
}
if (null != exception) {
throw exception;
}
}
/**
* Makes a directory, including any necessary but nonexistent parent
* directories. If a file already exists with specified name but it is
* not a directory then an IOException is thrown.
* If the directory cannot be created (or does not already exist)
* 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
*/
public static void forceMkdir(final File directory) throws IOException {
if (directory.exists()) {
if (!directory.isDirectory()) {
final String message =
"File "
+ directory
+ " exists and is "
+ "not a directory. Unable to create directory.";
throw new IOException(message);
}
} else {
if (!directory.mkdirs()) {
// Double-check that some other thread or process hasn't made
// the directory in the background
if (!directory.isDirectory()) {
final String message =
"Unable to create directory " + directory;
throw new IOException(message);
}
}
}
}
/**
* the size of a director
*
* @param directory the directory to check
* @return the size
*/
private static long sizeOfDirectory0(final File directory) {
final File[] files = directory.listFiles();
if (files == null) { // null if security restricted
return 0L;
}
long size = 0;
for (final File file : files) {
try {
if (!isSymlink(file)) {
size += sizeOf0(file); // internal method
if (size < 0) {
break;
}
}
} catch (final IOException ioe) {
// Ignore exceptions caught when asking if a File is a symlink.
}
}
return size;
}
// Internal method - does not check existence
/**
* the size of a file
*
* @param file the file to check
* @return the size of the file
*/
private static long sizeOf0(final File file) {
if (file.isDirectory()) {
return sizeOfDirectory0(file);
} else {
return file.length(); // will be 0 if file does not exist
}
}
/**
* Finds the size of a directory
*
* @param directory The directory
* @return the size
*/
private static BigInteger sizeOfDirectoryBig0(final File directory) {
final File[] files = directory.listFiles();
if (files == null) { // null if security restricted
return BigInteger.ZERO;
}
BigInteger size = BigInteger.ZERO;
for (final File file : files) {
try {
if (!isSymlink(file)) {
size = size.add(sizeOfBig0(file));
}
} catch (final IOException ioe) {
// Ignore exceptions caught when asking if a File is a symlink.
}
}
return size;
}
// internal method; if file does not exist will return 0
/**
* Returns the size of a file
*
* @param fileOrDir The file
* @return the size
*/
private static BigInteger sizeOfBig0(final File fileOrDir) {
if (fileOrDir.isDirectory()) {
return sizeOfDirectoryBig0(fileOrDir);
} else {
return BigInteger.valueOf(fileOrDir.length());
}
}
//-----------------------------------------------------------------------
public static boolean isSymlink(final File file) throws IOException {
if (file == null) {
throw new NullPointerException("File must not be null");
}
return Files.isSymbolicLink(file.toPath());
}
}