com.googlecode.kevinarpe.papaya.PathUtils Maven / Gradle / Ivy
package com.googlecode.kevinarpe.papaya;
/*
* #%L
* This file is part of Papaya.
* %%
* Copyright (C) 2013 - 2014 Kevin Connor ARPE ([email protected])
* %%
* Papaya is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GPL Classpath Exception:
* This project is subject to the "Classpath" exception as provided in
* the LICENSE file that accompanied this code.
*
* Papaya is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Papaya. If not, see .
* #L%
*/
import com.googlecode.kevinarpe.papaya.annotation.FullyTested;
import com.googlecode.kevinarpe.papaya.argument.ObjectArgs;
import com.googlecode.kevinarpe.papaya.argument.StringArgs;
import com.googlecode.kevinarpe.papaya.exception.PathException;
import com.googlecode.kevinarpe.papaya.exception.PathExceptionReason;
import java.io.File;
/**
* @author Kevin Connor ARPE ([email protected])
*/
public final class PathUtils {
// Disable default constructor
private PathUtils() {
}
/*
* TextFileReader:
* Set delimiter
* Keep/discard trailing delim
* Header/no header
* As: single string, list of lines, array of array, list of list, list of maps, "table"
* @throws IOException
*/
/**
* This is a convenience method for {@link #isRootDirectory(File)}.
*
* @throws NullPointerException
* if {@code pathname} is {@code null}
* @throws IllegalArgumentException
* if {@code pathname} is empty
*/
@FullyTested
public static boolean isRootDirectory(String pathname) {
StringArgs.checkNotEmpty(pathname, "pathname");
File path = new File(pathname);
boolean b = isRootDirectory(path);
return b;
}
/**
* Tests if a directory path is a root directory. On Windows, there can be many root
* directories, such A:\, B:\, C:\, D:\, etc. On UNIX (and its variants, including Mac OS X),
* there is only one root directory: /
*
* @param path
* any path to test, including existing file paths, or non-existant paths
*
* @return true if path is a root directory, else false
*
* @throws NullPointerException
* if {@code path} is {@code null}
*/
@FullyTested
public static boolean isRootDirectory(File path) {
ObjectArgs.checkNotNull(path, "path");
File absPath = path.getAbsoluteFile();
File parent = absPath.getParentFile();
boolean b = (null == parent);
return b;
}
/**
* This is a convenience method for {@link #makeDirectory(File)}.
*
* @throws NullPointerException
* if {@code pathname} is {@code null}
* @throws IllegalArgumentException
* if {@code pathname} is empty
*/
@FullyTested
public static File makeDirectory(String pathname)
throws PathException {
StringArgs.checkNotEmpty(pathname, "pathname");
File path = new File(pathname);
File result = makeDirectory(path);
return result;
}
/**
* Improved version of {@link File#mkdir()} with detailed error reporting via exceptions.
* The creation of directories may fail for a wide variety of reasons. Each reason is
* specifically codes into the thrown exception.
*
* This method does not throw an exception if directory already exists.
*
* This method throws an exception if any parent directory does not exist. Thus, in almost all
* "real world" cases, you should instead call {@link #makeDirectoryAndParents(File)}. This
* may save some sleepness nights when a parent directory is unexpectedly missing.
*
* @param path
* path for new directory
*
* @return reference to input path
*
* @throws NullPointerException
* if {@code path} is {@code null}
* @throws PathException
*
* - with reason {@link PathExceptionReason#PATH_IS_NORMAL_FILE}
* if {@code path} exists as a file
* - with reason {@link PathExceptionReason#PARENT_PATH_DOES_NOT_EXIST}
* if parent directory for {@code path} does not exist
* - with reason {@link PathExceptionReason#PARENT_PATH_IS_NORMAL_FILE}
* if parent directory for {@code path} exists as a file
* - with reason {@link PathExceptionReason#PARENT_PATH_IS_NON_WRITABLE_DIRECTORY}
* if parent directory for {@code path} exists as a directory, but is not
* writable
* - with reason {@link PathExceptionReason#PATH_DISK_PARTITION_IS_FULL}
* if disk partition for directory is full
* - with reason {@link PathExceptionReason#PARENT_PATH_DISK_PARTITION_IS_FULL}
* if disk partition for parent directory is full
* - with reason {@link PathExceptionReason#UNKNOWN}
* if reason for error is unknown
*
*
* @see #makeDirectory(String)
* @see #makeDirectoryAndParents(File)
*/
@FullyTested
public static File makeDirectory(File path)
throws PathException {
ObjectArgs.checkNotNull(path, "path");
if (path.mkdir() || path.isDirectory()) {
return path;
}
_throwExceptionAfterMkdirFailed(path, false);
return path;
}
/**
* This is a convenience method for {@link #makeDirectoryAndParents(File)}.
*
* @throws NullPointerException
* if {@code pathname} is {@code null}
* @throws IllegalArgumentException
* if {@code pathname} is empty
*/
@FullyTested
public static File makeDirectoryAndParents(String pathname)
throws PathException {
StringArgs.checkNotEmpty(pathname, "pathname");
File path = new File(pathname);
File result = makeDirectoryAndParents(path);
return result;
}
/**
* Improved version of {@link File#mkdirs()} with detailed error reporting via exceptions.
* The creation of directories may fail for a wide variety of reasons. Each reason is
* specifically codes into the thrown exception.
*
* This method does not throw an exception if directory already exists.
*
* @param path
* path for new directory
*
* @return reference to input path
*
* @throws NullPointerException
* if {@code path} is {@code null}
* @throws PathException
*
* - with reason {@link PathExceptionReason#PATH_IS_NORMAL_FILE}
* if {@code path} exists as a file
* - with reason {@link PathExceptionReason#PARENT_PATH_IS_NORMAL_FILE}
* if parent directory for {@code path} exists as a file
* - with reason {@link PathExceptionReason#PARENT_PATH_IS_NON_WRITABLE_DIRECTORY}
* if parent directory for {@code path} exists as a directory, but is not writable
* - with reason {@link PathExceptionReason#PATH_DISK_PARTITION_IS_FULL}
* if disk partition for directory is full
* - with reason {@link PathExceptionReason#PARENT_PATH_DISK_PARTITION_IS_FULL}
* if disk partition for parent directory is full
* - with reason {@link PathExceptionReason#UNKNOWN}
* if reason for error is unknown
*
*
* @see #makeDirectoryAndParents(String)
* @see #makeDirectory(File)
*/
@FullyTested
public static File makeDirectoryAndParents(File path)
throws PathException {
ObjectArgs.checkNotNull(path, "path");
if (path.mkdirs() || path.isDirectory()) {
return path;
}
_throwExceptionAfterMkdirFailed(path, true);
return path;
}
private static void _throwExceptionAfterMkdirFailed(File path, boolean parents)
throws PathException {
String desc = (parents ? "directory (and parents)" : "directory");
if (path.isFile()) {
String msg = String.format(
"Failed to make %s: Path exists as a file: '%s'",
desc,
path.getAbsolutePath());
throw new PathException(
PathExceptionReason.PATH_IS_NORMAL_FILE, path, null, msg);
}
if (path.getTotalSpace() > 0 && 0 == path.getFreeSpace()) {
String msg = String.format(
"Failed to make %s: '%s'%n\tDisk partition is full",
desc,
path.getAbsolutePath());
throw new PathException(
PathExceptionReason.PATH_DISK_PARTITION_IS_FULL, path, null, msg);
}
File absPath = path.getAbsoluteFile();
File parentPath = absPath.getParentFile();
// If (null == parentPath), then path is a root directory.
// But a root directory will never fail in call to mkdir/mkdirs().
// So we don't need to check if parentPath is null here.
do {
if (!parents && !parentPath.exists()) {
String msg = String.format(
"Failed to make %s: '%s'"
+ "%n\tParent path does not exist: '%s'",
desc,
path.getAbsolutePath(),
parentPath.getAbsolutePath());
throw new PathException(
PathExceptionReason.PARENT_PATH_DOES_NOT_EXIST, path, parentPath, msg);
}
if (parentPath.isFile()) {
String msg = String.format(
"Failed to make %s: '%s'"
+ "%n\tParent path exists as a file: '%s'",
desc,
path.getAbsolutePath(),
parentPath.getAbsolutePath());
throw new PathException(
PathExceptionReason.PARENT_PATH_IS_NORMAL_FILE, path, parentPath, msg);
}
if (parentPath.isDirectory() && !parentPath.canWrite()) {
String msg = String.format(
"Failed to make %s: '%s'"
+ "%n\tParent path exists as a directory, but is not writable: '%s'",
desc,
path.getAbsolutePath(),
parentPath.getAbsolutePath());
throw new PathException(
PathExceptionReason.PARENT_PATH_IS_NON_WRITABLE_DIRECTORY,
path,
parentPath,
msg);
}
if (parentPath.getTotalSpace() > 0 && 0 == parentPath.getFreeSpace()) {
String msg = String.format(
"Failed to make %s: '%s'%n\tDisk partition for parent path is full: '%s'",
desc,
path.getAbsolutePath(),
parentPath.getAbsolutePath());
throw new PathException(
PathExceptionReason.PARENT_PATH_DISK_PARTITION_IS_FULL, path, null, msg);
}
// Prepare for next iteration.
parentPath = parentPath.getParentFile();
}
while (parents && null != parentPath);
String msg = String.format(
"Failed to make %s: '%s'"
+ "%n\tUnknown reason",
desc,
path.getAbsolutePath());
throw new PathException(PathExceptionReason.UNKNOWN, path, null, msg);
}
/**
* This is a convenience method for {@link #removeDirectory(File)}.
*
* @throws NullPointerException
* if {@code pathname} is {@code null}
* @throws IllegalArgumentException
* if {@code pathname} is empty
*/
@FullyTested
public static void removeDirectory(String pathname)
throws PathException {
StringArgs.checkNotEmpty(pathname, "pathname");
File path = new File(pathname);
removeDirectory(path);
}
/**
* Improved version of {@link File#delete()} (for directories) with detailed error reporting
* via exceptions.
*
* This method does not throw an exception if directory does not exist.
*
* This method throws an exception if the directory is not empty -- contains any files or
* sub-directories.
*
* @param path
* path to empty directory to remove
*
* @throws NullPointerException
* if {@code path} is {@code null}
* @throws PathException
*
* - with reason {@link PathExceptionReason#PATH_IS_NORMAL_FILE}
* if {@code path} exists as a file
* - with reason {@link PathExceptionReason#PATH_IS_ROOT_DIRECTORY}
* if {@code path} exists as a root directory
* - with reason {@link PathExceptionReason#PARENT_PATH_IS_NON_WRITABLE_DIRECTORY}
* if parent directory for {@code path} exists as a directory, but is not writable
* - with reason {@link PathExceptionReason#UNKNOWN}
* if reason for error is unknown
*
*
* @see #removeDirectory(String)
*/
@FullyTested
public static void removeDirectory(File path)
throws PathException {
ObjectArgs.checkNotNull(path, "path");
if ((path.isDirectory() && path.delete()) || !path.exists()) {
return;
}
if (path.isFile()) {
String msg = String.format(
"Failed to remove directory: Path exists as a file: '%s'",
path.getAbsolutePath());
throw new PathException(
PathExceptionReason.PATH_IS_NORMAL_FILE, path, null, msg);
}
else {
File absPath = path.getAbsoluteFile();
File parentPath = absPath.getParentFile();
if (null == parentPath) {
String msg = String.format(
"Failed to remove directory: Path exists as a root directory: '%s'",
path.getAbsolutePath());
throw new PathException(
PathExceptionReason.PATH_IS_ROOT_DIRECTORY, path, null, msg);
}
if (parentPath.isDirectory() && !parentPath.canWrite()) {
String msg = String.format(
"Failed to remove directory: '%s'"
+ "%n\tParent path exists as a directory, but is not writable: '%s'",
path.getAbsolutePath(),
parentPath.getAbsolutePath());
throw new PathException(
PathExceptionReason.PARENT_PATH_IS_NON_WRITABLE_DIRECTORY,
path,
parentPath,
msg);
}
}
String msg = String.format(
"Failed to remove directory: '%s'"
+ "%n\tUnknown reason",
path.getAbsolutePath());
throw new PathException(PathExceptionReason.UNKNOWN, path, null, msg);
}
/**
* This is a convenience method for {@link #removeFile(File)}.
*
* @throws NullPointerException
* if {@code pathname} is {@code null}
* @throws IllegalArgumentException
* if {@code pathname} is empty
*/
@FullyTested
public static void removeFile(String pathname)
throws PathException {
StringArgs.checkNotEmpty(pathname, "pathname");
File path = new File(pathname);
removeFile(path);
}
/**
* Improved version of {@link File#delete()} (for files) with detailed error reporting
* via exceptions.
*
* This method does not throw an exception if file does not exist.
*
* @param path
* path to file to remove
*
* @throws NullPointerException
* if {@code path} is {@code null}
* @throws PathException
*
* - with reason {@link PathExceptionReason#PATH_IS_DIRECTORY}
* if {@code path} exists as a directory
* - with reason {@link PathExceptionReason#PARENT_PATH_IS_NON_WRITABLE_DIRECTORY}
* if parent directory for {@code path} exists as a directory, but is not writable
* - with reason {@link PathExceptionReason#UNKNOWN} if reason for error
* is unknown
*
*
* @see #removeFile(String)
*/
@FullyTested
public static void removeFile(File path)
throws PathException {
ObjectArgs.checkNotNull(path, "path");
if ((path.isFile() && path.delete()) || !path.exists()) {
return;
}
if (path.isDirectory()) {
String msg = String.format(
"Failed to remove file: Path exists as a directory: '%s'",
path.getAbsolutePath());
throw new PathException(
PathExceptionReason.PATH_IS_DIRECTORY, path, null, msg);
}
else {
File absPath = path.getAbsoluteFile();
File parentPath = absPath.getParentFile();
if (parentPath.isDirectory() && !parentPath.canWrite()) {
String msg = String.format(
"Failed to remove file: '%s'"
+ "%n\tParent path exists as a directory, but is not writable: '%s'",
path.getAbsolutePath(),
parentPath.getAbsolutePath());
throw new PathException(
PathExceptionReason.PARENT_PATH_IS_NON_WRITABLE_DIRECTORY,
path,
parentPath,
msg);
}
}
String msg = String.format(
"Failed to remove file: '%s'"
+ "%n\tUnknown reason",
path.getAbsolutePath());
throw new PathException(PathExceptionReason.UNKNOWN, path, null, msg);
}
// TODO: Reserve for next release
// Ideas: list, remove, other random actions... like chmod, touch, etc.
// Ref: http://stackoverflow.com/a/10337535/257299
// /**
// *
// * @param path
// * @return list of file and directory paths.
// *
// * - File paths appear before their parent directory paths.
// * - Order is most deep to most shallow.
// *
// *
// * @throws PathException
// */
// public static List recursiveListFilePaths(File path)
// throws PathException {
// PathArgs.checkDirectoryExists(path, "path");
//
// LinkedList pathList = new LinkedList();
// FileFilter optFileFilter = null;
// _coreRecursiveListFilePaths(path, pathList, optFileFilter);
// pathList.removeLast();
// List pathListCopy = ImmutableList.copyOf(pathList);
// return pathListCopy;
// }
//
// private static void _coreRecursiveListFilePaths(
// File dirPath, LinkedList pathList, FileFilter optFileFilter)
// throws PathException {
// File[] childPathArr = _listFiles(dirPath);
// pathList.addFirst(dirPath);
// LinkedList childDirPathList = new LinkedList();
// for (File childPath: childPathArr) {
// if (childPath.isDirectory()) {
// if (null != optFileFilter && !optFileFilter.accept(childPath)) {
// continue;
// }
// pathList.addFirst(childPath);
// childDirPathList.add(childPath);
// }
// }
// for (File childPath: childPathArr) {
// if (childPath.isFile()) {
// if (null != optFileFilter && !optFileFilter.accept(childPath)) {
// continue;
// }
// pathList.addFirst(childPath);
// }
// }
// for (File childDirPath: childDirPathList) {
// _coreRecursiveListFilePaths(childDirPath, pathList, optFileFilter);
// }
// }
//
// private static File[] _listFiles(File dirPath)
// throws PathException {
// File[] childPathArr = dirPath.listFiles();
// if (null == childPathArr) {
// if (!dirPath.exists()) {
// String msg = String.format(
// "Failed to list files for path (does not exist): '%s'",
// dirPath.getAbsolutePath());
// throw new PathException(
// PathExceptionReason.PATH_DOES_NOT_EXIST, dirPath, null, msg);
// }
// if (dirPath.isFile()) {
// String msg = String.format(
// "Failed to list files for path (exists as file, not directory): '%s'",
// dirPath.getAbsolutePath());
// throw new PathException(PathExceptionReason.PATH_IS_NORMAL_FILE, dirPath, null, msg);
// }
// // Exists + Directory...
// if (!dirPath.canExecute()) {
// String msg = String.format(
// "Failed to list files for path (execute permission not set): '%s'",
// dirPath.getAbsolutePath());
// throw new PathException(
// PathExceptionReason.PATH_IS_NON_EXECUTABLE_DIRECTORY, dirPath, null, msg);
// }
// String msg = String.format(
// "Failed to list files for path (unknown error): '%s'",
// dirPath.getAbsolutePath());
// throw new PathException(PathExceptionReason.UNKNOWN, dirPath, null, msg);
// }
// return childPathArr;
// }
//
// public static void main(String[] argArr) {
// System.out.println(new File("a/b/c").getAbsolutePath());
// System.out.println(System.getProperty("user.dir"));
// File[] arr = new File("nono").listFiles();
//
// }
}