mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2026-05-18 00:00:03 -04:00
1083 lines
38 KiB
Java
Executable File
1083 lines
38 KiB
Java
Executable File
/*
|
|
* 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.
|
|
* <P>
|
|
* Versions exists take Input and OutputStreams as parameters, to
|
|
* allow for copying streams (URL's etc.).
|
|
*
|
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
|
* @author <A href="mailto:eirik.torske@iconmedialab.no">Eirik Torske</A>
|
|
* @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 occurrence
|
|
* 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<File> {
|
|
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<NumberFormat> sNumberFormat = new ThreadLocal<NumberFormat>() {
|
|
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<File> 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;
|
|
}
|
|
});
|
|
}
|
|
}
|