com.twelvemonkeys.io.FileUtil Maven / Gradle / Ivy
Show all versions of common-io Show documentation
/*
* Copyright (c) 2008, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.io;
import com.twelvemonkeys.lang.StringUtil;
import com.twelvemonkeys.lang.Validate;
import com.twelvemonkeys.util.Visitor;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;
import java.net.URL;
import java.text.NumberFormat;
/**
* A utility class with some useful file and i/o related methods.
*
* Versions exists take Input and OutputStreams as parameters, to
* allow for copying streams (URL's etc.).
*
* @author Harald Kuhr
* @author Eirik Torske
* @author last modified by $Author: haku $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/FileUtil.java#3 $
*/
public final class FileUtil {
// TODO: Be more cosequent using resolve() all places where File objects are involved
// TODO: Parameter handling (allow null vs IllegalArgument)
// TODO: Exception handling
/**
* The size of the buffer used for copying
*/
public final static int BUF_SIZE = 1024;
private static String TEMP_DIR = null;
private final static FileSystem FS = FileSystem.get();
public static void main(String[] pArgs) throws IOException {
File file;
if (pArgs[0].startsWith("file:")) {
file = toFile(new URL(pArgs[0]));
System.out.println(file);
}
else {
file = new File(pArgs[0]);
System.out.println(file.toURL());
}
System.out.println("Free space: " + getFreeSpace(file) + "/" + getTotalSpace(file) + " bytes");
}
/*
* Method main for test only.
*
public static void main0(String[] pArgs) {
if (pArgs.length != 2) {
System.out.println("usage: java Copy in out");
return;
}
try {
if (!copy(pArgs[0], pArgs[1])) {
System.out.println("Error copying");
}
}
catch (IOException e) {
System.out.println(e.getMessage());
}
}
//*/
// Avoid instances/constructor showing up in API doc
private FileUtil() {}
/**
* Copies the fromFile to the toFile location. If toFile is a directory, a
* new file is created in that directory, with the name of the fromFile.
* If the toFile exists, the file will not be copied, unless owerWrite is
* true.
*
* @param pFromFileName The name of the file to copy from
* @param pToFileName The name of the file to copy to
* @return true if the file was copied successfully,
* false if the output file exists. In all other cases, an
* IOException is thrown, and the method does not return a value.
* @throws IOException if an i/o error occurs during copy
*/
public static boolean copy(String pFromFileName, String pToFileName) throws IOException {
return copy(new File(pFromFileName), new File(pToFileName), false);
}
/**
* Copies the fromFile to the toFile location. If toFile is a directory, a
* new file is created in that directory, with the name of the fromFile.
* If the toFile exists, the file will not be copied, unless owerWrite is
* true.
*
* @param pFromFileName The name of the file to copy from
* @param pToFileName The name of the file to copy to
* @param pOverWrite Specifies if the toFile should be overwritten, if it
* exists.
* @return true if the file was copied successfully,
* false if the output file exists, and the owerWrite parameter is
* false. In all other cases, an
* IOException is thrown, and the method does not return a value.
* @throws IOException if an i/o error occurs during copy
*/
public static boolean copy(String pFromFileName, String pToFileName, boolean pOverWrite) throws IOException {
return copy(new File(pFromFileName), new File(pToFileName), pOverWrite);
}
/**
* Copies the fromFile to the toFile location. If toFile is a directory, a
* new file is created in that directory, with the name of the fromFile.
* If the toFile exists, the file will not be copied, unless owerWrite is
* true.
*
* @param pFromFile The file to copy from
* @param pToFile The file to copy to
* @return true if the file was copied successfully,
* false if the output file exists. In all other cases, an
* IOException is thrown, and the method does not return a value.
* @throws IOException if an i/o error occurs during copy
*/
public static boolean copy(File pFromFile, File pToFile) throws IOException {
return copy(pFromFile, pToFile, false);
}
/**
* Copies the fromFile to the toFile location. If toFile is a directory, a
* new file is created in that directory, with the name of the fromFile.
* If the toFile exists, the file will not be copied, unless owerWrite is
* true.
*
* @param pFromFile The file to copy from
* @param pToFile The file to copy to
* @param pOverWrite Specifies if the toFile should be overwritten, if it
* exists.
* @return {@code true} if the file was copied successfully,
* {@code false} if the output file exists, and the
* {@code pOwerWrite} parameter is
* {@code false}. In all other cases, an
* {@code IOExceptio}n is thrown, and the method does not return.
* @throws IOException if an i/o error occurs during copy
*/
public static boolean copy(File pFromFile, File pToFile, boolean pOverWrite) throws IOException {
// Copy all directory structure
if (pFromFile.isDirectory()) {
return copyDir(pFromFile, pToFile, pOverWrite);
}
// Check if destination is a directory
if (pToFile.isDirectory()) {
// Create a new file with same name as from
pToFile = new File(pToFile, pFromFile.getName());
}
// Check if file exists, and return false if overWrite is false
if (!pOverWrite && pToFile.exists()) {
return false;
}
InputStream in = null;
OutputStream out = null;
try {
// Use buffer size two times byte array, to avoid i/o bottleneck
in = new FileInputStream(pFromFile);
out = new FileOutputStream(pToFile);
// Copy from inputStream to outputStream
copy(in, out);
}
//Just pass any IOException on up the stack
finally {
close(in);
close(out);
}
return true; // If we got here, everything is probably okay.. ;-)
}
/**
* Tries to close the given stream.
* NOTE: If the stream cannot be closed, the IOException thrown is silently
* ignored.
*
* @param pInput the stream to close
*/
public static void close(InputStream pInput) {
try {
if (pInput != null) {
pInput.close();
}
}
catch (IOException ignore) {
// Non critical error
}
}
/**
* Tries to close the given stream.
* NOTE: If the stream cannot be closed, the IOException thrown is silently
* ignored.
*
* @param pOutput the stream to close
*/
public static void close(OutputStream pOutput) {
try {
if (pOutput != null) {
pOutput.close();
}
}
catch (IOException ignore) {
// Non critical error
}
}
static void close(Reader pReader) {
try {
if (pReader != null) {
pReader.close();
}
}
catch (IOException ignore) {
// Non critical error
}
}
static void close(Writer pWriter) {
try {
if (pWriter != null) {
pWriter.close();
}
}
catch (IOException ignore) {
// Non critical error
}
}
/**
* Copies a directory recursively. If the destination folder does not exist,
* it is created
*
* @param pFrom the source directory
* @param pTo the destination directory
* @param pOverWrite {@code true} if we should allow overwrting existing files
* @return {@code true} if all files were copied sucessfully
* @throws IOException if {@code pTo} exists, and it not a directory,
* or if copying of any of the files in the folder fails
*/
private static boolean copyDir(File pFrom, File pTo, boolean pOverWrite) throws IOException {
if (pTo.exists() && !pTo.isDirectory()) {
throw new IOException("A directory may only be copied to another directory, not to a file");
}
pTo.mkdirs(); // mkdir?
boolean allOkay = true;
File[] files = pFrom.listFiles();
for (File file : files) {
if (!copy(file, new File(pTo, file.getName()), pOverWrite)) {
allOkay = false;
}
}
return allOkay;
}
/**
* Copies all data from one stream to another.
* The data is copied from the fromStream to the toStream using buffered
* streams for efficiency.
*
* @param pFrom The input srteam to copy from
* @param pTo The output stream to copy to
* @return true. Otherwise, an
* IOException is thrown, and the method does not return a value.
* @throws IOException if an i/o error occurs during copy
* @throws IllegalArgumentException if either {@code pFrom} or {@code pTo} is
* {@code null}
*/
public static boolean copy(InputStream pFrom, OutputStream pTo) throws IOException {
Validate.notNull(pFrom, "from");
Validate.notNull(pTo, "to");
// TODO: Consider using file channels for faster copy where possible
// Use buffer size two times byte array, to avoid i/o bottleneck
// TODO: Consider letting the client decide as this is sometimes not a good thing!
InputStream in = new BufferedInputStream(pFrom, BUF_SIZE * 2);
OutputStream out = new BufferedOutputStream(pTo, BUF_SIZE * 2);
byte[] buffer = new byte[BUF_SIZE];
int count;
while ((count = in.read(buffer)) != -1) {
out.write(buffer, 0, count);
}
// Flush out stream, to write any remaining buffered data
out.flush();
return true; // If we got here, everything is probably okay.. ;-)
}
/**
* Gets the file (type) extension of the given file.
* A file extension is the part of the filename, after the last occurence
* of a period {@code '.'}.
* If the filename contains no period, {@code null} is returned.
*
* @param pFileName the full filename with extension
* @return the extension (type) of the file, or {@code null}
*/
public static String getExtension(final String pFileName) {
return getExtension0(getFilename(pFileName));
}
/**
* Gets the file (type) extension of the given file.
* A file extension is the part of the filename, after the last occurence
* of a period {@code '.'}.
* If the filename contains no period, {@code null} is returned.
*
* @param pFile the file
* @return the extension (type) of the file, or {@code null}
*/
public static String getExtension(final File pFile) {
return getExtension0(pFile.getName());
}
// NOTE: Assumes filename and no path
private static String getExtension0(final String pFileName) {
int index = pFileName.lastIndexOf('.');
if (index >= 0) {
return pFileName.substring(index + 1);
}
// No period found
return null;
}
/**
* Gets the file name of the given file, without the extension (type).
* A file extension is the part of the filename, after the last occurence
* of a period {@code '.'}.
* If the filename contains no period, the complete file name is returned
* (same as {@code pFileName}, if the string contains no path elements).
*
* @param pFileName the full filename with extension
* @return the base name of the file
*/
public static String getBasename(final String pFileName) {
return getBasename0(getFilename(pFileName));
}
/**
* Gets the file name of the given file, without the extension (type).
* A file extension is the part of the filename, after the last occurence
* of a period {@code '.'}.
* If the filename contains no period, {@code pFile.getName()} is returned.
*
* @param pFile the file
* @return the base name of the file
*/
public static String getBasename(final File pFile) {
return getBasename0(pFile.getName());
}
// NOTE: Assumes filename and no path
public static String getBasename0(final String pFileName) {
int index = pFileName.lastIndexOf('.');
if (index >= 0) {
return pFileName.substring(0, index);
}
// No period found
return pFileName;
}
/**
* Extracts the directory path without the filename, from a complete
* filename path.
*
* @param pPath The full filename path.
* @return the path without the filename.
* @see File#getParent
* @see #getFilename
*/
public static String getDirectoryname(final String pPath) {
return getDirectoryname(pPath, File.separatorChar);
}
/**
* Extracts the directory path without the filename, from a complete
* filename path.
*
* @param pPath The full filename path.
* @param pSeparator the separator char used in {@code pPath}
* @return the path without the filename.
* @see File#getParent
* @see #getFilename
*/
public static String getDirectoryname(final String pPath, final char pSeparator) {
int index = pPath.lastIndexOf(pSeparator);
if (index < 0) {
return ""; // Assume only filename
}
return pPath.substring(0, index);
}
/**
* Extracts the filename of a complete filename path.
*
* @param pPath The full filename path.
* @return the extracted filename.
* @see File#getName
* @see #getDirectoryname
*/
public static String getFilename(final String pPath) {
return getFilename(pPath, File.separatorChar);
}
/**
* Extracts the filename of a complete filename path.
*
* @param pPath The full filename path.
* @param pSeparator The file separator.
* @return the extracted filename.
* @see File#getName
* @see #getDirectoryname
*/
public static String getFilename(final String pPath, final char pSeparator) {
int index = pPath.lastIndexOf(pSeparator);
if (index < 0) {
return pPath; // Assume only filename
}
return pPath.substring(index + 1);
}
/**
* Tests if a file or directory has no content.
* A file is empty if it has a length of 0L. A non-existing file is also
* considered empty.
* A directory is considered empty if it contains no files.
*
* @param pFile The file to test
* @return {@code true} if the file is empty, otherwise
* {@code false}.
*/
public static boolean isEmpty(File pFile) {
if (pFile.isDirectory()) {
return (pFile.list().length == 0);
}
return (pFile.length() == 0);
}
/**
* Gets the default temp directory for the system as a File.
*
* @return a {@code File}, representing the default temp directory.
* @see File#createTempFile
*/
public static File getTempDirFile() {
return new File(getTempDir());
}
/**
* Gets the default temp directory for the system.
*
* @return a {@code String}, representing the path to the default temp
* directory.
* @see File#createTempFile
*/
public static String getTempDir() {
synchronized (FileUtil.class) {
if (TEMP_DIR == null) {
// Get the 'java.io.tmpdir' property
String tmpDir = System.getProperty("java.io.tmpdir");
if (StringUtil.isEmpty(tmpDir)) {
// Stupid fallback...
// TODO: Delegate to FileSystem?
if (new File("/temp").exists()) {
tmpDir = "/temp"; // Windows
}
else {
tmpDir = "/tmp"; // Unix
}
}
TEMP_DIR = tmpDir;
}
}
return TEMP_DIR;
}
/**
* Gets the contents of the given file, as a byte array.
*
* @param pFilename the name of the file to get content from
* @return the content of the file as a byte array.
* @throws IOException if the read operation fails
*/
public static byte[] read(String pFilename) throws IOException {
return read(new File(pFilename));
}
/**
* Gets the contents of the given file, as a byte array.
*
* @param pFile the file to get content from
* @return the content of the file as a byte array.
* @throws IOException if the read operation fails
*/
public static byte[] read(File pFile) throws IOException {
// Custom implementation, as we know the size of a file
if (!pFile.exists()) {
throw new FileNotFoundException(pFile.toString());
}
byte[] bytes = new byte[(int) pFile.length()];
InputStream in = null;
try {
// Use buffer size two times byte array, to avoid i/o bottleneck
in = new BufferedInputStream(new FileInputStream(pFile), BUF_SIZE * 2);
int off = 0;
int len;
while ((len = in.read(bytes, off, in.available())) != -1 && (off < bytes.length)) {
off += len;
// System.out.println("read:" + len);
}
}
// Just pass any IOException on up the stack
finally {
close(in);
}
return bytes;
}
/**
* Reads all data from the input stream to a byte array.
*
* @param pInput The input stream to read from
* @return The content of the stream as a byte array.
* @throws IOException if an i/o error occurs during read.
*/
public static byte[] read(InputStream pInput) throws IOException {
// Create byte array
ByteArrayOutputStream bytes = new FastByteArrayOutputStream(BUF_SIZE);
// Copy from stream to byte array
copy(pInput, bytes);
return bytes.toByteArray();
}
/**
* Writes the contents from a byte array to an output stream.
*
* @param pOutput The output stream to write to
* @param pData The byte array to write
* @return {@code true}, otherwise an IOException is thrown.
* @throws IOException if an i/o error occurs during write.
*/
public static boolean write(OutputStream pOutput, byte[] pData) throws IOException {
// Write data
pOutput.write(pData);
// If we got here, all is okay
return true;
}
/**
* Writes the contents from a byte array to a file.
*
* @param pFile The file to write to
* @param pData The byte array to write
* @return {@code true}, otherwise an IOException is thrown.
* @throws IOException if an i/o error occurs during write.
*/
public static boolean write(File pFile, byte[] pData) throws IOException {
boolean success = false;
OutputStream out = null;
try {
out = new BufferedOutputStream(new FileOutputStream(pFile));
success = write(out, pData);
}
finally {
close(out);
}
return success;
}
/**
* Writes the contents from a byte array to a file.
*
* @param pFilename The name of the file to write to
* @param pData The byte array to write
* @return {@code true}, otherwise an IOException is thrown.
* @throws IOException if an i/o error occurs during write.
*/
public static boolean write(String pFilename, byte[] pData) throws IOException {
return write(new File(pFilename), pData);
}
/**
* Deletes the specified file.
*
* @param pFile The file to delete
* @param pForce Forces delete, even if the parameter is a directory, and
* is not empty. Be careful!
* @return {@code true}, if the file existed and was deleted.
* @throws IOException if an i/o error occurs during delete.
*/
public static boolean delete(final File pFile, final boolean pForce) throws IOException {
if (pForce && pFile.isDirectory()) {
return deleteDir(pFile);
}
return pFile.exists() && pFile.delete();
}
/**
* Deletes a directory recursively.
*
* @param pFile the file to delete
* @return {@code true} if the file was deleted sucessfully
* @throws IOException if an i/o error occurs during delete.
*/
private static boolean deleteDir(final File pFile) throws IOException {
// Recusively delete all files/subfolders
// Deletes the files using visitor pattern, to avoid allocating
// a file array, which may throw OutOfMemoryExceptions for
// large directories/in low memory situations
class DeleteFilesVisitor implements Visitor {
private int failedCount = 0;
private IOException exception = null;
public void visit(final File pFile) {
try {
if (!delete(pFile, true)) {
failedCount++;
}
}
catch (IOException e) {
failedCount++;
if (exception == null) {
exception = e;
}
}
}
boolean succeeded() throws IOException {
if (exception != null) {
throw exception;
}
return failedCount == 0;
}
}
DeleteFilesVisitor fileDeleter = new DeleteFilesVisitor();
visitFiles(pFile, null, fileDeleter);
// If any of the deletes above failed, this will fail (or return false)
return fileDeleter.succeeded() && pFile.delete();
}
/**
* Deletes the specified file.
*
* @param pFilename The name of file to delete
* @param pForce Forces delete, even if the parameter is a directory, and
* is not empty. Careful!
* @return {@code true}, if the file existed and was deleted.
* @throws java.io.IOException if deletion fails
*/
public static boolean delete(String pFilename, boolean pForce) throws IOException {
return delete(new File(pFilename), pForce);
}
/**
* Deletes the specified file.
*
* @param pFile The file to delete
* @return {@code true}, if the file existed and was deleted.
* @throws java.io.IOException if deletion fails
*/
public static boolean delete(File pFile) throws IOException {
return delete(pFile, false);
}
/**
* Deletes the specified file.
*
* @param pFilename The name of file to delete
* @return {@code true}, if the file existed and was deleted.
* @throws java.io.IOException if deletion fails
*/
public static boolean delete(String pFilename) throws IOException {
return delete(new File(pFilename), false);
}
/**
* Renames the specified file.
* If the destination is a directory (and the source is not), the source
* file is simply moved to the destination directory.
*
* @param pFrom The file to rename
* @param pTo The new file
* @param pOverWrite Specifies if the tofile should be overwritten, if it
* exists
* @return {@code true}, if the file was renamed.
*
* @throws FileNotFoundException if {@code pFrom} does not exist.
*/
public static boolean rename(File pFrom, File pTo, boolean pOverWrite) throws IOException {
if (!pFrom.exists()) {
throw new FileNotFoundException(pFrom.getAbsolutePath());
}
if (pFrom.isFile() && pTo.isDirectory()) {
pTo = new File(pTo, pFrom.getName());
}
return (pOverWrite || !pTo.exists()) && pFrom.renameTo(pTo);
}
/**
* Renames the specified file, if the destination does not exist.
* If the destination is a directory (and the source is not), the source
* file is simply moved to the destination directory.
*
* @param pFrom The file to rename
* @param pTo The new file
* @return {@code true}, if the file was renamed.
* @throws java.io.IOException if rename fails
*/
public static boolean rename(File pFrom, File pTo) throws IOException {
return rename(pFrom, pTo, false);
}
/**
* Renames the specified file.
* If the destination is a directory (and the source is not), the source
* file is simply moved to the destination directory.
*
* @param pFrom The file to rename
* @param pTo The new name of the file
* @param pOverWrite Specifies if the tofile should be overwritten, if it
* exists
* @return {@code true}, if the file was renamed.
* @throws java.io.IOException if rename fails
*/
public static boolean rename(File pFrom, String pTo, boolean pOverWrite) throws IOException {
return rename(pFrom, new File(pTo), pOverWrite);
}
/**
* Renames the specified file, if the destination does not exist.
* If the destination is a directory (and the source is not), the source
* file is simply moved to the destination directory.
*
* @param pFrom The file to rename
* @param pTo The new name of the file
* @return {@code true}, if the file was renamed.
* @throws java.io.IOException if rename fails
*/
public static boolean rename(File pFrom, String pTo) throws IOException {
return rename(pFrom, new File(pTo), false);
}
/**
* Renames the specified file.
* If the destination is a directory (and the source is not), the source
* file is simply moved to the destination directory.
*
* @param pFrom The name of the file to rename
* @param pTo The new name of the file
* @param pOverWrite Specifies if the tofile should be overwritten, if it
* exists
* @return {@code true}, if the file was renamed.
* @throws java.io.IOException if rename fails
*/
public static boolean rename(String pFrom, String pTo, boolean pOverWrite) throws IOException {
return rename(new File(pFrom), new File(pTo), pOverWrite);
}
/**
* Renames the specified file, if the destination does not exist.
* If the destination is a directory (and the source is not), the source
* file is simply moved to the destination directory.
*
* @param pFrom The name of the file to rename
* @param pTo The new name of the file
* @return {@code true}, if the file was renamed.
* @throws java.io.IOException if rename fails
*/
public static boolean rename(String pFrom, String pTo) throws IOException {
return rename(new File(pFrom), new File(pTo), false);
}
/**
* Lists all files (and directories) in a specific folder.
*
* @param pFolder The folder to list
* @return a list of {@code java.io.File} objects.
* @throws FileNotFoundException if {@code pFolder} is not a readable file
*/
public static File[] list(final String pFolder) throws FileNotFoundException {
return list(pFolder, null);
}
/**
* Lists all files (and directories) in a specific folder which are
* embraced by the wildcard filename mask provided.
*
* @param pFolder The folder to list
* @param pFilenameMask The wildcard filename mask
* @return a list of {@code java.io.File} objects.
* @see File#listFiles(FilenameFilter)
* @throws FileNotFoundException if {@code pFolder} is not a readable file
*/
public static File[] list(final String pFolder, final String pFilenameMask) throws FileNotFoundException {
if (StringUtil.isEmpty(pFolder)) {
return null;
}
File folder = resolve(pFolder);
if (!(/*folder.exists() &&*/folder.isDirectory() && folder.canRead())) {
// NOTE: exists is implicitly called by isDirectory
throw new FileNotFoundException("\"" + pFolder + "\" is not a directory or is not readable.");
}
if (StringUtil.isEmpty(pFilenameMask)) {
return folder.listFiles();
}
// TODO: Rewrite to use regexp
FilenameFilter filter = new FilenameMaskFilter(pFilenameMask);
return folder.listFiles(filter);
}
/**
* Creates a {@code File} based on the path part of the URL, for
* file-protocol ({@code file:}) based URLs.
*
* @param pURL the {@code file:} URL
* @return a new {@code File} object representing the URL
*
* @throws NullPointerException if {@code pURL} is {@code null}
* @throws IllegalArgumentException if {@code pURL} is
* not a file-protocol URL.
*
* @see java.io.File#toURI()
* @see java.io.File#File(java.net.URI)
*/
public static File toFile(URL pURL) {
if (pURL == null) {
throw new NullPointerException("URL == null");
}
// NOTE: Precondition tests below is based on the File(URI) constructor,
// and is most likely overkill...
// NOTE: A URI is absolute iff it has a scheme component
// As the scheme has to be "file", this is implicitly tested below
// NOTE: A URI is opaque iff it is absolute and it's shceme-specific
// part does not begin with a '/', see below
if (!"file".equals(pURL.getProtocol())) {
// URL protocol => URI scheme
throw new IllegalArgumentException("URL scheme is not \"file\"");
}
if (pURL.getAuthority() != null) {
throw new IllegalArgumentException("URL has an authority component");
}
if (pURL.getRef() != null) {
// URL ref (anchor) => URI fragment
throw new IllegalArgumentException("URI has a fragment component");
}
if (pURL.getQuery() != null) {
throw new IllegalArgumentException("URL has a query component");
}
String path = pURL.getPath();
if (!path.startsWith("/")) {
// A URL should never be able to represent an opaque URI, test anyway
throw new IllegalArgumentException("URI is not hierarchical");
}
if (path.isEmpty()) {
throw new IllegalArgumentException("URI path component is empty");
}
// Convert separator, doesn't seem to be neccessary on Windows/Unix,
// but do it anyway to be compatible...
if (File.separatorChar != '/') {
path = path.replace('/', File.separatorChar);
}
return resolve(path);
}
public static File resolve(String pPath) {
return Win32File.wrap(new File(pPath));
}
public static File resolve(File pPath) {
return Win32File.wrap(pPath);
}
public static File resolve(File pParent, String pChild) {
return Win32File.wrap(new File(pParent, pChild));
}
public static File[] resolve(File[] pPaths) {
return Win32File.wrap(pPaths);
}
// TODO: Handle SecurityManagers in a deterministic way
// TODO: Exception handling
// TODO: What happens if the file does not exist?
public static long getFreeSpace(final File pPath) {
// NOTE: Allow null, to get space in current/system volume
File path = pPath != null ? pPath : new File(".");
Long space = getSpace16("getFreeSpace", path);
if (space != null) {
return space;
}
return FS.getFreeSpace(path);
}
public static long getUsableSpace(final File pPath) {
// NOTE: Allow null, to get space in current/system volume
File path = pPath != null ? pPath : new File(".");
Long space = getSpace16("getUsableSpace", path);
if (space != null) {
return space;
}
return getTotalSpace(path);
}
// TODO: FixMe for Windows, before making it public...
public static long getTotalSpace(final File pPath) {
// NOTE: Allow null, to get space in current/system volume
File path = pPath != null ? pPath : new File(".");
Long space = getSpace16("getTotalSpace", path);
if (space != null) {
return space;
}
return FS.getTotalSpace(path);
}
private static Long getSpace16(final String pMethodName, final File pPath) {
try {
Method freeSpace = File.class.getMethod(pMethodName);
return (Long) freeSpace.invoke(pPath);
}
catch (NoSuchMethodException ignore) {}
catch (IllegalAccessException ignore) {}
catch (InvocationTargetException e) {
Throwable throwable = e.getTargetException();
if (throwable instanceof SecurityException) {
throw (SecurityException) throwable;
}
throw new UndeclaredThrowableException(throwable);
}
return null;
}
/**
* Formats the given number to a human readable format.
* Kind of like {@code df -h}.
*
* @param pSizeInBytes the size in byte
* @return a human readable string representation
*/
public static String toHumanReadableSize(final long pSizeInBytes) {
// TODO: Rewrite to use String.format?
if (pSizeInBytes < 1024L) {
return pSizeInBytes + " Bytes";
}
else if (pSizeInBytes < (1024L << 10)) {
return getSizeFormat().format(pSizeInBytes / (double) (1024L)) + " KB";
}
else if (pSizeInBytes < (1024L << 20)) {
return getSizeFormat().format(pSizeInBytes / (double) (1024L << 10)) + " MB";
}
else if (pSizeInBytes < (1024L << 30)) {
return getSizeFormat().format(pSizeInBytes / (double) (1024L << 20)) + " GB";
}
else if (pSizeInBytes < (1024L << 40)) {
return getSizeFormat().format(pSizeInBytes / (double) (1024L << 30)) + " TB";
}
else {
return getSizeFormat().format(pSizeInBytes / (double) (1024L << 40)) + " PB";
}
}
// NumberFormat is not thread-safe, so we stick to thread-confined instances
private static ThreadLocal sNumberFormat = new ThreadLocal() {
protected NumberFormat initialValue() {
NumberFormat format = NumberFormat.getNumberInstance();
// TODO: Consider making this locale/platform specific, OR a method parameter...
// format.setMaximumFractionDigits(2);
format.setMaximumFractionDigits(0);
return format;
}
};
private static NumberFormat getSizeFormat() {
return sNumberFormat.get();
}
/**
* Visits all files in {@code pDirectory}. Optionally filtered through a {@link FileFilter}.
*
* @param pDirectory the directory to visit files in
* @param pFilter the filter, may be {@code null}, meaning all files will be visited
* @param pVisitor the visitor
*
* @throws IllegalArgumentException if either {@code pDirectory} or {@code pVisitor} are {@code null}
*
* @see com.twelvemonkeys.util.Visitor
*/
@SuppressWarnings({"ResultOfMethodCallIgnored"})
public static void visitFiles(final File pDirectory, final FileFilter pFilter, final Visitor pVisitor) {
Validate.notNull(pDirectory, "directory");
Validate.notNull(pVisitor, "visitor");
pDirectory.listFiles(new FileFilter() {
public boolean accept(final File pFile) {
if (pFilter == null || pFilter.accept(pFile)) {
pVisitor.visit(pFile);
}
return false;
}
});
}
}