Files
android_translation_layer/src/api-impl/android/net/Uri.java
2024-04-09 18:19:13 +02:00

244 lines
6.2 KiB
Java

package android.net;
import java.net.URI;
import java.net.URISyntaxException;
import libcore.net.UriCodec;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import android.os.Parcelable;
public class Uri implements Parcelable {
public static final Uri EMPTY = new Uri();
public URI uri;
public static Uri parse(String s) {
Uri ret = new Uri();
try {
ret.uri = URI.create(s.trim());
} catch (IllegalArgumentException e) {
}
return ret;
}
public static String decode(String s) {
if (s == null) {
return null;
}
return UriCodec.decode(s, false, StandardCharsets.UTF_8, false);
}
public static String encode(String s) {
return encode(s, null);
}
/**
* -- FROM ANDROID, Licensed under the Apache License, Version 2.0 --
* Returns true if the given character is allowed.
*
* @param c character to check
* @param allow characters to allow
* @return true if the character is allowed or false if it should be
* encoded
*/
private final static int NOT_FOUND = -1;
private static boolean isAllowed(char c, String allow) {
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || "_-!.~'()*".indexOf(c) != NOT_FOUND || (allow != null && allow.indexOf(c) != NOT_FOUND);
}
/**
* -- FROM ANDROID, Licensed under the Apache License, Version 2.0 --
* Encodes characters in the given string as '%'-escaped octets
* using the UTF-8 scheme. Leaves letters ("A-Z", "a-z"), numbers
* ("0-9"), and unreserved characters ("_-!.~'()*") intact. Encodes
* all other characters with the exception of those specified in the
* allow argument.
*
* @param s string to encode
* @param allow set of additional characters to allow in the encoded form,
* null if no characters should be skipped
* @return an encoded version of s suitable for use as a URI component,
* or null if s is null
*/
private static final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray();
private static final String DEFAULT_ENCODING = "UTF-8";
public static String encode(String s, String allow) {
if (s == null) {
return null;
}
// Lazily-initialized buffers.
StringBuilder encoded = null;
int oldLength = s.length();
// This loop alternates between copying over allowed characters and
// encoding in chunks. This results in fewer method calls and
// allocations than encoding one character at a time.
int current = 0;
while (current < oldLength) {
// Start in "copying" mode where we copy over allowed chars.
// Find the next character which needs to be encoded.
int nextToEncode = current;
while (nextToEncode < oldLength && isAllowed(s.charAt(nextToEncode), allow)) {
nextToEncode++;
}
// If there's nothing more to encode...
if (nextToEncode == oldLength) {
if (current == 0) {
// We didn't need to encode anything!
return s;
} else {
// Presumably, we've already done some encoding.
encoded.append(s, current, oldLength);
return encoded.toString();
}
}
if (encoded == null) {
encoded = new StringBuilder();
}
if (nextToEncode > current) {
// Append allowed characters leading up to this point.
encoded.append(s, current, nextToEncode);
} else {
// assert nextToEncode == current
}
// Switch to "encoding" mode.
// Find the next allowed character.
current = nextToEncode;
int nextAllowed = current + 1;
while (nextAllowed < oldLength && !isAllowed(s.charAt(nextAllowed), allow)) {
nextAllowed++;
}
// Convert the substring to bytes and encode the bytes as
// '%'-escaped octets.
String toEncode = s.substring(current, nextAllowed);
try {
byte[] bytes = toEncode.getBytes(DEFAULT_ENCODING);
int bytesLength = bytes.length;
for (int i = 0; i < bytesLength; i++) {
encoded.append('%');
encoded.append(HEX_DIGITS[(bytes[i] & 0xf0) >> 4]);
encoded.append(HEX_DIGITS[bytes[i] & 0xf]);
}
} catch (UnsupportedEncodingException e) {
throw new AssertionError(e);
}
current = nextAllowed;
}
// Encoded could still be null at this point if s is empty.
return encoded == null ? s : encoded.toString();
}
public Builder buildUpon() {
Builder builder = new Builder();
builder.scheme = getScheme();
builder.authority = getAuthority();
builder.path = getPath();
builder.query = uri.getQuery();
return builder;
}
public static final class Builder {
private String scheme;
private String authority;
private String path;
private String query;
public Builder appendQueryParameter(String key, String value) {
this.query = (this.query != null ? this.query + "&" : "") + key + "=" + value;
return this;
}
public Builder scheme(String scheme) {
this.scheme = scheme;
return this;
}
public Builder authority(String authority) {
this.authority = authority;
return this;
}
public Builder encodedPath(String encodedPath) {
this.path = decode(encodedPath);
return this;
}
public Builder appendPath(String path) {
this.path = (this.path != null ? this.path : "") + "/" + path;
return this;
}
public Uri build() throws URISyntaxException {
if ("content".equals(scheme)) { // hack: content providers not yet supported
scheme = "file";
authority = null;
path = path.substring(path.indexOf("/"));
}
Uri ret = new Uri();
ret.uri = new URI(scheme, authority, path, query, null);
return ret;
}
public String toString() {
try {
return build().toString();
} catch (URISyntaxException e) {
return super.toString();
}
}
}
public String getScheme() {
return uri.getScheme();
}
public String getPath() {
return uri.getPath();
}
public String getAuthority() {
return uri.getAuthority();
}
@Override
public String toString() {
return String.valueOf(uri);
}
public static Uri fromFile(File file) {
Uri ret = new Uri();
ret.uri = file.toURI();
return ret;
}
public String getLastPathSegment() {
String[] segments = uri.getPath().split("/");
return segments[segments.length - 1];
}
public String getQueryParameter(String key) {
for (String pair : uri.getQuery().split("&")) {
if (pair.startsWith(key + "=")) {
return pair.substring(key.length() + 1);
}
}
return null;
}
}