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

Side by Side 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: Address review comments and *add* the new test suite Created 8 years, 10 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 unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « dart/tools/testing/dart/test_runner.dart ('k') | deps/compiler.deps/DEPS » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 #library('uri');
6
5 /** 7 /**
6 * A parsed URI, inspired by: 8 * A parsed URI, inspired by:
7 * http://closure-library.googlecode.com/svn/docs/class_goog_Uri.html 9 * http://closure-library.googlecode.com/svn/docs/class_goog_Uri.html
8 */ 10 */
9 class Uri { 11 class Uri {
10 /** 12 final String scheme;
11 * Parses a URL query string into a map. Because you can have multiple values 13 final String userInfo;
12 * for the same parameter name, each parameter name maps to a list of 14 final String domain;
13 * values. For example, '?a=b&c=d&a=e' would be parsed as 15 final int port;
14 * [{'a':['b','e'],'c':['d']}]. 16 final String path;
15 */ 17 final String query;
16 // TODO(jmesserly): consolidate with new Uri.fromString(...) 18 final String fragment;
17 static Map<String, List<String>> parseQuery(String queryString) {
18 final queryParams = new Map<String, List<String>>();
19 if (queryString.startsWith('?')) {
20 final params = queryString.substring(1, queryString.length).split('&');
21 for (final param in params) {
22 List<String> parts = param.split('=');
23 if (parts.length == 2) {
24 // TODO(hiltonc) the name and value should be URL decoded.
25 String name = parts[0];
26 String value = parts[1];
27 19
28 // Create a list of values for this name if not yet done. 20 Uri.fromString(String uri) : this._fromMatch(_splitRe.firstMatch(uri));
29 List values = queryParams[name];
30 if (values === null) {
31 values = new List();
32 queryParams[name] = values;
33 }
34 21
35 values.add(value); 22 Uri._fromMatch(Match m) : this(_emptyIfNull(m[_COMPONENT_SCHEME]),
36 } 23 _emptyIfNull(m[_COMPONENT_USER_INFO]),
37 } 24 _emptyIfNull(m[_COMPONENT_DOMAIN]),
38 } 25 _parseIntOrZero(m[_COMPONENT_PORT]),
39 return queryParams; 26 _emptyIfNull(m[_COMPONENT_PATH]),
40 } 27 _emptyIfNull(m[_COMPONENT_QUERY_DATA]),
28 _emptyIfNull(m[_COMPONENT_FRAGMENT]));
41 29
42 /** 30 const Uri([String this.scheme = "", String this.userInfo ="",
43 * Percent-encodes a string for use as a query parameter in a URI. 31 String this.domain = "", int this.port = 0,
44 */ 32 String this.path = "", String this.query = "",
45 // TODO(rnystrom): Get rid of this when the real encodeURIComponent() 33 String this.fragment = ""]);
46 // function is available within Dart.
47 static String encodeComponent(String component) {
48 if (component == null) return component;
49 34
50 // TODO(terry): Added b/5096547 to track replace should by default behave 35 static String _emptyIfNull(String val) => val != null ? val : '';
51 // like replaceAll to avoid a problematic usage pattern.
52 return component.replaceAll(':', '%3A')
53 .replaceAll('/', '%2F')
54 .replaceAll('?', '%3F')
55 .replaceAll('=', '%3D')
56 .replaceAll('&', '%26')
57 .replaceAll(' ', '%20');
58 }
59
60 /**
61 * Decodes a string used a query parameter by replacing percent-encoded
62 * sequences with their original characters.
63 */
64 // TODO(jmesserly): replace this with a better implementation
65 static String decodeComponent(String component) {
66 if (component == null) return component;
67
68 return component.replaceAll('%3A', ':')
69 .replaceAll('%2F', '/')
70 .replaceAll('%3F', '?')
71 .replaceAll('%3D', '=')
72 .replaceAll('%26', '&')
73 .replaceAll('%20', ' ');
74 }
75
76 String scheme;
77 String userInfo;
78 String domain;
79 int port;
80 String path;
81 String query;
82 String fragment;
83
84 Uri.fromString(String uri) {
85 final m = _splitRe.firstMatch(uri);
86
87 scheme = _decodeOrEmpty(m[_COMPONENT_SCHEME]);
88 userInfo = _decodeOrEmpty(m[_COMPONENT_USER_INFO]);
89 domain = _decodeOrEmpty(m[_COMPONENT_DOMAIN]);
90 port = _parseIntOrZero(m[_COMPONENT_PORT]);
91 path = _decodeOrEmpty(m[_COMPONENT_PATH]);
92 query = _decodeOrEmpty(m[_COMPONENT_QUERY_DATA]);
93 fragment = _decodeOrEmpty(m[_COMPONENT_FRAGMENT]);
94 }
95
96 static String _decodeOrEmpty(String val) {
97 // TODO(jmesserly): use Uri.decodeComponent when available
98 //return val ? Uri.decodeComponent(val) : '';
99 return val != null ? val : '';
100 }
101 36
102 static int _parseIntOrZero(String val) { 37 static int _parseIntOrZero(String val) {
103 if (val !== null && val != '') { 38 if (val !== null && val != '') {
104 return Math.parseInt(val); 39 return Math.parseInt(val);
105 } else { 40 } else {
106 return 0; 41 return 0;
107 } 42 }
108 } 43 }
109 44
110 // NOTE: This code was ported from: closure-library/closure/goog/uri/utils.js 45 // NOTE: This code was ported from: closure-library/closure/goog/uri/utils.js
111 static RegExp _splitReLazy; 46 static final RegExp _splitRe = const RegExp(
112 47 '^' +
113 static RegExp get _splitRe() { 48 '(?:' +
114 if (_splitReLazy == null) { 49 '([^:/?#.]+)' + // scheme - ignore special characters
115 _splitReLazy = new RegExp( 50 // used by other URL parts such as :,
116 '^' + 51 // ?, /, #, and .
117 '(?:' + 52 ':)?' +
118 '([^:/?#.]+)' + // scheme - ignore special characters 53 '(?://' +
119 // used by other URL parts such as :, 54 '(?:([^/?#]*)@)?' + // userInfo
120 // ?, /, #, and . 55 '([\\w\\d\\-\\u0100-\\uffff.%]*)' +
121 ':)?' + 56 // domain - restrict to letters,
122 '(?://' + 57 // digits, dashes, dots, percent
123 '(?:([^/?#]*)@)?' + // userInfo 58 // escapes, and unicode characters.
124 '([\\w\\d\\-\\u0100-\\uffff.%]*)' + 59 '(?::([0-9]+))?' + // port
125 // domain - restrict to letters, 60 ')?' +
126 // digits, dashes, dots, percent 61 '([^?#]+)?' + // path
127 // escapes, and unicode characters. 62 '(?:\\?([^#]*))?' + // query
128 '(?::([0-9]+))?' + // port 63 '(?:#(.*))?' + // fragment
129 ')?' + 64 '\$');
130 '([^?#]+)?' + // path
131 '(?:\\?([^#]*))?' + // query
132 '(?:#(.*))?' + // fragment
133 '\$');
134 }
135 return _splitReLazy;
136 }
137 65
138 static final _COMPONENT_SCHEME = 1; 66 static final _COMPONENT_SCHEME = 1;
139 static final _COMPONENT_USER_INFO = 2; 67 static final _COMPONENT_USER_INFO = 2;
140 static final _COMPONENT_DOMAIN = 3; 68 static final _COMPONENT_DOMAIN = 3;
141 static final _COMPONENT_PORT = 4; 69 static final _COMPONENT_PORT = 4;
142 static final _COMPONENT_PATH = 5; 70 static final _COMPONENT_PATH = 5;
143 static final _COMPONENT_QUERY_DATA = 6; 71 static final _COMPONENT_QUERY_DATA = 6;
144 static final _COMPONENT_FRAGMENT = 7; 72 static final _COMPONENT_FRAGMENT = 7;
73
74 /**
75 * Determines whether a URI is absolute.
76 *
77 * See: http://tools.ietf.org/html/rfc3986#section-4.3
78 */
79 bool isAbsolute() {
80 if ("" == scheme) return false;
81 if ("" != fragment) return false;
82 return true;
83
84 /* absolute-URI = scheme ":" hier-part [ "?" query ]
85 * hier-part = "//" authority path-abempty
86 * / path-absolute
87 * / path-rootless
88 * / path-empty
89 *
90 * path = path-abempty ; begins with "/" or is empty
91 * / path-absolute ; begins with "/" but not "//"
92 * / path-noscheme ; begins with a non-colon segment
93 * / path-rootless ; begins with a segment
94 * / path-empty ; zero characters
95 *
96 * path-abempty = *( "/" segment )
97 * path-absolute = "/" [ segment-nz *( "/" segment ) ]
98 * path-noscheme = segment-nz-nc *( "/" segment )
99 * path-rootless = segment-nz *( "/" segment )
100 * path-empty = 0<pchar>
101 * segment = *pchar
102 * segment-nz = 1*pchar
103 * segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" )
104 * ; non-zero-length segment without any colon ":"
105 *
106 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
107 */
108 }
109
110 Uri resolve(String uri) {
111 return resolveUri(new Uri.fromString(uri));
112 }
113
114 Uri resolveUri(Uri reference) {
115 // From RFC 3986.
116 String targetScheme;
117 String targetUserInfo;
118 String targetDomain;
119 int targetPort;
120 String targetPath;
121 String targetQuery;
122 if (reference.scheme != "") {
123 targetScheme = reference.scheme;
124 targetUserInfo = reference.userInfo;
125 targetDomain = reference.domain;
126 targetPort = reference.port;
127 targetPath = removeDotSegments(reference.path);
128 targetQuery = reference.query;
129 } else {
130 if (reference.hasAuthority()) {
131 targetUserInfo = reference.userInfo;
132 targetDomain = reference.domain;
133 targetPort = reference.port;
134 targetPath = removeDotSegments(reference.path);
135 targetQuery = reference.query;
136 } else {
137 if (reference.path == "") {
138 targetPath = this.path;
139 if (reference.query != "") {
140 targetQuery = reference.query;
141 } else {
142 targetQuery = this.query;
143 }
144 } else {
145 if (reference.path.startsWith("/")) {
146 targetPath = removeDotSegments(reference.path);
147 } else {
148 targetPath = removeDotSegments(merge(this.path, reference.path));
149 }
150 targetQuery = reference.query;
151 }
152 targetUserInfo = this.userInfo;
153 targetDomain = this.domain;
154 targetPort = this.port;
155 }
156 targetScheme = this.scheme;
157 }
158 return new Uri(targetScheme, targetUserInfo, targetDomain, targetPort,
159 targetPath, targetQuery, reference.fragment);
160 }
161
162 bool hasAuthority() {
163 return (userInfo != "") || (domain != "") || (port != 0);
164 }
165
166 String toString() {
167 StringBuffer sb = new StringBuffer();
168 _addIfNonEmpty(sb, scheme, scheme, ':');
169 if (hasAuthority() || (scheme == "file")) {
170 sb.add("//");
171 _addIfNonEmpty(sb, userInfo, userInfo, "@");
172 sb.add(domain);
173 if (port != 0) {
174 sb.add(":");
175 sb.add(port.toString());
176 }
177 }
178 sb.add(path);
179 _addIfNonEmpty(sb, query, "?", query);
180 _addIfNonEmpty(sb, fragment, "#", fragment);
181 return sb.toString();
182 }
183
184 static void _addIfNonEmpty(StringBuffer sb, String test,
185 String first, String second) {
186 if ("" != test) {
187 sb.add(first);
188 sb.add(second);
189 }
190 }
145 } 191 }
192
193 String merge(String base, String reference) {
194 if (base == "") return "/$reference";
195 return base.substring(0, base.lastIndexOf("/") + 1) + "$reference";
196 }
197
198 String removeDotSegments(String path) {
199 List<String> output = [];
200 bool appendSlash = false;
201 for (String segment in path.split("/")) {
202 appendSlash = false;
203 if (segment == "..") {
204 if (!output.isEmpty() &&
205 ((output.length != 1) || (output[0] != ""))) output.removeLast();
206 appendSlash = true;
207 } else if ("." == segment) {
208 appendSlash = true;
209 } else {
210 output.add(segment);
211 }
212 }
213 if (appendSlash) output.add("");
214 return Strings.join(output, "/");
215 }
OLDNEW
« no previous file with comments | « dart/tools/testing/dart/test_runner.dart ('k') | deps/compiler.deps/DEPS » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698