Index: dart/utils/uri/uri.dart |
diff --git a/dart/utils/uri/uri.dart b/dart/utils/uri/uri.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..9116a9d59aec3b7b920cc94e1f6e084f580960cd |
--- /dev/null |
+++ b/dart/utils/uri/uri.dart |
@@ -0,0 +1,304 @@ |
+// 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. |
+ |
+#library('uri'); |
+ |
+/** |
+ * A parsed URI, inspired by: |
+ * http://closure-library.googlecode.com/svn/docs/class_goog_Uri.html |
+ */ |
+class Uri { |
+ final String scheme; |
+ final String userInfo; |
+ final String domain; |
+ final int port; |
+ final String path; |
+ final String query; |
+ final String fragment; |
+ |
+ /** |
+ * Determines whether a URI is absolute. |
+ * |
+ * See: http://tools.ietf.org/html/rfc3986#section-4.3 |
+ */ |
+ bool isAbsolute() { |
+ if ("" == scheme) return false; |
+ if ("" != fragment) return false; |
+ return true; |
+ |
+ /* absolute-URI = scheme ":" hier-part [ "?" query ] |
+ * 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(new Uri.fromString(uri)); |
+ } |
+ |
+ Uri resolveUri(Uri reference) { |
+ // From RFC 3986. |
+ String targetScheme; |
+ String targetUserInfo; |
+ String targetDomain; |
+ int targetPort; |
+ String targetPath; |
+ String targetQuery; |
+ if (reference.scheme != "") { |
+ targetScheme = reference.scheme; |
+ targetUserInfo = reference.userInfo; |
+ targetDomain = reference.domain; |
+ targetPort = reference.port; |
+ targetPath = removeDotSegments(reference.path); |
+ targetQuery = reference.query; |
+ } else { |
+ if (reference.hasAuthority()) { |
+ targetUserInfo = reference.userInfo; |
+ targetDomain = reference.domain; |
+ 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; |
+ targetDomain = this.domain; |
+ targetPort = this.port; |
+ } |
+ targetScheme = this.scheme; |
+ } |
+ return new Uri(targetScheme, targetUserInfo, targetDomain, targetPort, |
+ targetPath, targetQuery, reference.fragment); |
+ } |
+ |
+ bool hasAuthority() { |
+ return (userInfo != "") || (domain != "") || (port != 0); |
+ } |
+ |
+ String toString() { |
+ StringBuffer sb = new StringBuffer(); |
+ _addIfNonEmpty(sb, scheme, scheme, ':'); |
+ if (hasAuthority() || (scheme == "file")) { |
+ sb.add("//"); |
+ _addIfNonEmpty(sb, userInfo, userInfo, "@"); |
+ sb.add(domain); |
+ if (port != 0) { |
+ sb.add(":"); |
+ sb.add(port.toString()); |
+ } |
+ } |
+ sb.add(path); |
+ _addIfNonEmpty(sb, query, "?", query); |
+ _addIfNonEmpty(sb, fragment, "#", fragment); |
+ return sb.toString(); |
+ } |
+ |
+ static void _addIfNonEmpty(StringBuffer sb, String test, |
+ String first, String second) { |
+ if ("" != test) { |
+ sb.add(first); |
+ sb.add(second); |
+ } |
+ } |
+ |
+ Uri.fromString(String uri) : this._fromMatch(_splitRe.firstMatch(uri)); |
+ |
+ Uri._fromMatch(Match m) : this(_emptyIfNull(m[_COMPONENT_SCHEME]), |
+ _emptyIfNull(m[_COMPONENT_USER_INFO]), |
+ _emptyIfNull(m[_COMPONENT_DOMAIN]), |
+ _parseIntOrZero(m[_COMPONENT_PORT]), |
+ _emptyIfNull(m[_COMPONENT_PATH]), |
+ _emptyIfNull(m[_COMPONENT_QUERY_DATA]), |
+ _emptyIfNull(m[_COMPONENT_FRAGMENT])); |
+ |
+ const Uri([String this.scheme = "", String this.userInfo ="", |
+ String this.domain = "", int this.port = 0, |
+ String this.path = "", String this.query = "", |
+ String this.fragment = ""]); |
+ |
+ static String _emptyIfNull(String val) => val != null ? val : ''; |
+ |
+ static int _parseIntOrZero(String val) { |
+ if (val !== null && val != '') { |
+ return Math.parseInt(val); |
+ } else { |
+ return 0; |
+ } |
+ } |
+ |
+ // NOTE: This code was ported from: closure-library/closure/goog/uri/utils.js |
+ static RegExp _splitReLazy; |
+ |
+ static RegExp get _splitRe() { |
+ if (_splitReLazy == null) { |
+ _splitReLazy = new RegExp( |
+ '^' + |
+ '(?:' + |
+ '([^:/?#.]+)' + // scheme - ignore special characters |
+ // used by other URL parts such as :, |
+ // ?, /, #, and . |
+ ':)?' + |
+ '(?://' + |
+ '(?:([^/?#]*)@)?' + // userInfo |
+ '([\\w\\d\\-\\u0100-\\uffff.%]*)' + |
+ // domain - restrict to letters, |
+ // digits, dashes, dots, percent |
+ // escapes, and unicode characters. |
+ '(?::([0-9]+))?' + // port |
+ ')?' + |
+ '([^?#]+)?' + // path |
+ '(?:\\?([^#]*))?' + // query |
+ '(?:#(.*))?' + // fragment |
+ '\$'); |
+ } |
+ return _splitReLazy; |
+ } |
+ |
+ static final _COMPONENT_SCHEME = 1; |
+ static final _COMPONENT_USER_INFO = 2; |
+ static final _COMPONENT_DOMAIN = 3; |
+ static final _COMPONENT_PORT = 4; |
+ static final _COMPONENT_PATH = 5; |
+ static final _COMPONENT_QUERY_DATA = 6; |
+ static final _COMPONENT_FRAGMENT = 7; |
+} |
+ |
+testUri(String uri, bool isAbsolute) { |
+ Expect.equals(isAbsolute, new Uri.fromString(uri).isAbsolute()); |
+ Expect.stringEquals(uri, new Uri.fromString(uri).toString()); |
+} |
+ |
+main() { |
ahe
2012/01/29 14:38:00
I'll move this method and testUri to a test case b
|
+ testUri("http:", true); |
+ testUri("file://", true); |
+ testUri("file", false); |
+ testUri("http://user@example.com:80/fisk?query=89&hest=silas", true); |
+ testUri("http://user@example.com:80/fisk?query=89&hest=silas#fragment", |
+ false); |
+ Expect.stringEquals("http://user@example.com:80/a/b/c?query#fragment", |
+ const Uri("http", "user", "example.com", 80, "/a/b/c", |
+ "query", "fragment").toString()); |
+ Expect.stringEquals("null://null@null/a/b/c/?null#null", |
+ const Uri(null, null, null, 0, "/a/b/c/", |
+ null, null).toString()); |
+ Expect.stringEquals("file://", new Uri.fromString("file:").toString()); |
+ Expect.stringEquals("/a/g", removeDotSegments("/a/b/c/./../../g")); |
+ Expect.stringEquals("mid/6", removeDotSegments("mid/content=5/../6")); |
+ Expect.stringEquals("a/b/e", removeDotSegments("a/b/c/d/../../e")); |
+ Expect.stringEquals("a/b/e", removeDotSegments("../a/b/c/d/../../e")); |
+ Expect.stringEquals("a/b/e", removeDotSegments("./a/b/c/d/../../e")); |
+ Expect.stringEquals("a/b/e", removeDotSegments("../a/b/./c/d/../../e")); |
+ Expect.stringEquals("a/b/e", removeDotSegments("./a/b/./c/d/../../e")); |
+ Expect.stringEquals("a/b/e/", removeDotSegments("./a/b/./c/d/../../e/.")); |
+ Expect.stringEquals("a/b/e/", removeDotSegments("./a/b/./c/d/../../e/./.")); |
+ Expect.stringEquals("a/b/e/", removeDotSegments("./a/b/./c/d/../../e/././.")); |
+ |
+ // From RFC 3986. |
+ Uri base = new Uri.fromString("http://a/b/c/d;p?q"); |
+ Expect.stringEquals("g:h", base.resolve("g:h").toString()); |
+ Expect.stringEquals("http://a/b/c/g", base.resolve("g").toString()); |
+ Expect.stringEquals("http://a/b/c/g", base.resolve("./g").toString()); |
+ Expect.stringEquals("http://a/b/c/g/", base.resolve("g/").toString()); |
+ Expect.stringEquals("http://a/g", base.resolve("/g").toString()); |
+ Expect.stringEquals("http://g", base.resolve("//g").toString()); |
+ Expect.stringEquals("http://a/b/c/d;p?y", base.resolve("?y").toString()); |
+ Expect.stringEquals("http://a/b/c/g?y", base.resolve("g?y").toString()); |
+ Expect.stringEquals("http://a/b/c/d;p?q#s", base.resolve("#s").toString()); |
+ Expect.stringEquals("http://a/b/c/g#s", base.resolve("g#s").toString()); |
+ Expect.stringEquals("http://a/b/c/g?y#s", base.resolve("g?y#s").toString()); |
+ Expect.stringEquals("http://a/b/c/;x", base.resolve(";x").toString()); |
+ Expect.stringEquals("http://a/b/c/g;x", base.resolve("g;x").toString()); |
+ Expect.stringEquals("http://a/b/c/g;x?y#s", |
+ base.resolve("g;x?y#s").toString()); |
+ Expect.stringEquals("http://a/b/c/d;p?q", base.resolve("").toString()); |
+ Expect.stringEquals("http://a/b/c/", base.resolve(".").toString()); |
+ Expect.stringEquals("http://a/b/c/", base.resolve("./").toString()); |
+ Expect.stringEquals("http://a/b/", base.resolve("..").toString()); |
+ Expect.stringEquals("http://a/b/", base.resolve("../").toString()); |
+ Expect.stringEquals("http://a/b/g", base.resolve("../g").toString()); |
+ Expect.stringEquals("http://a/", base.resolve("../..").toString()); |
+ Expect.stringEquals("http://a/", base.resolve("../../").toString()); |
+ Expect.stringEquals("http://a/g", base.resolve("../../g").toString()); |
+ Expect.stringEquals("http://a/g", base.resolve("../../../g").toString()); |
+ Expect.stringEquals("http://a/g", base.resolve("../../../../g").toString()); |
+ Expect.stringEquals("http://a/g", base.resolve("/./g").toString()); |
+ Expect.stringEquals("http://a/g", base.resolve("/../g").toString()); |
+ Expect.stringEquals("http://a/b/c/g.", base.resolve("g.").toString()); |
+ Expect.stringEquals("http://a/b/c/.g", base.resolve(".g").toString()); |
+ Expect.stringEquals("http://a/b/c/g..", base.resolve("g..").toString()); |
+ Expect.stringEquals("http://a/b/c/..g", base.resolve("..g").toString()); |
+ Expect.stringEquals("http://a/b/g", base.resolve("./../g").toString()); |
+ Expect.stringEquals("http://a/b/c/g/", base.resolve("./g/.").toString()); |
+ Expect.stringEquals("http://a/b/c/g/h", base.resolve("g/./h").toString()); |
+ Expect.stringEquals("http://a/b/c/h", base.resolve("g/../h").toString()); |
+ Expect.stringEquals("http://a/b/c/g;x=1/y", |
+ base.resolve("g;x=1/./y").toString()); |
+ Expect.stringEquals("http://a/b/c/y", base.resolve("g;x=1/../y").toString()); |
+ Expect.stringEquals("http://a/b/c/g?y/./x", |
+ base.resolve("g?y/./x").toString()); |
+ Expect.stringEquals("http://a/b/c/g?y/../x", |
+ base.resolve("g?y/../x").toString()); |
+ Expect.stringEquals("http://a/b/c/g#s/./x", |
+ base.resolve("g#s/./x").toString()); |
+ Expect.stringEquals("http://a/b/c/g#s/../x", |
+ base.resolve("g#s/../x").toString()); |
Bill Hesse
2012/01/31 10:37:57
Should you also test parameters in the path elemen
ahe
2012/01/31 11:43:00
That's being tested on lines:
243-246
269-270
Bu
|
+ Expect.stringEquals("http:g", base.resolve("http:g").toString()); |
+} |
+ |
+String merge(String base, String reference) { |
+ if (base == "") return "/$reference"; |
+ return base.substring(0, base.lastIndexOf("/") + 1) + "$reference"; |
+} |
+ |
+String removeDotSegments(String path) { |
+ List<String> output = []; |
+ bool appendSlash = false; |
+ for (String segment in path.split("/")) { |
+ appendSlash = false; |
+ if (segment == "..") { |
+ if (!output.isEmpty() && |
+ ((output.length != 1) || (output[0] != ""))) output.removeLast(); |
+ appendSlash = true; |
+ } else if ("." == segment) { |
+ appendSlash = true; |
+ } else { |
+ output.add(segment); |
+ } |
+ } |
+ if (appendSlash) output.add(""); |
+ return Strings.join(output, "/"); |
+} |