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}: * * * * 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}: * * * * 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); } }