/** * Normalize the given path. The following transformation takes place: * * <ol> * <li>All `.' segments are removed. * <li>Each `..' segment which can be paired with a prior non-`..' segment is removed along with * the preceding segment. * <li>A `.' segment is added to the front if the first segment contains a colon (`:'). This is * a deviation from the RFC, which prevents confusion between the path and the scheme. * </ol> * * <p>The resulting URI will be free of `.' and `..' segments, barring those that were prepended * or which couldn't be paired, respectively. * * @param relativePath the relative path to be normalized. * @return the normalized path. */ private String normalizePath(String relativePath) { /* This follows the algorithm in section 5.2.4. of RFC3986, but doesn't modify the input buffer. */ StringBuilder input = new StringBuilder(relativePath); StringBuilder output = new StringBuilder(); int start = 0; while (start < input.length()) { /* A */ if (input.indexOf("../", start) == start) { start += 3; continue; } if (input.indexOf("./", start) == start) { start += 2; continue; } /* B */ if (input.indexOf("/./", start) == start) { start += 2; continue; } if (input.indexOf("/.", start) == start && input.charAt(start + 2) != '.') { start += 1; input.setCharAt(start, '/'); continue; } /* C */ if (input.indexOf("/../", start) == start) { start += 3; removeLastSegment(output); continue; } if (input.indexOf("/..", start) == start) { start += 2; input.setCharAt(start, '/'); removeLastSegment(output); continue; } /* D */ if (start == input.length() - 1 && input.indexOf(".", start) == start) { input.delete(0, 1); continue; } if (start == input.length() - 2 && input.indexOf("..", start) == start) { input.delete(0, 2); continue; } /* E */ int indexOfSlash = input.indexOf("/", start); while (indexOfSlash == start) { output.append("/"); ++start; indexOfSlash = input.indexOf("/", start); } if (indexOfSlash == -1) indexOfSlash = input.length(); output.append(input.substring(start, indexOfSlash)); start = indexOfSlash; } return output.toString(); }