blob: 5d5f27deedac2d7c7ecd252ab50d0e3427e29316 [file] [log] [blame]
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package libcore.net.url;
import java.util.Locale;
public final class UrlUtils {
private UrlUtils() {
}
/**
* Returns the path will relative path segments like ".." and "." resolved.
* The returned path will not necessarily start with a "/" character. This
* handles ".." and "." segments at both the beginning and end of the path.
*
* @param discardRelativePrefix true to remove leading ".." segments from
* the path. This is appropriate for paths that are known to be
* absolute.
*/
public static String canonicalizePath(String path, boolean discardRelativePrefix) {
// the first character of the current path segment
int segmentStart = 0;
// the number of segments seen thus far that can be erased by sequences of '..'.
int deletableSegments = 0;
for (int i = 0; i <= path.length(); ) {
int nextSegmentStart;
if (i == path.length()) {
nextSegmentStart = i;
} else if (path.charAt(i) == '/') {
nextSegmentStart = i + 1;
} else {
i++;
continue;
}
/*
* We've encountered either the end of a segment or the end of the
* complete path. If the final segment was "." or "..", remove the
* appropriate segments of the path.
*/
if (i == segmentStart + 1 && path.regionMatches(segmentStart, ".", 0, 1)) {
// Given "abc/def/./ghi", remove "./" to get "abc/def/ghi".
path = path.substring(0, segmentStart) + path.substring(nextSegmentStart);
i = segmentStart;
} else if (i == segmentStart + 2 && path.regionMatches(segmentStart, "..", 0, 2)) {
if (deletableSegments > 0 || discardRelativePrefix) {
// Given "abc/def/../ghi", remove "def/../" to get "abc/ghi".
deletableSegments--;
int prevSegmentStart = path.lastIndexOf('/', segmentStart - 2) + 1;
path = path.substring(0, prevSegmentStart) + path.substring(nextSegmentStart);
i = segmentStart = prevSegmentStart;
} else {
// There's no segment to delete; this ".." segment must be retained.
i++;
segmentStart = i;
}
} else {
if (i > 0) {
deletableSegments++;
}
i++;
segmentStart = i;
}
}
return path;
}
/**
* Returns a path that can be safely concatenated with {@code authority}. If
* the authority is null or empty, this can be any path. Otherwise the paths
* run together like {@code http://android.comindex.html}.
*/
public static String authoritySafePath(String authority, String path) {
if (authority != null && !authority.isEmpty() && !path.isEmpty() && !path.startsWith("/")) {
return "/" + path;
}
return path;
}
/**
* Returns the scheme prefix like "http" from the URL spec, or null if the
* spec doesn't start with a scheme. Scheme prefixes match this pattern:
* {@code alpha ( alpha | digit | '+' | '-' | '.' )* ':'}
*/
public static String getSchemePrefix(String spec) {
int colon = spec.indexOf(':');
if (colon < 1) {
return null;
}
for (int i = 0; i < colon; i++) {
char c = spec.charAt(i);
if (!isValidSchemeChar(i, c)) {
return null;
}
}
return spec.substring(0, colon).toLowerCase(Locale.US);
}
public static boolean isValidSchemeChar(int index, char c) {
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
return true;
}
if (index > 0 && ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.')) {
return true;
}
return false;
}
/**
* Returns the index of the first char of {@code chars} in {@code string}
* bounded between {@code start} and {@code end}. This returns {@code end}
* if none of the characters exist in the requested range.
*/
public static int findFirstOf(String string, String chars, int start, int end) {
for (int i = start; i < end; i++) {
char c = string.charAt(i);
if (chars.indexOf(c) != -1) {
return i;
}
}
return end;
}
}