/*
* ====================================================================
* Copyright (c) 2004-2012 TMate Software Ltd. All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://svnkit.com/license.html
* If newer versions of this license are posted there, you may use a
* newer version instead, at your option.
* ====================================================================
*/
package org.tmatesoft.svn.core.internal.util;
import java.io.File;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.StringTokenizer;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
import org.tmatesoft.svn.util.SVNLogType;
/**
* @author TMate Software Ltd.
* @version 1.3
*/
public class SVNPathUtil {
public static final Comparator<String> PATH_COMPARATOR = new Comparator<String>() {
public int compare(String o1, String o2) {
if (o1 == o2) {
return 0;
} else if (o1 == null) {
return -1;
} else if (o2 == null) {
return 1;
}
return o1.replace('/', '\0').compareTo(o2.replace('/', '\0'));
}
};
public static boolean isCanonical(String path) {
return (path != null && path.equals(canonicalizePath(path)));
}
public static void checkPathIsValid(String path) throws SVNException {
for (int i = 0; i < path.length(); i++) {
char ch = path.charAt(i);
if (SVNEncodingUtil.isASCIIControlChar(ch)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.FS_PATH_SYNTAX, "Invalid control character ''{0}'' in path ''{1}''", new String[]{"0x" + SVNFormatUtil.getHexNumberFromByte((byte) ch), path});
SVNErrorManager.error(err, SVNLogType.DEFAULT);
}
}
}
public static String getAbsolutePath(String path){
if (path == null){
return null;
}
if (path.length() == 0 || "/".equals(path)){
return "/";
}
if (isURL(path)){
return path;
}
path = path.endsWith("/") ? path.substring(0, path.length() - "/".length()) : path;
return path.startsWith("/") ? path : "/" + path;
}
public static String validateFilePath(String path) {
path = path.replace(File.separatorChar, '/');
StringBuffer result = new StringBuffer();
List segments = new LinkedList();
for (StringTokenizer tokens = new StringTokenizer(path, "/", false); tokens.hasMoreTokens();) {
String segment = tokens.nextToken();
if ("..".equals(segment)) {
if (!segments.isEmpty()) {
segments.remove(segments.size() - 1);
} else {
File root = new File(System.getProperty("user.dir"));
while (root.getParentFile() != null) {
segments.add(0, root.getParentFile().getName());
root = root.getParentFile();
}
}
continue;
} else if (".".equals(segment) || segment.length() == 0) {
continue;
}
segments.add(segment);
}
if (path.length() > 0 && path.charAt(0) == '/') {
result.append("/");
}
if (path.length() > 1 && path.charAt(1) == '/') {
result.append("/");
}
for (Iterator tokens = segments.iterator(); tokens.hasNext();) {
String token = (String) tokens.next();
result.append(token);
if (tokens.hasNext()) {
result.append('/');
}
}
if (result.length() == 2 && result.charAt(result.length() - 1) == ':') {
result.append('/');
}
return result.toString();
}
public static String canonicalizePath(String path) {
if (path == null){
return null;
}
boolean isUrl = isURL(path);
StringBuffer result = new StringBuffer();
int i = 0;
for (; i < path.length(); i++) {
if (path.charAt(i) == '/' || path.charAt(i) == ':') {
break;
}
}
String scheme = null;
int index = 0;
if (i > 0 && i + 2 < path.length() && path.charAt(i) == ':' && path.charAt(i + 1) == '/' && path.charAt(i + 2) == '/') {
scheme = path.substring(0, i + 3);
result.append(scheme);
index = i + 3;
}
if (index < path.length() && path.charAt(index) == '/') {
result.append('/');
index++;
if (SVNFileUtil.isWindows && scheme == null && index < path.length() && path.charAt(index) == '/') {
result.append('/');
index++;
}
}
int segmentCount = 0;
while (index < path.length()) {
int nextIndex = index;
while (nextIndex < path.length() && path.charAt(nextIndex) != '/'
&& !(isUrl && (nextIndex + 2) < path.length() && path.charAt(nextIndex) == '%' && path.charAt(nextIndex + 1) == '2' &&
Character.toUpperCase(path.charAt(nextIndex + 2)) == 'F')) {
nextIndex++;
}
int slashLength = 0;
if (nextIndex < path.length()) {
if (path.charAt(nextIndex) == '/')
slashLength = 1;
else if (path.charAt(nextIndex) == '%')
slashLength = 3;
}
int segmentLength = nextIndex - index;
if (segmentLength == 0 || (segmentLength == 1 && path.charAt(index) == '.')
|| (isUrl && segmentLength == 3 && path.charAt(index) == '%' && path.charAt(index + 1) == '2' && Character.toUpperCase(path.charAt(index + 2)) == 'E')) {
} else {
result.append(path.substring(index, index + segmentLength));
if (slashLength > 0)
result.append('/');
segmentCount++;
}
index = nextIndex;
if (index < path.length()) {
index += slashLength;
}
}
if ((segmentCount > 0 || scheme != null) && result.charAt(result.length() - 1) == '/') {
result = result.delete(result.length() - 1, result.length());
}
if (SVNFileUtil.isWindows && segmentCount < 2 && result.length() >= 2 && result.charAt(0) == '/' && result.charAt(1) == '/') {
result = result.delete(0, 1);
}
return result.toString();
}
public static String canonicalizeAbsolutePath(String path) {
if (path == null) {
return null;
}
if ("".equals(path)) {
return "/";
}
if (!path.startsWith("/")) {
path = "/" + path;
}
StringBuffer canonicalizedPath = new StringBuffer();
boolean skipSlashes = false;
for (int i = 0; i < path.length(); i++) {
char ch = path.charAt(i);
if (ch == '/') {
if (skipSlashes) {
continue;
}
skipSlashes = true;
} else {
if (skipSlashes) {
skipSlashes = false;
}
}
canonicalizedPath.append(ch);
}
if (canonicalizedPath.length() > 1 && canonicalizedPath.charAt(canonicalizedPath.length() - 1) == '/') {
canonicalizedPath.deleteCharAt(canonicalizedPath.length() - 1);
}
return canonicalizedPath.toString();
}
public static String append(String f, String s) {
f = f == null ? "" : f;
s = s == null ? "" : s;
int l1 = f.length();
int l2 = s.length();
char[] r = new char[l1 + l2 + 2];
int index = 0;
for (int i = 0; i < l1; i++) {
char ch = f.charAt(i);
if (i + 1 == l1 && ch == '/') {
break;
}
r[index++] = ch;
}
for (int i = 0; i < l2; i++) {
char ch = s.charAt(i);
if (i == 0 && ch != '/' && index > 0) {
r[index++] = '/';
}
if (i + 1 == l2 && ch == '/') {
break;
}
r[index++] = ch;
}
return new String(r, 0, index);
}
public static boolean isSinglePathComponent(String name) {
/* Can't be empty or `..' */
if (name == null || "".equals(name) || "..".equals(name)) {
return true;
}
/* Slashes are bad */
if (name.indexOf('/') != -1) {
return false;
}
/* It is valid. */
return true;
}
public static String head(String path) {
for (int i = 0; i < path.length(); i++) {
if (path.charAt(i) == '/') {
return path.substring(0, i);
}
}
return path;
}
public static String removeHead(String path) {
for (int i = 0; i < path.length(); i++) {
if (path.charAt(i) == '/') {
int ind = i;
for (; ind < path.length(); ind++) {
if (path.charAt(ind) == '/') {
continue;
}
break;
}
return path.substring(ind);
}
}
return "";
}
public static String tail(String path) {
int index = path.length() - 1;
if (index >= 0 && index < path.length() && path.charAt(index) == '/') {
index--;
}
for (int i = index; i >= 0; i--) {
if (path.charAt(i) == '/') {
return path.substring(i + 1, index + 1);
}
}
return path;
}
public static String removeTail(String path) {
int index = path.length() - 1;
while (index >= 0) {
if (path.charAt(index) == '/') {
return path.substring(0, index);
}
index--;
}
return "";
}
public static String getCommonPathAncestor(String path1, String path2) {
if (path1 == null || path2 == null) {
return null;
}
path1 = path1.replace(File.separatorChar, '/');
path2 = path2.replace(File.separatorChar, '/');
int index = 0;
int separatorIndex = 0;
while (index < path1.length() && index < path2.length()) {
if (path1.charAt(index) != path2.charAt(index)) {
break;
}
if (path1.charAt(index) == '/') {
separatorIndex = index;
}
index++;
}
if (index == path1.length() && index == path2.length()) {
return path1;
} else if (index == path1.length() && path2.charAt(index) == '/') {
return path1;
} else if (index == path2.length() && path1.charAt(index) == '/') {
return path2;
}
return path1.substring(0, separatorIndex);
}
public static String condencePaths(String[] paths, Collection condencedPaths, boolean removeRedundantPaths) {
if (paths == null || paths.length == 0) {
return null;
}
if (paths.length == 1) {
return paths[0];
}
String rootPath = paths[0];
for (int i = 0; i < paths.length; i++) {
String url = paths[i];
rootPath = getCommonPathAncestor(rootPath, url);
}
if (condencedPaths != null && removeRedundantPaths) {
for (int i = 0; i < paths.length; i++) {
String path1 = paths[i];
if (path1 == null) {
continue;
}
for (int j = 0; j < paths.length; j++) {
if (i == j) {
continue;
}
String path2 = paths[j];
if (path2 == null) {
continue;
}
String common = getCommonPathAncestor(path1, path2);
if ("".equals(common) || common == null) {
continue;
}
// remove logner path here
if (common.equals(path1)) {
paths[j] = null;
} else if (common.equals(path2)) {
paths[i] = null;
}
}
}
for (int j = 0; j < paths.length; j++) {
String path = paths[j];
if (path != null && path.equals(rootPath)) {
paths[j] = null;
}
}
}
if (condencedPaths != null) {
for (int i = 0; i < paths.length; i++) {
String path = paths[i];
if (path == null) {
continue;
}
if (rootPath != null && !"".equals(rootPath)) {
path = path.substring(rootPath.length());
if (path.startsWith("/")) {
path = path.substring(1);
}
}
condencedPaths.add(path);
}
}
return rootPath;
}
public static int getSegmentsCount(String path) {
int count = path.length() > 0 ? 1 : 0;
// skip first char, then count number of '/'
for (int i = 1; i < path.length(); i++) {
if (path.charAt(i) == '/') {
count++;
}
}
return count;
}
public static boolean isAncestor(String parentPath, String childPath) {
parentPath = parentPath == null ? "" : parentPath;
childPath = childPath == null ? "" : childPath;
if (parentPath.length() == 0) {
return !childPath.startsWith("/");
}
if (childPath.startsWith(parentPath)) {
if (parentPath.length() != childPath.length() && !parentPath.endsWith("/") &&
childPath.charAt(parentPath.length()) != '/') {
if (parentPath.startsWith("file://") && childPath.startsWith("file://")) {
//HACK: maybe encoded back slashes (UNC path)?
String encodedSlash = SVNEncodingUtil.uriEncode("\\");
return parentPath.endsWith(encodedSlash) ||
childPath.substring(parentPath.length()).startsWith(encodedSlash);
}
return false;
}
return true;
}
return false;
}
/**
* Former pathIsChild.
*
* @param path
* @param pathChild
* @return
*/
public static String getPathAsChild(String path, String pathChild) {
if (path == null || pathChild == null) {
return null;
}
if (pathChild.compareTo(path) == 0) {
return null;
}
if (path.length() == 0 && !pathChild.startsWith("/")) {
return pathChild;
}
if (!path.endsWith("/")) { // We don't want to have /foobar being a child of /foo
path = path + "/";
}
if (pathChild.startsWith(path)) {
return pathChild.substring(path.length());
}
return null;
}
public static String getRelativePath(String parent, String child) {
parent = parent.replace(File.separatorChar, '/');
child = child.replace(File.separatorChar, '/');
String relativePath = getPathAsChild(parent, child);
return relativePath == null ? "" : relativePath;
}
public static boolean isURL(String pathOrUrl) {
pathOrUrl = pathOrUrl != null ? pathOrUrl.toLowerCase() : null;
return pathOrUrl != null
&& (pathOrUrl.startsWith("http://")
|| pathOrUrl.startsWith("https://")
|| pathOrUrl.startsWith("svn://")
|| (pathOrUrl.startsWith("svn+") && pathOrUrl.indexOf("://") > 4)
|| pathOrUrl.startsWith("file://"));
}
public static boolean isWithinBasePath(String basePath, String path) {
return "".equals(basePath) || (path.startsWith(basePath) && (path.length() == basePath.length() ||
path.charAt(basePath.length()) == '/'));
}
public static boolean isAbsolute(String path) {
if (path == null || path.length() == 0) {
return false;
}
if (path.charAt(0) == '/') {
return true;
}
if (SVNFileUtil.isWindows && path.length() > 1) {
char ch0 = path.charAt(0);
char ch1 = path.charAt(1);
if (((ch0 >= 'A' && ch0 <= 'Z') || (ch0 >= 'a' && ch0 <= 'z')) && ch1 == ':') {
return true;
}
}
return false;
}
}