Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(928)

Unified Diff: sdk/lib/core/uri.dart

Issue 16019002: Merge the dart:uri library into dart:core and update the Uri class (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Add missing files Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: sdk/lib/core/uri.dart
diff --git a/sdk/lib/core/uri.dart b/sdk/lib/core/uri.dart
new file mode 100644
index 0000000000000000000000000000000000000000..86f37fb1ef31bee3cd6b7e7997e5d51b8d7f74a6
--- /dev/null
+++ b/sdk/lib/core/uri.dart
@@ -0,0 +1,787 @@
+// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+part of dart.core;
+
+/**
+ * A parsed URI, as specified by RFC-3986, http://tools.ietf.org/html/rfc3986.
+ */
+class Uri {
+ int _port;
+
+ /*
+ * Returns the URI scheme. If there is no scheme the empty string
floitsch 2013/05/24 20:37:27 All these comments are dartdocs. -> /** instead of
Søren Gjesse 2013/05/27 10:47:21 Of cause - thanks.
+ * will be returned.
floitsch 2013/05/24 20:37:27 No need for future. If there is no scheme the empt
Søren Gjesse 2013/05/27 10:47:21 Done. Similar change for all Uri component proper
+ */
+ final String scheme;
+
+ /*
+ * Returns the user info part of the URI authority. If there is no
+ * user info in the authority the empty string will be returned.
+ */
+ final String userInfo;
+
+ /*
+ * Returns the host part of the URI authority. If there is no
+ * authority and hence no host the empty string will be returned.
+ */
+ final String host;
+
+ /*
+ * Returns the port part of the URI authority. If there is no
+ * port in the authority 0 will be returned.
+ */
+ int get port => _port;
+
+ /*
+ * Returns the URI path. The returned path is encoded. To get direct
+ * access to the decoded path use [pathSegement]. If there is no
floitsch 2013/05/24 20:37:27 pathSegments
Søren Gjesse 2013/05/27 10:47:21 Done.
+ * path the empty string will be returned.
+ */
+ final String path;
+
+ /*
+ * Returns the URI query. The returned query is encoded. To get
+ * direct access to the decoded query use [queryParameters]. If
+ * there is no query the empty string will be returned.
+ */
+ final String query;
+
+ /*
+ * Returns the URI fragment. If there is no fragment the empty string
+ * will be returned.
+ */
+ final String fragment;
+
+ /**
+ * Creates a new URI object by parsing a URI string.
+ */
+ static Uri parse(String uri) => new Uri._fromMatch(_splitRe.firstMatch(uri));
+
+ Uri._fromMatch(Match m) :
+ this(scheme: _emptyIfNull(m[_COMPONENT_SCHEME]),
+ userInfo: _emptyIfNull(m[_COMPONENT_USER_INFO]),
+ host: _eitherOf(
+ m[_COMPONENT_HOST], m[_COMPONENT_HOST_IPV6]),
+ port: _parseIntOrZero(m[_COMPONENT_PORT]),
+ path: _emptyIfNull(m[_COMPONENT_PATH]),
+ query: _emptyIfNull(m[_COMPONENT_QUERY_DATA]),
+ fragment: _emptyIfNull(m[_COMPONENT_FRAGMENT]));
+
+ /*
+ * Create a new URI from its components.
+ *
+ * The URI scheme is set through [scheme]. The scheme will be
+ * normalized to all lowercase letters.
+ *
+ * The URI user info of the domain is set through [userInfo].
+ *
+ * The URI host part of the domain is set through [host]. The host
+ * can either be a hostname, a IPv4 address or an IPv6 address,
+ * contained in '[' and ']'. If the host contains a ':' character,
+ * the host will have the '[' and ']' added.
+ *
+ * The URI port is set through [port]. The port is normalized for
+ * scheme http and https where port 80 and port 443 respectively
+ * will be set as 0.
floitsch 2013/05/24 20:37:27 is set to 0.
Søren Gjesse 2013/05/27 10:47:21 This was a broken sentence. Removed the "as 0" fro
+ *
+ * The path component can be specified using either [path] or
+ * [pathSegments]. When [path] is used the provided string is
+ * expected to be fully percent-encoded and will be used in its
floitsch 2013/05/24 20:37:27 is used
Søren Gjesse 2013/05/27 10:47:21 Done.
+ * literal form for the URI path. When [pathSegments] is used each
+ * of the provided segments will be percent encoded and joined using
+ * the forward slash separator.
+ *
+ * The query component can be specified using either [query] or
+ * [queryParameters]. When [query] is used the provided string is
+ * expected to be fully percent-encoded and will be used in its
+ * literal form for the URI query. When [queryParameters] is used xxx
floitsch 2013/05/24 20:37:27 xxx
Søren Gjesse 2013/05/27 10:47:21 My mark of unfinished comment - sorry.
+ *
+ * The URI fragment is set through [fragment].
+ */
+ Uri({scheme,
+ this.userInfo: "",
+ this.host: "",
+ port: 0,
+ String path,
+ List<String> pathSegments,
+ String query,
+ Map<String, String> queryParameters,
+ fragment: ""}) :
+ scheme = _makeScheme(scheme),
+ path = _makePath(path, pathSegments),
+ query = _makeQuery(query, queryParameters),
+ fragment = _makeFragment(fragment) {
+ // Perform scheme specific normalization.
+ if (scheme == "http" && port == 80) {
+ _port = 0;
+ } else if (scheme == "https" && port == 443) {
+ _port = 0;
+ } else {
+ _port = port;
+ }
+ }
+
+ /*
+ * Returns the URI path split into its segments. Each of the
+ * segments in the returned list have been decoded. If the path is
+ * empty the empty list will be returned.
+ */
+ List<String> get pathSegments {
+ if (path == "") return [];
floitsch 2013/05/24 20:37:27 const <String>[]; ?
Søren Gjesse 2013/05/27 10:47:21 Done.
+ return path.split("/").map(Uri.decodeComponent).toList(growable: false);
+ }
+
+ /*
+ * Returns the URI query split into a map according to the rules
+ * specified for FORM post in the HTML 4.01 specification. Each key
+ * and value in the returned map have been decoded. If there is no
+ * query the empty map will be returned.
+ */
+ Map<String, String> get queryParameters {
+ return query.split("&").fold({}, (map, element) {
+ int index = element.indexOf("=");
+ if (index == -1) {
+ if (!element.isEmpty) map[element] = "";
+ } else if (index != 0) {
+ var key = element.substring(0, index);
+ var value = element.substring(index + 1, element.length);
floitsch 2013/05/24 20:37:27 substring doesn't need the second argument if it i
Søren Gjesse 2013/05/27 10:47:21 Done.
+ map[Uri.decodeQueryComponent(key)] = decodeQueryComponent(value);
+ }
+ return map;
+ });
+ }
+
+ static String _makeScheme(String scheme) {
+ bool isSchemeLowerCharacter(int ch) {
+ return ch < 128 &&
+ ((_schemeLowerTable[ch >> 4] & (1 << (ch & 0x0f))) != 0);
+ }
+
+ bool isSchemeCharacter(int ch) {
+ return ch < 128 && ((_schemeTable[ch >> 4] & (1 << (ch & 0x0f))) != 0);
+ }
+
+ if (scheme == null) return "";
+ bool allLowercase = true;
+ int length = scheme.length;
+ for (int i = 0; i < length; i++) {
+ int codeUnit = scheme.codeUnitAt(i);
+ if (!isSchemeLowerCharacter(codeUnit)) {
+ if (isSchemeCharacter(codeUnit)) {
+ allLowercase = false;
+ } else {
+ throw new ArgumentError('Illegal scheme');
floitsch 2013/05/24 20:37:27 Add the scheme: 'Illegal scheme: $scheme'.
Søren Gjesse 2013/05/27 10:47:21 Done.
+ }
+ }
+ }
+
+ return allLowercase ? scheme : scheme.toLowerCase();
+ }
+
+ static String _makePath(String path, List<String> pathSegments) {
+ if (path == null && pathSegments == null) return "";
+ if (path != null && pathSegments != null) {
+ throw new ArgumentError('Both path and pathSegments specified');
+ }
+ if (path != null) return _normalize(path);
+
+ return pathSegments.map((s) => _uriEncode(_pathCharTable, s)).join("/");
+ }
+
+ static String _makeQuery(String query, Map<String, String> queryParameters) {
+ if (query == null && queryParameters == null) return "";
+ if (query != null && queryParameters != null) {
+ throw new ArgumentError('Both query and queryParameters specified');
+ }
+ if (query != null) return _normalize(query);
+
+ var result = new StringBuffer();
+ var first = true;
+ queryParameters.forEach((key, value) {
+ if (!first) {
+ result.write("&");
+ }
+ first = false;
+ result.write(Uri.encodeQueryComponent(key));
+ if (value != null && !value.isEmpty) {
+ result.write("=");
+ result.write(Uri.encodeQueryComponent(value));
+ }
+ });
+ return result.toString();
+ }
+
+ static String _makeFragment(String fragment) {
+ if (fragment == null) return "";
+ return _normalize(fragment);
+ }
+
+ static String _normalize(String component) {
+ bool isNormalizedHexDigit(int digit) {
+ return (_CharCode.ZERO <= digit && digit <= _CharCode.NINE) ||
+ (_CharCode.UPPER_CASE_A <= digit && digit <= _CharCode.UPPER_CASE_F);
+ }
+
+ bool isLowerCaseHexDigit(int digit) {
+ return _CharCode.LOWER_CASE_A <= digit && digit <= _CharCode.LOWER_CASE_F;
+ }
+
+ bool isUnreserved(int ch) {
+ return ch < 128 &&
+ ((_unreservedTable[ch >> 4] & (1 << (ch & 0x0f))) != 0);
+ }
+
+ int normalizeHexDigit(int index) {
+ var codeUnit = component.codeUnitAt(index);
+ if (isLowerCaseHexDigit(codeUnit)) {
+ return codeUnit - 0x20;
+ } else if (!isNormalizedHexDigit(codeUnit)) {
+ throw new ArgumentError("Invalid URI component");
+ } else {
+ return codeUnit;
+ }
+ }
+
+ int decodeHexDigitPair(int index) {
+ int byte = 0;
+ for (int i = 0; i < 2; i++) {
+ var codeUnit = component.codeUnitAt(index + i);
+ if (_CharCode.ZERO <= codeUnit && codeUnit <= _CharCode.NINE) {
+ byte = byte * 16 + codeUnit - _CharCode.ZERO;
+ } else {
+ // Check ranges A-F (0x41-0x46) and a-f (0x61-0x66).
+ codeUnit |= 0x20;
+ if (_CharCode.LOWER_CASE_A <= codeUnit &&
+ codeUnit <= _CharCode.LOWER_CASE_F) {
+ byte = byte * 16 + codeUnit - 0x57;
floitsch 2013/05/24 20:37:27 what's 0x57?
Søren Gjesse 2013/05/27 10:47:21 LOWERCASE_A - 10 Change the code to which should
+ } else {
+ throw new ArgumentError("Invalid URI encoding");
floitsch 2013/05/24 20:37:27 add reason.
Søren Gjesse 2013/05/27 10:47:21 Done.
+ }
+ }
+ }
+ return byte;
+ }
+
+ // Start building the normalized component string.
+ StringBuffer result;
+ int length = component.length;
+ int index = 0;
+ int prevIndex = 0;
+ while (index < length) {
+
+ // Copy a part of the component string to the result.
+ fillResult() {
+ if (result == null) {
+ assert(prevIndex == 0);
+ result = new StringBuffer(component.substring(prevIndex, index));
+ } else {
+ result.write(component.substring(prevIndex, index));
+ }
+ }
+
+ // Normalize percent encoding to uppercase and don't encode
+ // unreserved characters.
+ if (component.codeUnitAt(index) == _CharCode.PERCENT) {
+ if (length < index + 2) {
+ throw new ArgumentError("Invalid Uri component");
floitsch 2013/05/24 20:37:27 add reason.
Søren Gjesse 2013/05/27 10:47:21 Done.
+ }
+
+ var codeUnit1 = component.codeUnitAt(index + 1);
+ var codeUnit2 = component.codeUnitAt(index + 2);
+ var decodedCodeUnit = decodeHexDigitPair(index + 1);
+ if (isNormalizedHexDigit(codeUnit1) &&
+ isNormalizedHexDigit(codeUnit2) &&
+ !isUnreserved(decodedCodeUnit)) {
+ index += 3;
+ } else {
+ fillResult();
+ if (isUnreserved(decodedCodeUnit)) {
+ result.writeCharCode(decodedCodeUnit);
+ } else {
+ result.write("%");
+ result.writeCharCode(normalizeHexDigit(index + 1));
+ result.writeCharCode(normalizeHexDigit(index + 2));
+ }
+ index += 3;
+ prevIndex = index;
+ }
+ } else {
+ index++;
+ }
+ }
+ assert(index == length);
+
+ if (result == null) return component;
+ return result.toString();
+ }
+
+ static String _emptyIfNull(String val) => val != null ? val : '';
+
+ static int _parseIntOrZero(String val) {
+ if (val != null && val != '') {
+ return int.parse(val);
+ } else {
+ return 0;
+ }
+ }
+
+ static String _eitherOf(String val1, String val2) {
+ if (val1 != null) return val1;
+ if (val2 != null) return val2;
+ return '';
+ }
+
+ // NOTE: This code was ported from: closure-library/closure/goog/uri/utils.js
+ static final RegExp _splitRe = new RegExp(
+ '^'
+ '(?:'
+ '([^:/?#.]+)' // scheme - ignore special characters
+ // used by other URL parts such as :,
+ // ?, /, #, and .
+ ':)?'
+ '(?://'
+ '(?:([^/?#]*)@)?' // userInfo
+ '(?:'
+ r'([\w\d\-\u0100-\uffff.%]*)'
+ // host - restrict to letters,
+ // digits, dashes, dots, percent
+ // escapes, and unicode characters.
+ '|'
+ // TODO(ajohnsen): Only allow a max number of parts?
+ r'\[([A-Fa-f0-9:.]*)\])'
+ // IPv6 host - restrict to hex,
+ // dot and colon.
+ '(?::([0-9]+))?' // port
+ ')?'
+ r'([^?#[]+)?' // path
+ r'(?:\?([^#]*))?' // query
+ '(?:#(.*))?' // fragment
+ r'$');
+
+ static const _COMPONENT_SCHEME = 1;
+ static const _COMPONENT_USER_INFO = 2;
+ static const _COMPONENT_HOST = 3;
+ static const _COMPONENT_HOST_IPV6 = 4;
+ static const _COMPONENT_PORT = 5;
+ static const _COMPONENT_PATH = 6;
+ static const _COMPONENT_QUERY_DATA = 7;
+ static const _COMPONENT_FRAGMENT = 8;
+
+ /**
+ * Returns `true` if the URI is absolute.
+ */
+ bool get isAbsolute {
+ if ("" == scheme) return false;
+ if ("" != fragment) return false;
+ return true;
+
+ /* absolute-URI = scheme ":" hier-part [ "?" query ]
floitsch 2013/05/24 20:37:27 why is this comment here?
Søren Gjesse 2013/05/27 10:47:21 It was there in the dart:uri library. Removed it.
+ * hier-part = "//" authority path-abempty
+ * / path-absolute
+ * / path-rootless
+ * / path-empty
+ *
+ * path = path-abempty ; begins with "/" or is empty
+ * / path-absolute ; begins with "/" but not "//"
+ * / path-noscheme ; begins with a non-colon segment
+ * / path-rootless ; begins with a segment
+ * / path-empty ; zero characters
+ *
+ * path-abempty = *( "/" segment )
+ * path-absolute = "/" [ segment-nz *( "/" segment ) ]
+ * path-noscheme = segment-nz-nc *( "/" segment )
+ * path-rootless = segment-nz *( "/" segment )
+ * path-empty = 0<pchar>
+ * segment = *pchar
+ * segment-nz = 1*pchar
+ * segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" )
+ * ; non-zero-length segment without any colon ":"
+ *
+ * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
+ */
+ }
+
+ Uri resolve(String uri) {
+ return resolveUri(Uri.parse(uri));
+ }
+
+ Uri resolveUri(Uri reference) {
+ // From RFC 3986.
+ String targetScheme;
+ String targetUserInfo;
+ String targetHost;
+ int targetPort;
+ String targetPath;
+ String targetQuery;
+ if (reference.scheme != "") {
+ targetScheme = reference.scheme;
+ targetUserInfo = reference.userInfo;
+ targetHost = reference.host;
+ targetPort = reference.port;
+ targetPath = removeDotSegments(reference.path);
+ targetQuery = reference.query;
+ } else {
+ if (reference.hasAuthority) {
+ targetUserInfo = reference.userInfo;
+ targetHost = reference.host;
+ targetPort = reference.port;
+ targetPath = removeDotSegments(reference.path);
+ targetQuery = reference.query;
+ } else {
+ if (reference.path == "") {
+ targetPath = this.path;
+ if (reference.query != "") {
+ targetQuery = reference.query;
+ } else {
+ targetQuery = this.query;
+ }
+ } else {
+ if (reference.path.startsWith("/")) {
+ targetPath = removeDotSegments(reference.path);
+ } else {
+ targetPath = removeDotSegments(merge(this.path, reference.path));
+ }
+ targetQuery = reference.query;
+ }
+ targetUserInfo = this.userInfo;
+ targetHost = this.host;
+ targetPort = this.port;
+ }
+ targetScheme = this.scheme;
+ }
+ return new Uri(scheme: targetScheme,
+ userInfo: targetUserInfo,
floitsch 2013/05/24 20:37:27 indentation.
Søren Gjesse 2013/05/27 10:47:21 Done.
+ host: targetHost,
+ port: targetPort,
+ path: targetPath,
+ query: targetQuery,
+ fragment: reference.fragment);
+ }
+
+ bool get hasAuthority {
+ return (userInfo != "") || (host != "") || (port != 0);
floitsch 2013/05/24 20:37:27 why is the authority linked to a non-zery port?
Søren Gjesse 2013/05/27 10:47:21 Good catch! An authority always have a host. Chan
+ }
+
+ /**
+ * For http/https schemes returns URI's [origin][] - scheme://host:port.
floitsch 2013/05/24 20:37:27 Returns the URI's [origin][] for http/https scheme
Søren Gjesse 2013/05/27 10:47:21 Done.
+ * For all other schemes throws StateError.
+ * [origin]: http://www.w3.org/TR/2011/WD-html5-20110405/origin-0.html#origin
+ */
+ String get origin {
+ if (scheme == "") {
+ throw new StateError("Cannot use origin without a scheme");
floitsch 2013/05/24 20:37:27 give information: "Cannot use origin without a sch
Søren Gjesse 2013/05/27 10:47:21 Done.
+ }
+ if (scheme != "http" && scheme != "https") {
+ throw new StateError(
+ "origin is applicable to http/https schemes only. Not \'$scheme\'");
+ }
+ StringBuffer sb = new StringBuffer();
+ sb.write(scheme);
+ sb.write(":");
+ if (host == null || host == "") {
floitsch 2013/05/24 20:37:27 Move check up to the checks.
Søren Gjesse 2013/05/27 10:47:21 Done.
+ throw new StateError("Cannot use origin without a host");
+ }
+
+ sb.write("//");
+ sb.write(host);
+ if (port != 0) {
+ sb.write(":");
+ sb.write(port);
+ }
+ return sb.toString();
floitsch 2013/05/24 20:37:27 No need for StringBuffers. if (port == 0) return "
Søren Gjesse 2013/05/27 10:47:21 Good point. Changed.
+ }
+
+ String toString() {
+ StringBuffer sb = new StringBuffer();
+ _addIfNonEmpty(sb, scheme, scheme, ':');
+ if (hasAuthority || (scheme == "file")) {
+ sb.write("//");
+ _addIfNonEmpty(sb, userInfo, userInfo, "@");
+ sb.write(host == null ? "null" :
+ host.contains(':') ? '[$host]' : host);
+ if (port != 0) {
+ sb.write(":");
+ sb.write(port.toString());
+ }
+ }
+ sb.write(path == null ? "null" : path);
floitsch 2013/05/24 20:37:27 isn't the path null-check redundant?
Søren Gjesse 2013/05/27 10:47:21 It is - removed.
+ _addIfNonEmpty(sb, query, "?", query);
+ _addIfNonEmpty(sb, fragment, "#", fragment);
+ return sb.toString();
+ }
+
+ bool operator==(other) {
+ if (other is! Uri) return false;
+ Uri uri = other;
+ return scheme == uri.scheme &&
+ userInfo == uri.userInfo &&
+ host == uri.host &&
+ port == uri.port &&
+ path == uri.path &&
+ query == uri.query &&
+ fragment == uri.fragment;
+ }
+
+ int get hashCode {
+ int combine(part, current) {
+ // The sum is truncated to 30 bits to make sure it fits into a Smi.
+ return (current * 31 + part.hashCode) & 0x3FFFFFFF;
+ }
+ return combine(scheme, combine(userInfo, combine(host, combine(port,
+ combine(path, combine(query, combine(fragment, 1)))))));
+ }
+
+ static void _addIfNonEmpty(StringBuffer sb, String test,
+ String first, String second) {
+ if ("" != test) {
+ sb.write(first == null ? "null" : first);
+ sb.write(second == null ? "null" : second);
+ }
+ }
+
+ /**
+ * A javaScript-like URI component encoder, this encodes a URI
+ * [component] by replacing each instance of certain characters by
floitsch 2013/05/24 20:37:27 If the set of characters is fixed, add it at the e
Søren Gjesse 2013/05/27 10:47:21 Done.
+ * one, two, three, or four escape sequences representing the UTF-8
+ * encoding of the character (will only be four escape sequences for
+ * characters composed of two "surrogate" characters). To avoid
+ * unexpected requests to the server, you should call
+ * Uri.encodeComponent on any user-entered parameters that will be
+ * passed as part of a URI.
+ *
+ * For encoding the query part consider using
+ * [encodeQueryComponent].
+ *
+ * When manually encoding path segments or query components remember
+ * to encode each part separately before building the path or query
+ * string.
+ *
+ * To avoid the need for explicitly encoding use the [pathSegments]
+ * and [queryParameters] optional named arguments when constructing
+ * a Uri.
+ */
+ static String encodeComponent(String component) {
+ return _uriEncode(_unreserved2396Table, component);
+ }
+
+ /*
+ * Encode [component] according to the HTML 4.01 rules for encoding
+ * the posting of a HTML form as a query string component. Spaces
+ * will be replaced with plus and all characters except for
+ * uppercase and lowercase letters, decimal digits, hyphen, period,
+ * underscore and tilde will be encoded. Note that the set of
+ * characters encoded is a superset of what HTML 4.01 says as it
+ * refers to RFC 1738 for reserved characters.
+ *
+ * When manually encoding query components remember to encode each
+ * part separately before building the query string.
+ *
+ * See http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2 for more
floitsch 2013/05/24 20:37:27 I think there is a way to make this a link.
Søren Gjesse 2013/05/27 10:47:21 I will postpone the adding of proper links to a se
+ * details.
+ */
+ static String encodeQueryComponent(String component) {
+ return _uriEncode(_unreservedTable, component, spaceToPlus: true);
+ }
+
+ /**
+ * A javaScript-like URI component decoder.
floitsch 2013/05/24 20:37:27 Is it JavaScript like, or more general? In other w
Søren Gjesse 2013/05/27 10:47:21 Rephrased the comment and explained more precisely
+ */
+ static String decodeComponent(String encodedComponent) {
+ return _uriDecode(encodedComponent);
+ }
+
+ static String decodeQueryComponent(String encodedComponent) {
+ return _uriDecode(encodedComponent, plusToSpace: true);
+ }
+
+ /**
+ * A JavaScript-like URI encoder. Encodes a full URI string by
+ * replacing each instance of certain characters by one, two, three,
+ * or four escape sequences representing the UTF-8 encoding of the
+ * character (will only be four escape sequences for characters
+ * composed of two "surrogate" characters). This assumes that [uri]
+ * is a complete URI, so does not encode reserved characters that
+ * have special meaning in the URI: [:#;,/?:@&=+\$:] It returns the
floitsch 2013/05/24 20:37:27 nit (personal preference): `#;,/?:@&=+\$`.
Søren Gjesse 2013/05/27 10:47:21 Done.
+ * escaped URI.
+ */
+ static String encodeFull(String uri) {
+ return _uriEncode(_encodeFullTable, uri);
+ }
+
+ /**
+ * A JavaScript-like URI decoder. Decodes a full URI string. This is
floitsch 2013/05/24 20:37:27 Start with what it does: "Decodes ...". Make one s
Søren Gjesse 2013/05/27 10:47:21 Rephrased the comment and explained more precisely
+ * an implementation of JavaScript's decodeURIComponent function.
+ * Decodes a Uniform Resource Identifier [uri] previously created by
+ * encodeURI or by a similar routine. It replaces each escape sequence
+ * in [uri] with the character that it represents.
+ */
+ static String decodeFull(String uri) {
+ return _uriDecode(uri);
+ }
+
+ // Tables of char-codes organized as a bit vector of 128 bits where
+ // each bit indicate whether a character code on the 0-127 needs to
+ // be escaped or not.
+
+ // The unreserved characters of RFC 3986.
+ static const _unreservedTable = const [
+ // LSB MSB
+ // | |
+ 0x0000, // 0x00 - 0x0f 0000000000000000
+ 0x0000, // 0x10 - 0x1f 0000000000000000
+ // -.
+ 0x6000, // 0x20 - 0x2f 0000000000000110
+ // 0123456789
+ 0x03ff, // 0x30 - 0x3f 1111111111000000
+ // ABCDEFGHIJKLMNO
+ 0xfffe, // 0x40 - 0x4f 0111111111111111
+ // PQRSTUVWXYZ _
+ 0x87ff, // 0x50 - 0x5f 1111111111100001
+ // abcdefghijklmno
+ 0xfffe, // 0x60 - 0x6f 0111111111111111
+ // pqrstuvwxyz ~
+ 0x47ff]; // 0x70 - 0x7f 1111111111100010
+
+ // The unreserved characters of RFC 2396.
+ static const _unreserved2396Table = const [
+ // LSB MSB
+ // | |
+ 0x0000, // 0x00 - 0x0f 0000000000000000
+ 0x0000, // 0x10 - 0x1f 0000000000000000
+ // ! '()* -.
+ 0x6782, // 0x20 - 0x2f 0100000111100110
+ // 0123456789
+ 0x03ff, // 0x30 - 0x3f 1111111111000000
+ // ABCDEFGHIJKLMNO
+ 0xfffe, // 0x40 - 0x4f 0111111111111111
+ // PQRSTUVWXYZ _
+ 0x87ff, // 0x50 - 0x5f 1111111111100001
+ // abcdefghijklmno
+ 0xfffe, // 0x60 - 0x6f 0111111111111111
+ // pqrstuvwxyz ~
+ 0x47ff]; // 0x70 - 0x7f 1111111111100010
+
+ // Table of reserved characters specified by ECMAScript 5.
+ static const _encodeFullTable = const [
+ // LSB MSB
+ // | |
+ 0x0000, // 0x00 - 0x0f 0000000000000000
+ 0x0000, // 0x10 - 0x1f 0000000000000000
+ // ! #$ &'()*+,-./
+ 0xf7da, // 0x20 - 0x2f 0101101111101111
+ // 0123456789:; = ?
+ 0xafff, // 0x30 - 0x3f 1111111111110101
+ // @ABCDEFGHIJKLMNO
+ 0xffff, // 0x40 - 0x4f 1111111111111111
+ // PQRSTUVWXYZ _
+ 0x87ff, // 0x50 - 0x5f 1111111111100001
+ // abcdefghijklmno
+ 0xfffe, // 0x60 - 0x6f 0111111111111111
+ // pqrstuvwxyz ~
+ 0x47ff]; // 0x70 - 0x7f 1111111111100010
+
+ // Characters allowed in the scheme.
+ static const _schemeTable = const [
+ // LSB MSB
+ // | |
+ 0x0000, // 0x00 - 0x0f 0000000000000000
+ 0x0000, // 0x10 - 0x1f 0000000000000000
+ // + -.
+ 0x6800, // 0x20 - 0x2f 0000000000010110
+ // 0123456789
+ 0x03ff, // 0x30 - 0x3f 1111111111000000
+ // ABCDEFGHIJKLMNO
+ 0xfffe, // 0x40 - 0x4f 0111111111111111
+ // PQRSTUVWXYZ
+ 0x07ff, // 0x50 - 0x5f 1111111111100001
+ // abcdefghijklmno
+ 0xfffe, // 0x60 - 0x6f 0111111111111111
+ // pqrstuvwxyz
+ 0x07ff]; // 0x70 - 0x7f 1111111111100010
+
+ // Characters allowed in scheme except for upper case letters.
+ static const _schemeLowerTable = const [
+ // LSB MSB
+ // | |
+ 0x0000, // 0x00 - 0x0f 0000000000000000
+ 0x0000, // 0x10 - 0x1f 0000000000000000
+ // + -.
+ 0x6800, // 0x20 - 0x2f 0000000000010110
+ // 0123456789
+ 0x03ff, // 0x30 - 0x3f 1111111111000000
+ //
+ 0x0000, // 0x40 - 0x4f 0111111111111111
+ //
+ 0x0000, // 0x50 - 0x5f 1111111111100001
+ // abcdefghijklmno
+ 0xfffe, // 0x60 - 0x6f 0111111111111111
+ // pqrstuvwxyz
+ 0x07ff]; // 0x70 - 0x7f 1111111111100010
+
+ // Sub delimiter characters combined with unreserved as of 3986.
+ // sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
+ // / "*" / "+" / "," / ";" / "="
+ // RFC 3986 section 2.3.
+ // unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
+ static const _subDelimitersTable = const [
+ // LSB MSB
+ // | |
+ 0x0000, // 0x00 - 0x0f 0000000000000000
+ 0x0000, // 0x10 - 0x1f 0000000000000000
+ // ! $ &'()*+,-.
+ 0x7fd2, // 0x20 - 0x2f 0100101111111110
+ // 0123456789 ; =
+ 0x2bff, // 0x30 - 0x3f 1111111111010100
+ // ABCDEFGHIJKLMNO
+ 0xfffe, // 0x40 - 0x4f 0111111111111111
+ // PQRSTUVWXYZ _
+ 0x87ff, // 0x50 - 0x5f 1111111111100001
+ // abcdefghijklmno
+ 0xfffe, // 0x60 - 0x6f 0111111111111111
+ // pqrstuvwxyz ~
+ 0x47ff]; // 0x70 - 0x7f 1111111111100010
+
+ // Characters allowed in the path as of RFC 3986.
+ // RFC 3986 section 3.3.
+ // pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
+ static const _pathCharTable = const [
+ // LSB MSB
+ // | |
+ 0x0000, // 0x00 - 0x0f 0000000000000000
+ 0x0000, // 0x10 - 0x1f 0000000000000000
+ // ! $ &'()*+,-.
+ 0x7fd2, // 0x20 - 0x2f 0100101111111110
+ // 0123456789:; =
+ 0x2fff, // 0x30 - 0x3f 1111111111110100
+ // @ABCDEFGHIJKLMNO
+ 0xffff, // 0x40 - 0x4f 1111111111111111
+ // PQRSTUVWXYZ _
+ 0x87ff, // 0x50 - 0x5f 1111111111100001
+ // abcdefghijklmno
+ 0xfffe, // 0x60 - 0x6f 0111111111111111
+ // pqrstuvwxyz ~
+ 0x47ff]; // 0x70 - 0x7f 1111111111100010
+
+ // Characters allowed in the query as of RFC 3986.
+ // RFC 3986 section 3.4.
+ // query = *( pchar / "/" / "?" )
+ static const _queryCharTable = const [
+ // LSB MSB
+ // | |
+ 0x0000, // 0x00 - 0x0f 0000000000000000
+ 0x0000, // 0x10 - 0x1f 0000000000000000
+ // ! $ &'()*+,-./
+ 0xffd2, // 0x20 - 0x2f 0100101111111111
+ // 0123456789:; = ?
+ 0xafff, // 0x30 - 0x3f 1111111111110101
+ // @ABCDEFGHIJKLMNO
+ 0xffff, // 0x40 - 0x4f 1111111111111111
+ // PQRSTUVWXYZ _
+ 0x87ff, // 0x50 - 0x5f 1111111111100001
+ // abcdefghijklmno
+ 0xfffe, // 0x60 - 0x6f 0111111111111111
+ // pqrstuvwxyz ~
+ 0x47ff]; // 0x70 - 0x7f 1111111111100010
+}

Powered by Google App Engine
This is Rietveld 408576698