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

Unified Diff: dart/utils/uri/uri.dart

Issue 9295043: Create a URI class in utils/ (based on client/util/). (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge
Patch Set: Created 8 years, 11 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
« no previous file with comments | « dart/client/util/utilslib.dart ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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, "/");
+}
« no previous file with comments | « dart/client/util/utilslib.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698