/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.tuscany.sca.contribution.service.util; 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.net.URL; import java.util.regex.Pattern; public class FileHelper { /** * The extension separator character. */ private static final char EXTENSION_SEPARATOR = '.'; /** * The Unix separator character. */ private static final char UNIX_SEPARATOR = '/'; /** * The Windows separator character. */ private static final char WINDOWS_SEPARATOR = '\\'; /** * Returns the index of the last directory separator character. *
* This method will handle a file in either Unix or Windows format. The * position of the last forward or backslash is returned. *
* The output will be the same irrespective of the machine that the code is * running on. * * @param filename the filename to find the last path separator in, null * returns -1 * @return the index of the last separator character, or -1 if there is no * such character */ public static int indexOfLastSeparator(String filename) { if (filename == null) { return -1; } int lastUnixPos = filename.lastIndexOf(UNIX_SEPARATOR); int lastWindowsPos = filename.lastIndexOf(WINDOWS_SEPARATOR); return Math.max(lastUnixPos, lastWindowsPos); } /** * Returns the index of the last extension separator character, which is a * dot. *
* This method also checks that there is no directory separator after the * last dot. To do this it uses {@link #indexOfLastSeparator(String)} which * will handle a file in either Unix or Windows format. *
* The output will be the same irrespective of the machine that the code is * running on. * * @param filename the filename to find the last path separator in, null * returns -1 * @return the index of the last separator character, or -1 if there is no * such character */ public static int indexOfExtension(String filename) { if (filename == null) { return -1; } int extensionPos = filename.lastIndexOf(EXTENSION_SEPARATOR); int lastSeparator = indexOfLastSeparator(filename); return lastSeparator > extensionPos ? -1 : extensionPos; } /** * Gets the name minus the path from a full filename. *
* This method will handle a file in either Unix or Windows format. The text * after the last forward or backslash is returned. * *
* a/b/c.txt --> c.txt * a.txt --> a.txt * a/b/c --> c * a/b/c/ --> "" ** *
* The output will be the same irrespective of the machine that the code is * running on. * * @param fileName the filename to query, null returns null * @return the name of the file without the path, or an empty string if none * exists */ public static String getName(String fileName) { if (fileName == null) { return null; } int index = indexOfLastSeparator(fileName); return fileName.substring(index + 1); } /** * Gets the extension of a filename. *
* This method returns the textual part of the filename after the last dot. * There must be no directory separator after the dot. * *
* foo.txt --> "txt" * a/b/c.jpg --> "jpg" * a/b.txt/c --> "" * a/b/c --> "" ** *
* The output will be the same irrespective of the machine that the code is * running on. * * @param filename the filename to retrieve the extension of. * @return the extension of the file or an empty string if none exists. */ public static String getExtension(String filename) { if (filename == null) { return null; } int index = indexOfExtension(filename); if (index == -1) { return ""; } else { return filename.substring(index + 1); } } /** * Make a directory, including any necessary but nonexistent parent * directories. If there already exists a file with specified name or the * directory cannot be created then an exception is thrown. * * @param directory directory to create, not null * @throws NullPointerException if the directory is null * @throws IOException if the directory cannot be created */ public static void forceMkdir(File directory) throws IOException { if (directory.exists()) { if (directory.isFile()) { String message = "File " + directory + " exists and is " + "not a directory. Unable to create directory."; throw new IOException(message); } } else { if (!directory.mkdirs()) { String message = "Unable to create directory " + directory; throw new IOException(message); } } } /** * Delete a file. If file is a directory, delete it and all sub-directories. *
* The difference between File.delete() and this method are: *
URL
to a File
.
*
* From version 1.1 this method will decode the URL. Syntax such as
* file:///my%20docs/file.txt
will be correctly decoded to
* /my docs/file.txt
.
*
* @param url the file URL to convert, null returns null
* @return the equivalent File
object, or null
* if the URL's protocol is not file
* @throws IllegalArgumentException if the file is incorrectly encoded
*/
public static File toFile(URL url) {
if (url == null || !url.getProtocol().equals("file")) {
return null;
} else {
String filename = url.getFile().replace('/', File.separatorChar);
int pos = 0;
while ((pos = filename.indexOf('%', pos)) >= 0) { // NOPMD
if (pos + 2 < filename.length()) {
String hexStr = filename.substring(pos + 1, pos + 3);
char ch = (char)Integer.parseInt(hexStr, 16);
filename = filename.substring(0, pos) + ch + filename.substring(pos + 3);
}
}
return new File(filename);
}
}
public static FileFilter getFileFilter(String regExp, boolean ignoreCase) {
return new RegExpFilter(regExp, ignoreCase);
}
/**
* A regular-expression based resource filter
*/
public static class RegExpFilter implements FileFilter {
private Pattern pattern;
public RegExpFilter(Pattern pattern) {
this.pattern = pattern;
}
public RegExpFilter(String patternStr, boolean ignoreCase) {
this.pattern = Pattern.compile(patternStr, ignoreCase ? Pattern.CASE_INSENSITIVE : 0);
}
public boolean accept(File file) {
return pattern.matcher(file.getName()).matches();
}
/**
* Convert wildcard into a regex pattern
*
* @param str
* @return
*/
public static RegExpFilter getWildcardFilter(String str, boolean ignoreCase) {
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < str.length(); i++) {
char ch = str.charAt(i);
if (ch == '?') {
buffer.append('.');
} else if (ch == '*') {
buffer.append(".*");
} else {
buffer.append(ch);
}
}
return new RegExpFilter(buffer.toString(), ignoreCase);
}
}
/**
* Clean a directory without deleting it.
*
* @param directory directory to clean
* @throws IOException in case cleaning is unsuccessful
*/
public static void cleanDirectory(File directory) throws IOException {
if (!directory.exists()) {
String message = directory + " does not exist";
throw new IllegalArgumentException(message);
}
if (!directory.isDirectory()) {
String message = directory + " is not a directory";
throw new IllegalArgumentException(message);
}
File[] files = directory.listFiles();
if (files == null) { // null if security restricted
throw new IOException("Failed to list contents of " + directory);
}
IOException exception = null;
for (int i = 0; i < files.length; i++) {
File file = files[i];
try {
forceDelete(file);
} catch (IOException ioe) {
exception = ioe;
}
}
if (null != exception) {
throw exception;
}
}
/**
* Clean a directory without deleting it.
*
* @param directory directory to clean, must not be null
* @throws NullPointerException if the directory is null
* @throws IOException in case cleaning is unsuccessful
*/
private static void cleanDirectoryOnExit(File directory) throws IOException {
if (!directory.exists()) {
String message = directory + " does not exist";
throw new IllegalArgumentException(message);
}
if (!directory.isDirectory()) {
String message = directory + " is not a directory";
throw new IllegalArgumentException(message);
}
File[] files = directory.listFiles();
if (files == null) { // null if security restricted
throw new IOException("Failed to list contents of " + directory);
}
IOException exception = null;
for (int i = 0; i < files.length; i++) {
File file = files[i];
try {
forceDeleteOnExit(file);
} catch (IOException ioe) {
exception = ioe;
}
}
if (null != exception) {
throw exception;
}
}
/**
* 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.
*
* @param srcDir an existing directory to copy, must not be
* null
* @param destDir the new directory, must not be 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
* @since Commons IO 1.1
*/
public static void copyDirectory(File srcDir, 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.
*
* @param srcDir an existing directory to copy, must not be
* null
* @param destDir the new directory, must not be 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
* null
* @throws IOException if source or destination is invalid
* @throws IOException if an IO error occurs during copying
* @since Commons IO 1.1
*/
public static void copyDirectory(File srcDir, File destDir, boolean preserveFileDate) throws IOException {
if (srcDir == null) {
throw new NullPointerException("Source must not be null");
}
if (destDir == null) {
throw new NullPointerException("Destination must not be null");
}
if (!srcDir.exists()) {
throw new FileNotFoundException("Source '" + srcDir + "' does not exist");
}
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");
}
doCopyDirectory(srcDir, destDir, preserveFileDate);
}
// -----------------------------------------------------------------------
/**
* Copies a directory to within another directory preserving the file dates.
*
* This method copies the source directory and all its contents to a * directory of the same name in 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.
*
* @param srcDir an existing directory to copy, must not be
* null
* @param destDir the directory to place the copy in, must not be
* 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
* @since Commons IO 1.2
*/
public static void copyDirectoryToDirectory(File srcDir, File destDir) throws IOException {
if (srcDir == null) {
throw new NullPointerException("Source must not be null");
}
if (!(srcDir.exists() && srcDir.isDirectory())) {
throw new IllegalArgumentException("Source '" + destDir + "' is not a directory");
}
if (destDir == null) {
throw new NullPointerException("Destination must not be null");
}
if (!(destDir.exists() && destDir.isDirectory())) {
throw new IllegalArgumentException("Destination '" + destDir + "' is not a directory");
}
copyDirectory(srcDir, new File(destDir, srcDir.getName()), true);
}
/**
* 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.
*
* @param srcFile an existing file to copy, must not be null
* @param destFile the new file, must not be 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 #copyFileToDirectory(File, File)
*/
public static void copyFile(File srcFile, 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.
*
* @param srcFile an existing file to copy, must not be null
* @param destFile the new file, must not be 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
* null
* @throws IOException if source or destination is invalid
* @throws IOException if an IO error occurs during copying
* @see #copyFileToDirectory(File, File, boolean)
*/
public static void copyFile(File srcFile, File destFile, boolean preserveFileDate) throws IOException {
if (srcFile == null) {
throw new NullPointerException("Source must not be null");
}
if (destFile == null) {
throw new NullPointerException("Destination must not be null");
}
if (!srcFile.exists()) {
throw new FileNotFoundException("Source '" + srcFile + "' does not exist");
}
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");
}
if (!(destFile.getParentFile() != null && destFile.getParentFile().exists())) {
if (!destFile.getParentFile().mkdirs()) { //NOPMD
throw new IOException("Destination '" + destFile + "' directory cannot be created");
}
}
if (!(destFile.exists() && destFile.canWrite())) {
throw new IOException("Destination '" + destFile + "' exists but is read-only");
}
doCopyFile(srcFile, destFile, preserveFileDate);
}
// -----------------------------------------------------------------------
/**
* 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.
*
* @param srcFile an existing file to copy, must not be null
* @param destDir the directory to place the copy in, must not be
* 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(File srcFile, 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.
*
* @param srcFile an existing file to copy, must not be null
* @param destDir the directory to place the copy in, must not be
* 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
* null
* @throws IOException if source or destination is invalid
* @throws IOException if an IO error occurs during copying
* @see #copyFile(File, File, boolean)
* @since Commons IO 1.3
*/
public static void copyFileToDirectory(File srcFile, File destDir, boolean preserveFileDate) throws IOException {
if (destDir == null) {
throw new NullPointerException("Destination must not be null");
}
if (!(destDir.exists() && destDir.isDirectory())) {
throw new IllegalArgumentException("Destination '" + destDir + "' is not a directory");
}
copyFile(srcFile, new File(destDir, srcFile.getName()), preserveFileDate);
}
// -----------------------------------------------------------------------
/**
* Recursively delete a directory.
*
* @param directory directory to delete
* @throws IOException in case deletion is unsuccessful
*/
public static void deleteDirectory(File directory) throws IOException {
if (!directory.exists()) {
return;
}
cleanDirectory(directory);
if (!directory.delete()) {
String message = "Unable to delete directory " + directory + ".";
throw new IOException(message);
}
}
/**
* Recursively schedule directory for deletion on JVM exit.
*
* @param directory directory to delete, must not be null
* @throws NullPointerException if the directory is null
* @throws IOException in case deletion is unsuccessful
*/
private static void deleteDirectoryOnExit(File directory) throws IOException {
if (!directory.exists()) {
return;
}
cleanDirectoryOnExit(directory);
directory.deleteOnExit();
}
/**
* Internal copy directory method.
*
* @param srcDir the validated source directory, must not be
* null
* @param destDir the validated destination directory, must not be
* null
* @param preserveFileDate whether to preserve the file date
* @throws IOException if an error occurs
* @since Commons IO 1.1
*/
private static void doCopyDirectory(File srcDir, File destDir, boolean preserveFileDate) throws IOException {
if (destDir.exists()) {
if (!destDir.isDirectory()) {
throw new IOException("Destination '" + destDir + "' exists but is not a directory");
}
} else {
if (!destDir.mkdirs()) {
throw new IOException("Destination '" + destDir + "' directory cannot be created");
}
if (preserveFileDate) {
destDir.setLastModified(srcDir.lastModified());
}
}
if (!destDir.canWrite()) {
throw new IOException("Destination '" + destDir + "' cannot be written to");
}
// recurse
File[] files = srcDir.listFiles();
if (files == null) { // null if security restricted
throw new IOException("Failed to list contents of " + srcDir);
}
for (int i = 0; i < files.length; i++) {
File copiedFile = new File(destDir, files[i].getName());
if (files[i].isDirectory()) {
doCopyDirectory(files[i], copiedFile, preserveFileDate);
} else {
doCopyFile(files[i], copiedFile, preserveFileDate);
}
}
}
/**
* Internal copy file method.
*
* @param srcFile the validated source file, must not be null
* @param destFile the validated destination file, must not be
* null
* @param preserveFileDate whether to preserve the file date
* @throws IOException if an error occurs
*/
private static void doCopyFile(File srcFile, File destFile, boolean preserveFileDate) throws IOException {
if (destFile.exists() && destFile.isDirectory()) {
throw new IOException("Destination '" + destFile + "' exists but is a directory");
}
FileInputStream input = new FileInputStream(srcFile);
try {
FileOutputStream output = new FileOutputStream(destFile);
try {
IOHelper.copy(input, output);
} finally {
IOHelper.closeQuietly(output);
}
} finally {
IOHelper.closeQuietly(input);
}
if (srcFile.length() != destFile.length()) {
throw new IOException("Failed to copy full contents from '" + srcFile + "' to '" + destFile + "'");
}
if (preserveFileDate) {
destFile.setLastModified(srcFile.lastModified());
}
}
/**
* Schedule 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 null
* @throws NullPointerException if the file is null
* @throws IOException in case deletion is unsuccessful
*/
public static void forceDeleteOnExit(File file) throws IOException {
if (file.isDirectory()) {
deleteDirectoryOnExit(file);
} else {
file.deleteOnExit();
}
}
}