org.qedeq.base.io.Path Maven / Gradle / Ivy
/* This file is part of the project "Hilbert II" - http://www.qedeq.org
*
* Copyright 2000-2013, Michael Meyling .
*
* "Hilbert II" 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 2 of the License, or (at your option) any later version.
*
* This program 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.
*/
package org.qedeq.base.io;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.ArrayUtils;
import org.qedeq.base.utility.EqualsUtility;
import org.qedeq.base.utility.StringUtility;
/**
* A file path that leads to a directory or file and is absolute or relative.
* This abstraction of a file location was done to create relative file paths.
* This class makes some assumptions about a file path:
* "/" is the directory separator, "/" is also the root directory and ".." specifies
* the parent directory. "." is the current directory.
* A directory path must end with a "/" if a path ends not with a "/" it will be a file path
* (but the empty path "" is a directory).
*
* @author Michael Meyling
*/
public final class Path {
/** Directories that build up the path. If this is an absolute path the first
* name is empty. */
private final String[] path;
/** File name. */
private final String name;
/**
* Create file with given path and name.
*
* @param filePath Path to file with "/" as directory name separator. Relative directories
* are removed from the file path if possible. Must not be
* null
. Might be "" this is called the empty path.
*/
public Path(final String filePath) {
final String[] p = StringUtility.split(filePath, "/");
name = p[p.length - 1];
final String[] p2 = new String[p.length - 1];
System.arraycopy(p, 0, p2, 0, p2.length);
path = removeRelativeDirs(p2);
}
/**
* Create file with given path and name. Relative directories are removed from
* the file path if possible.
*
* @param dirPath Directory path to file with "/" as directory name separator.
* This value can end with a "/" but it must not be null
.
* @param fileName File name. It should not contain "/" but this is not checked.
*/
public Path(final String dirPath, final String fileName) {
this((dirPath.endsWith("/") ? StringUtility.split(dirPath.substring(0,
dirPath.length() - 1), "/") : StringUtility.split(dirPath, "/")), fileName);
}
/**
* Create file with given path and name. Relative directories are removed
* from the file path if possible.
*
* @param dirNames Directory names. If this is an absolute path the first
* name is empty. Must not be null
.
* @param fileName File name. It should not contain "/" but this is not checked.
*/
public Path(final String[] dirNames, final String fileName) {
path = removeRelativeDirs(dirNames);
name = (fileName != null ? fileName : "");
}
/**
* Create new file path relative to given one. If the original path or filePath
* is a relative path it will return filePath
.
*
* @param filePath Path to file relative to this
.
* @return Relative file path (if possible).
*/
public Path createRelative(final String filePath) {
final Path to = new Path(filePath);
if (isRelative()) {
// if from is relative, then we don't need to think further
return to;
}
if (to.isRelative()) {
return to;
}
// both are absolute so we try to navigate within the file system
// to get a relative path
int max = 0;
while (max < path.length && max < to.path.length) {
if (!"..".equals(path[max]) && EqualsUtility.equals(path[max], to.path[max])) {
max++;
} else {
break;
}
}
final String[] r = new String[path.length - max + to.path.length - max];
for (int i = max; i < path.length; i++) {
r[i - max] = "..";
}
for (int i = max; i < to.path.length; i++) {
r[i - max + path.length - max] = to.path[i];
}
return new Path(r, to.name);
}
/**
* Describes this path a directory?
*
* @return Is directory?
*/
public boolean isDirectory() {
return name.length() == 0;
}
/**
* Describes this path a file (and not a directory)?
*
* @return Is this a path to a file?
*/
public boolean isFile() {
return !isDirectory();
}
/**
* Is this an absolute path? If first path directory name is empty or ends with ":"
* (MS windows tribute) this is the case.
*
* @return Is absolute path?
*/
public boolean isAbsolute() {
return path.length > 0 && (path[0].length() == 0 || path[0].endsWith(":"));
}
/**
* Is this a relative path?
*
* @return Is this a relative path?
*/
public boolean isRelative() {
return !isAbsolute();
}
/**
* Get filename. Is "" if this is a pure directory path.
*
* @return File name. Might be "" but not null
.
*/
public String getFileName() {
return name;
}
/**
* Get directory of this path. This might be a relative or absolute
* path and ends with "/". (Only the empty path "" has no ending "/".)
*
* @return Directory this path points to. Will end with "/" if this is not
* an empty path.
*/
public String getDirectory() {
StringBuffer result = new StringBuffer(256);
for (int i = 0; i < path.length; i++) {
result.append(path[i]).append("/");
}
return result.toString();
}
/**
* Remove ".." and "." directories out of path if possible.
*
* @param dirNames Directories that build up the path.
* @return Directories that build up the same path.
*/
private String[] removeRelativeDirs(final String[] dirNames) {
List d = new ArrayList();
for (int i = 0; i < dirNames.length; i++) {
d.add(dirNames[i]);
}
for (int i = 0; i < d.size(); ) {
if (i > 0 && "..".equals(d.get(i)) && !"".equals(d.get(i - 1))
&& !"..".equals(d.get(i - 1))) {
d.remove(i - 1);
d.remove(i - 1);
i--;
} else if (".".equals(d.get(i))) {
d.remove(i);
} else {
i++;
}
}
return (String[]) d.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
}
public String toString() {
StringBuffer result = new StringBuffer(256);
for (int i = 0; i < path.length; i++) {
result.append(path[i]).append("/");
}
result.append(name);
return result.toString();
}
public boolean equals(final Object obj) {
if (!(obj instanceof Path)) {
return false;
}
final Path other = (Path) obj;
return EqualsUtility.equals(path, other.path) && name.equals(other.name);
}
public int hashCode() {
return toString().hashCode();
}
}