package ie.dcu.util;
import java.io.*;
import java.net.*;
/**
* Utility functions for file and filename manipulation.
*
* @author Kevin McGuinness
*/
public class FileUtils {
/**
* Size of the copy file buffer.
*/
private static final int BUFFER_SIZE = 8092;
/**
* Returns the file extension of the file. This method returns {@code null} if
* the file has no extension. The method also returns {@code null}:
*
*
* - If the filename contains a period as it's last character
* - If the filename contains a single period as its first character
*
*
* The returned extension is always lowercase.
*
* @param file
* The file.
* @param includePeriod
* Set to {@code true} to include the period in the returned
* extension.
* @return The file extension, or {@code null}
*/
public static final String getExtension(File file, boolean includePeriod) {
String filename = file.getName();
int idx = filename.lastIndexOf('.');
// If the period is not the first character or the last
if (idx > 0 && idx < filename.length() - 1) {
if (!includePeriod) {
idx++;
}
return filename.substring(idx).toLowerCase();
}
return null;
}
/**
* Returns the file extension of the file. This method returns {@code null} if
* the file has no extension. The method also returns {@code null}:
*
*
* - If the filename contains a period as it's last character
* - If the filename contains a single period as its first character
*
*
* This method does not include the period in the file extension. The returned
* extension is always lower-case.
*
* @param file
* The file.
* @return The file extension, or {@code null}
*/
public static final String getExtension(File file) {
return getExtension(file, false);
}
/**
* Remove the file extension from the filename, if it has one.
*
* @param filename
* The filename.
* @return The filename with the extension removed.
*/
public static final String removeExtension(String filename) {
int idx = filename.lastIndexOf('.');
if (idx > 0 && idx < filename.length() - 1) {
return filename.substring(0, idx);
}
return filename;
}
/**
* Replace the file extension with another. If the file has no existing
* extension, the new extension will be appended. If the new extension is not
* prefixed with a period, one will be added.
*
* @param filename
* The filename (not a file path).
* @param extension
* The new extension.
* @return The filename with the extension replaced.
*/
public static final String replaceExtension(String filename, String extension) {
boolean period = extension.startsWith(".");
int idx = filename.lastIndexOf('.');
if (idx > 0 && idx < filename.length() - 1) {
// Strip and append new extension
if (period) {
return filename.substring(0, idx) + extension;
} else {
return filename.substring(0, idx) + "." + extension;
}
}
if (period) {
return filename + extension;
}
return filename + "." + extension;
}
/**
* Returns true if the filename has an extension.
*
* @param filename
* The file name (not a file path).
* @return {@code true} if it has an extension.
*/
public static boolean hasExtension(String filename) {
int idx = filename.lastIndexOf('.');
return (idx > 0 && idx < filename.length() - 1);
}
/**
* Returns true if the filename has an extension.
*
* @param file
* The file.
* @return {@code true} if it has an extension.
*/
public static boolean hasExtension(File file) {
return hasExtension(file.getName());
}
/**
* Close the stream. Does nothing if it is already closed and ignores any I/O
* exception that is thrown.
*
* @param c
* The stream, or {@code null}
*/
public static void close(Closeable c) {
if (c == null) {
return;
}
try {
c.close();
} catch (IOException e) {
// Intentionally empty
}
}
/**
* Copy a source file to a destination. If the destination file exists it is
* overwritten. If the destination file is a directory, the file is copied to
* that directory.
*
* @param src
* The source file.
* @param dst
* The target file.
* @throws IOException
* If there is a problem copying the file.
*/
public static void copy(File src, File dst) throws IOException {
// If directory, copy to file of the same name in that directory
if (dst.isDirectory()) {
dst = new File(dst, src.getName());
}
InputStream in = null;
OutputStream out = null;
try {
in = new FileInputStream(src);
out = new FileOutputStream(dst);
// Copy streams
copy(in, out);
} finally {
// Close streams
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
return;
}
/**
* Copy the data from the input stream to the output stream. The streams are
* not closed after the copy.
*
* @param in
* The input stream.
* @param out
* The output stream.
* @throws IOException
* If there is a problem performing the copy.
*/
public static void copy(InputStream in, OutputStream out) throws IOException {
byte[] buff = new byte[BUFFER_SIZE];
int len;
while ((len = in.read(buff)) > 0) {
out.write(buff, 0, len);
}
}
/**
* Move the src file to dst. Semantics are the same as copy except that
* the source file is deleted after the operation.
*
* @param src
* The source file.
* @param dst
* The destination.
* @return {@code true} if the move is sucessful, {@code false} if the source
* file cannot be removed.
* @throws IOException
*/
public static boolean move(File src, File dst) throws IOException {
copy(src, dst);
return src.delete();
}
/**
* Throws an {@link FileNotFoundException} if the file does not exist.
*
* @param file
* The file to check.
* @throws FileNotFoundException
* The exception thrown if the file doesn't exist.
*/
public static void checkExists(File file) throws FileNotFoundException {
if (!file.exists()) {
throw new FileNotFoundException("File: " +
file.getAbsolutePath() + " does not exist");
}
}
/**
* Throws an {@link FileNotFoundException} if any of the files do not exist.
*
* @param files
* The files to check.
* @throws FileNotFoundException
* The exception thrown if any file doesn't exist.
*/
public static void checkExists(File ... files) throws FileNotFoundException {
for (File f : files) {
checkExists(f);
}
}
/**
* Makes the directory if it doesn't already exist. Throws an exception if
* there is an error creating the directory.
*
* @param dir
* The directory to create.
* @throws IOException
* If there is an error making the directory.
*/
public static void mkdir(File dir) throws IOException {
if (dir.isDirectory()) {
return;
}
if (!dir.mkdir()) {
throw new IOException("Unable to create directory");
}
}
/**
* Makes the directory if it doesn't already exist. Throws an exception if
* there is an error creating the directory. All intermediate subdirectories
* are also created.
*
* @param dir
* The directory to create.
* @throws IOException
* If there is an error making the directory.
*/
public static void mkdirs(File dir) throws IOException {
if (dir.isDirectory()) {
return;
}
if (!dir.mkdirs()) {
throw new IOException("Unable to create directory");
}
}
/**
* Create an absolute url for a file.
*
* @param file
* The file.
* @return The url
*/
public static URL createAbsoluteURL(File file) {
File absolute = file.getAbsoluteFile();
try {
return absolute.toURI().toURL();
} catch (MalformedURLException e) {
// Shouldn't happen (url cant be malformed, can it?)
return null;
}
}
/**
* Join a set of path components using the path separator
*
* @param parts
* The path components
* @return The joined path
*/
public static String pathJoin(String ... parts) {
StringBuilder sb = new StringBuilder();
if (parts.length > 0 && parts[0].startsWith(File.separator)) {
// Assume an absolute path
sb.append(File.separator);
}
for (String part : parts) {
sb.append(normalizePathComponent(part));
if (part != parts[parts.length-1]) {
sb.append(File.separatorChar);
}
}
return sb.toString();
}
/**
* Removes leading and trailing path separators from the path component
*
* @param component
* A path component
* @return A stripped string
*/
private static String normalizePathComponent(String component) {
if (component.endsWith(File.separator)) {
component = component.substring(0, File.separator.length());
} else if (component.startsWith(File.separator)) {
component = component.substring(File.separator.length());
} else {
return component;
}
return normalizePathComponent(component);
}
}