webit.script.util.FileNameUtil Maven / Gradle / Ivy
Show all versions of webit-script Show documentation
// Copyright (c) 2013-2014, Webit Team. All Rights Reserved.
package webit.script.util;
/**
*
* @author Zqq
*/
public class FileNameUtil {
private static final char UNIX_SEPARATOR = '/';
private static final char WINDOWS_SEPARATOR = '\\';
private static boolean isSeparator(char ch) {
return (ch == UNIX_SEPARATOR) || (ch == WINDOWS_SEPARATOR);
}
public static String getPath(String filename) {
if (filename == null) {
return null;
}
int index;
if ((index = filename.lastIndexOf(UNIX_SEPARATOR)) < 0) {
return "";
}
return filename.substring(0, index + 1);
}
public static String concat(String basePath, String fullFilenameToAdd) {
if (basePath != null) {
int len = basePath.length();
int prefix = getPrefixLength(fullFilenameToAdd);
if (prefix == 0 && len != 0) {
if (isSeparator(basePath.charAt(len - 1))) {
return normalize(basePath.concat(fullFilenameToAdd));
}
return normalize(StringUtil.concat(basePath, "/", fullFilenameToAdd));
}
if (prefix > 0) {
return normalize(fullFilenameToAdd);
}
}
return null;
}
/**
* Internal method to perform the normalization.
*
* @param filename file name
* @return normalized filename
*/
public static String normalize(String filename) {
if (filename == null) {
return null;
}
int size = filename.length();
if (size == 0) {
return filename;
}
int prefix = getPrefixLength(filename);
if (prefix < 0) {
return null;
}
char[] array = new char[size + 2]; // +1 for possible extra slash, +2 for arraycopy
filename.getChars(0, filename.length(), array, 0);
// fix separators throughout
for (int i = 0; i < size; i++) {
if (array[i] == WINDOWS_SEPARATOR) {
array[i] = UNIX_SEPARATOR;
}
}
// add extra separator on the end to simplify code below
if (array[size - 1] != UNIX_SEPARATOR) {
array[size++] = UNIX_SEPARATOR;
}
// adjoining slashes
for (int i = prefix + 1; i < size; i++) {
if (array[i] == UNIX_SEPARATOR && array[i - 1] == UNIX_SEPARATOR) {
System.arraycopy(array, i, array, i - 1, size - i);
size--;
i--;
}
}
// dot slash
for (int i = prefix + 1; i < size; i++) {
if (array[i] == UNIX_SEPARATOR && array[i - 1] == '.'
&& (i == prefix + 1 || array[i - 2] == UNIX_SEPARATOR)) {
System.arraycopy(array, i + 1, array, i - 1, size - i);
size -= 2;
i--;
}
}
// double dot slash
outer:
for (int i = prefix + 2; i < size; i++) {
if (array[i] == UNIX_SEPARATOR && array[i - 1] == '.' && array[i - 2] == '.'
&& (i == prefix + 2 || array[i - 3] == UNIX_SEPARATOR)) {
if (i == prefix + 2) {
return null;
}
int j;
for (j = i - 4; j >= prefix; j--) {
if (array[j] == UNIX_SEPARATOR) {
// remove b/../ from a/b/../c
System.arraycopy(array, i + 1, array, j + 1, size - i);
size -= (i - j);
i = j + 1;
continue outer;
}
}
// remove a/../ from a/../c
System.arraycopy(array, i + 1, array, prefix, size - i);
size -= (i + 1 - prefix);
i = prefix + 1;
}
}
if (size <= 0) { // should never be less than 0
return "";
}
if (size <= prefix) { // should never be less than prefix
return new String(array, 0, size);
}
return new String(array, 0, size - 1); // lose trailing separator
}
// ---------------------------------------------------------------- prefix
/**
* Returns the length of the filename prefix, such as C:/
or
* ~/
.
*
* This method will handle a file in either Unix or Windows format.
*
* The prefix length includes the first slash in the full filename if
* applicable. Thus, it is possible that the length returned is greater than
* the length of the input string.
*
* Windows:
* a\b\c.txt --> "" --> relative
* \a\b\c.txt --> "\" --> current drive absolute
* C:a\b\c.txt --> "C:" --> drive relative
* C:\a\b\c.txt --> "C:\" --> absolute
* \\server\a\b\c.txt --> "\\server\" --> UNC
*
* Unix:
* a/b/c.txt --> "" --> relative
* /a/b/c.txt --> "/" --> absolute
* ~/a/b/c.txt --> "~/" --> current user
* ~ --> "~/" --> current user (slash added)
* ~user/a/b/c.txt --> "~user/" --> named user
* ~user --> "~user/" --> named user (slash added)
*
*
* The output will be the same irrespective of the machine that the code is
* running on. ie. both Unix and Windows prefixes are matched regardless.
*
* @param filename the filename to find the prefix in, null returns -1
* @return the length of the prefix, -1 if invalid or null
*/
private static int getPrefixLength(String filename) {
if (filename == null) {
return -1;
}
final int len;
if ((len = filename.length()) == 0) {
return 0;
}
final char ch0 = filename.charAt(0);
if (ch0 == '.') {
return 0;
}
if (ch0 == ':') {
return -1;
}
if (len == 1) {
if (isSeparator(ch0)) {
return 1;
}
return ch0 == '~' ? 2 : 0;
} else {
char ch1 = filename.charAt(1);
if (ch0 == '~') {
int posUnix = filename.indexOf(UNIX_SEPARATOR, 1);
if (posUnix == -1) {
return len + 1; // return a length greater than the input
}
return posUnix + 1;
}
if (ch1 == ':') {
if ((ch0 >= 'A' && ch0 <= 'Z') || (ch0 >= 'a' && ch0 <= 'z')) {
if (len == 2 || !isSeparator(filename.charAt(2))) {
return 2;
}
return 3;
}
return -1;
}
if (isSeparator(ch0)) {
if (isSeparator(ch1)) {
int posUnix = filename.indexOf(UNIX_SEPARATOR, 2);
int posWin = filename.indexOf(WINDOWS_SEPARATOR, 2);
if ((posUnix == -1 && posWin == -1) || posUnix == 2 || posWin == 2) {
return -1;
}
posUnix = (posUnix == -1 ? posWin : posUnix);
posWin = (posWin == -1 ? posUnix : posWin);
return Math.min(posUnix, posWin) + 1;
}
return 1;
}
return 0;
}
}
}