package com.github.sommeri.less4j.utils;
import java.io.File;
import org.apache.commons.io.FilenameUtils;
//copied from org.apache.commons.io.FilenameUtils.doNormalize and customized
public class RelativeFilenameUtils {
private static final char SYSTEM_SEPARATOR = File.separatorChar;
private static final char UNIX_SEPARATOR = '/';
private static final char WINDOWS_SEPARATOR = '\\';
private static final char OTHER_SEPARATOR;
static {
if (SYSTEM_SEPARATOR == WINDOWS_SEPARATOR) {
OTHER_SEPARATOR = UNIX_SEPARATOR;
} else {
OTHER_SEPARATOR = WINDOWS_SEPARATOR;
}
}
public static String normalizeNoEndSeparator(String filename) {
return doNormalize(filename, SYSTEM_SEPARATOR, false);
}
private static String doNormalize(String filename, char separator, boolean keepSeparator) {
if (filename == null) {
return null;
}
int size = filename.length();
if (size == 0) {
return filename;
}
int prefix = FilenameUtils.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
char otherSeparator = separator == SYSTEM_SEPARATOR ? OTHER_SEPARATOR : SYSTEM_SEPARATOR;
for (int i = 0; i < array.length; i++) {
if (array[i] == otherSeparator) {
array[i] = separator;
}
}
// add extra separator on the end to simplify code below
boolean lastIsDirectory = true;
if (array[size - 1] != separator) {
array[size++] = separator;
lastIsDirectory = false;
}
// adjoining slashes
for (int i = prefix + 1; i < size; i++) {
if (array[i] == separator && array[i - 1] == 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] == separator && array[i - 1] == '.' && (i == prefix + 1 || array[i - 2] == separator)) {
if (i == size - 1) {
lastIsDirectory = true;
}
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 (isEndOfClimbUp(array, i, separator) && (i == prefix + 2 || array[i - 3] == separator)) {
//if (located in the beginning or after drive or server name)
if (i == prefix + 2) {
// Modified by SomMeri to allow relative paths
//return null;
continue outer;
}
if (i == size - 1) {
lastIsDirectory = true;
}
if (isEndOfClimbUp(array, i - 3, separator)) {
// We reached previous climb up. That can happen only if we are at the
// beginning of the path and the path is starting with some climbs. e.g.
// path looks somewhat like this: ../../something
//
// We cannot climb further, so no action is needed.
} else {
int j;
for (j = i - 4; j >= prefix; j--) {
if (array[j] == 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);
}
if (lastIsDirectory && keepSeparator) {
return new String(array, 0, size); // keep trailing separator
}
return new String(array, 0, size - 1); // lose trailing separator
}
private static boolean isEndOfClimbUp(char[] array, int position, char separator) {
return array[position] == separator && array[position - 1] == '.' && array[position - 2] == '.';
}
}