| OLD | NEW |
| 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 part of dart.core; | 5 part of dart.core; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * A parsed URI, such as a URL. | 8 * A parsed URI, such as a URL. |
| 9 * | 9 * |
| 10 * **See also:** | 10 * **See also:** |
| 11 * | 11 * |
| 12 * * [URIs][uris] in the [library tour][libtour] | 12 * * [URIs][uris] in the [library tour][libtour] |
| 13 * * [RFC-3986](http://tools.ietf.org/html/rfc3986) | 13 * * [RFC-3986](http://tools.ietf.org/html/rfc3986) |
| 14 * | 14 * |
| 15 * [uris]: http://www.dartlang.org/docs/dart-up-and-running/contents/ch03.html#c
h03-uri | 15 * [uris]: http://www.dartlang.org/docs/dart-up-and-running/contents/ch03.html#c
h03-uri |
| 16 * [libtour]: http://www.dartlang.org/docs/dart-up-and-running/contents/ch03.htm
l | 16 * [libtour]: http://www.dartlang.org/docs/dart-up-and-running/contents/ch03.htm
l |
| 17 */ | 17 */ |
| 18 class Uri { | 18 class Uri { |
| 19 // The host name of the URI. |
| 20 // Set to `null` if there is no authority in a URI. |
| 19 final String _host; | 21 final String _host; |
| 22 // The port. Set to null if there is no port. Normalized to null if |
| 23 // the port is the default port for the scheme. |
| 24 // Set to the value of the default port if an empty port was supplied. |
| 20 int _port; | 25 int _port; |
| 26 // The path. Always non-null. |
| 21 String _path; | 27 String _path; |
| 22 | 28 |
| 23 /** | 29 /** |
| 24 * Returns the scheme component. | 30 * Returns the scheme component. |
| 25 * | 31 * |
| 26 * Returns the empty string if there is no scheme component. | 32 * Returns the empty string if there is no scheme component. |
| 27 */ | 33 */ |
| 34 // We represent the missing scheme as an empty string. |
| 35 // A valid scheme cannot be empty. |
| 28 final String scheme; | 36 final String scheme; |
| 29 | 37 |
| 30 /** | 38 /** |
| 31 * Returns the authority component. | 39 * Returns the authority component. |
| 32 * | 40 * |
| 33 * The authority is formatted from the [userInfo], [host] and [port] | 41 * The authority is formatted from the [userInfo], [host] and [port] |
| 34 * parts. | 42 * parts. |
| 35 * | 43 * |
| 36 * Returns the empty string if there is no authority component. | 44 * Returns the empty string if there is no authority component. |
| 37 */ | 45 */ |
| 38 String get authority { | 46 String get authority { |
| 39 if (!hasAuthority) return ""; | 47 if (!hasAuthority) return ""; |
| 40 var sb = new StringBuffer(); | 48 var sb = new StringBuffer(); |
| 41 _writeAuthority(sb); | 49 _writeAuthority(sb); |
| 42 return sb.toString(); | 50 return sb.toString(); |
| 43 } | 51 } |
| 44 | 52 |
| 45 /** | 53 /** |
| 54 * The user-info part of the authority. |
| 55 * |
| 56 * Does not distinguish between an empty user-info and an absent one. |
| 57 * The value is always non-null. |
| 58 */ |
| 59 final String _userInfo; |
| 60 |
| 61 /** |
| 46 * Returns the user info part of the authority component. | 62 * Returns the user info part of the authority component. |
| 47 * | 63 * |
| 48 * Returns the empty string if there is no user info in the | 64 * Returns the empty string if there is no user info in the |
| 49 * authority component. | 65 * authority component. |
| 50 */ | 66 */ |
| 51 final String userInfo; | 67 String get userInfo => _userInfo; |
| 52 | 68 |
| 53 /** | 69 /** |
| 54 * Returns the host part of the authority component. | 70 * Returns the host part of the authority component. |
| 55 * | 71 * |
| 56 * Returns the empty string if there is no authority component and | 72 * Returns the empty string if there is no authority component and |
| 57 * hence no host. | 73 * hence no host. |
| 58 * | 74 * |
| 59 * If the host is an IP version 6 address, the surrounding `[` and `]` is | 75 * If the host is an IP version 6 address, the surrounding `[` and `]` is |
| 60 * removed. | 76 * removed. |
| 61 */ | 77 */ |
| 62 String get host { | 78 String get host { |
| 63 if (_host != null && _host.startsWith('[')) { | 79 if (_host == null) return ""; |
| 80 if (_host.startsWith('[')) { |
| 64 return _host.substring(1, _host.length - 1); | 81 return _host.substring(1, _host.length - 1); |
| 65 } | 82 } |
| 66 return _host; | 83 return _host; |
| 67 } | 84 } |
| 68 | 85 |
| 69 /** | 86 /** |
| 70 * Returns the port part of the authority component. | 87 * Returns the port part of the authority component. |
| 71 * | 88 * |
| 72 * Returns 0 if there is no port in the authority component. | 89 * Returns the defualt port if there is no port number in the authority |
| 90 * component. That's 80 for http, 443 for https, and 0 for everything else. |
| 73 */ | 91 */ |
| 74 int get port { | 92 int get port { |
| 75 if (_port == 0) { | 93 if (_port == null) return _defaultPort(scheme); |
| 76 if (scheme == "http") return 80; | |
| 77 if (scheme == "https") return 443; | |
| 78 } | |
| 79 return _port; | 94 return _port; |
| 80 } | 95 } |
| 81 | 96 |
| 97 // The default port for the scheme of this Uri.. |
| 98 static int _defaultPort(String scheme) { |
| 99 if (scheme == "http") return 80; |
| 100 if (scheme == "https") return 443; |
| 101 return 0; |
| 102 } |
| 103 |
| 82 /** | 104 /** |
| 83 * Returns the path component. | 105 * Returns the path component. |
| 84 * | 106 * |
| 85 * The returned path is encoded. To get direct access to the decoded | 107 * The returned path is encoded. To get direct access to the decoded |
| 86 * path use [pathSegments]. | 108 * path use [pathSegments]. |
| 87 * | 109 * |
| 88 * Returns the empty string if there is no path component. | 110 * Returns the empty string if there is no path component. |
| 89 */ | 111 */ |
| 90 String get path => _path; | 112 String get path => _path; |
| 91 | 113 |
| 114 // The query content, or null if there is no query. |
| 115 final String _query; |
| 116 |
| 92 /** | 117 /** |
| 93 * Returns the query component. The returned query is encoded. To get | 118 * Returns the query component. The returned query is encoded. To get |
| 94 * direct access to the decoded query use [queryParameters]. | 119 * direct access to the decoded query use [queryParameters]. |
| 95 * | 120 * |
| 96 * Returns the empty string if there is no query component. | 121 * Returns the empty string if there is no query component. |
| 97 */ | 122 */ |
| 98 final String query; | 123 String get query => (_query == null) ? "" : _query; |
| 124 |
| 125 // The fragment content, or null if there is no fragment. |
| 126 final String _fragment; |
| 99 | 127 |
| 100 /** | 128 /** |
| 101 * Returns the fragment identifier component. | 129 * Returns the fragment identifier component. |
| 102 * | 130 * |
| 103 * Returns the empty string if there is no fragment identifier | 131 * Returns the empty string if there is no fragment identifier |
| 104 * component. | 132 * component. |
| 105 */ | 133 */ |
| 106 final String fragment; | 134 String get fragment => (_fragment == null) ? "" : _fragment; |
| 107 | 135 |
| 108 /** | 136 /** |
| 109 * Cache the computed return value of [pathSegements]. | 137 * Cache the computed return value of [pathSegements]. |
| 110 */ | 138 */ |
| 111 List<String> _pathSegments; | 139 List<String> _pathSegments; |
| 112 | 140 |
| 113 /** | 141 /** |
| 114 * Cache the computed return value of [queryParameters]. | 142 * Cache the computed return value of [queryParameters]. |
| 115 */ | 143 */ |
| 116 Map<String, String> _queryParameters; | 144 Map<String, String> _queryParameters; |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 175 // | 203 // |
| 176 // query = *( pchar / "/" / "?" ) | 204 // query = *( pchar / "/" / "?" ) |
| 177 // | 205 // |
| 178 // fragment = *( pchar / "/" / "?" ) | 206 // fragment = *( pchar / "/" / "?" ) |
| 179 bool isRegName(int ch) { | 207 bool isRegName(int ch) { |
| 180 return ch < 128 && ((_regNameTable[ch >> 4] & (1 << (ch & 0x0f))) != 0); | 208 return ch < 128 && ((_regNameTable[ch >> 4] & (1 << (ch & 0x0f))) != 0); |
| 181 } | 209 } |
| 182 const int EOI = -1; | 210 const int EOI = -1; |
| 183 | 211 |
| 184 String scheme = ""; | 212 String scheme = ""; |
| 185 String path; | |
| 186 String userinfo = ""; | 213 String userinfo = ""; |
| 187 String host = ""; | 214 String host = null; |
| 188 int port = 0; | 215 int port = null; |
| 189 String query = ""; | 216 String path = null; |
| 190 String fragment = ""; | 217 String query = null; |
| 218 String fragment = null; |
| 191 | 219 |
| 192 int index = 0; | 220 int index = 0; |
| 193 int pathStart = 0; | 221 int pathStart = 0; |
| 194 // End of input-marker. | 222 // End of input-marker. |
| 195 int char = EOI; | 223 int char = EOI; |
| 196 | 224 |
| 197 void parseAuth() { | 225 void parseAuth() { |
| 198 if (index == uri.length) { | 226 if (index == uri.length) { |
| 199 char = EOI; | 227 char = EOI; |
| 200 return; | 228 return; |
| (...skipping 26 matching lines...) Expand all Loading... |
| 227 index++; | 255 index++; |
| 228 char = EOI; | 256 char = EOI; |
| 229 } | 257 } |
| 230 int hostStart = authStart; | 258 int hostStart = authStart; |
| 231 int hostEnd = index; | 259 int hostEnd = index; |
| 232 if (lastAt >= 0) { | 260 if (lastAt >= 0) { |
| 233 userinfo = _makeUserInfo(uri, authStart, lastAt); | 261 userinfo = _makeUserInfo(uri, authStart, lastAt); |
| 234 hostStart = lastAt + 1; | 262 hostStart = lastAt + 1; |
| 235 } | 263 } |
| 236 if (lastColon >= 0) { | 264 if (lastColon >= 0) { |
| 237 int portNumber = 0; | 265 int portNumber; |
| 238 for (int i = lastColon + 1; i < index; i++) { | 266 if (lastColon + 1 < index) { |
| 239 int digit = uri.codeUnitAt(i); | 267 portNumber = 0; |
| 240 if (_ZERO > digit || _NINE < digit) { | 268 for (int i = lastColon + 1; i < index; i++) { |
| 241 _fail(uri, i, "Invalid port number"); | 269 int digit = uri.codeUnitAt(i); |
| 270 if (_ZERO > digit || _NINE < digit) { |
| 271 _fail(uri, i, "Invalid port number"); |
| 272 } |
| 273 portNumber = portNumber * 10 + (digit - _ZERO); |
| 242 } | 274 } |
| 243 portNumber = portNumber * 10 + (digit - _ZERO); | |
| 244 } | 275 } |
| 245 port = _makePort(portNumber, scheme); | 276 port = _makePort(portNumber, scheme); |
| 246 hostEnd = lastColon; | 277 hostEnd = lastColon; |
| 247 } | 278 } |
| 248 host = _makeHost(uri, hostStart, hostEnd, true); | 279 host = _makeHost(uri, hostStart, hostEnd, true); |
| 249 if (index < uri.length) { | 280 if (index < uri.length) { |
| 250 char = uri.codeUnitAt(index); | 281 char = uri.codeUnitAt(index); |
| 251 } | 282 } |
| 252 } | 283 } |
| 253 | 284 |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 333 char = uri.codeUnitAt(index); | 364 char = uri.codeUnitAt(index); |
| 334 if (char == _QUESTION || char == _NUMBER_SIGN) { | 365 if (char == _QUESTION || char == _NUMBER_SIGN) { |
| 335 break; | 366 break; |
| 336 } | 367 } |
| 337 char = EOI; | 368 char = EOI; |
| 338 } | 369 } |
| 339 state = NOT_IN_PATH; | 370 state = NOT_IN_PATH; |
| 340 } | 371 } |
| 341 | 372 |
| 342 assert(state == NOT_IN_PATH); | 373 assert(state == NOT_IN_PATH); |
| 343 bool ensureLeadingSlash = (host != "" || scheme == "file"); | 374 bool ensureLeadingSlash = (host != null || scheme == "file"); |
| 344 path = _makePath(uri, pathStart, index, null, ensureLeadingSlash); | 375 path = _makePath(uri, pathStart, index, null, ensureLeadingSlash); |
| 345 | 376 |
| 346 if (char == _QUESTION) { | 377 if (char == _QUESTION) { |
| 347 int numberSignIndex = uri.indexOf('#', index + 1); | 378 int numberSignIndex = uri.indexOf('#', index + 1); |
| 348 if (numberSignIndex < 0) { | 379 if (numberSignIndex < 0) { |
| 349 query = _makeQuery(uri, index + 1, uri.length, null); | 380 query = _makeQuery(uri, index + 1, uri.length, null); |
| 350 } else { | 381 } else { |
| 351 query = _makeQuery(uri, index + 1, numberSignIndex, null); | 382 query = _makeQuery(uri, index + 1, numberSignIndex, null); |
| 352 fragment = _makeFragment(uri, numberSignIndex + 1, uri.length); | 383 fragment = _makeFragment(uri, numberSignIndex + 1, uri.length); |
| 353 } | 384 } |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 390 } | 421 } |
| 391 // Combine message, slice and a caret pointing to the error index. | 422 // Combine message, slice and a caret pointing to the error index. |
| 392 message = "$message$pre${uri.substring(min, max)}$post\n" | 423 message = "$message$pre${uri.substring(min, max)}$post\n" |
| 393 "${' ' * (pre.length + index - min)}^"; | 424 "${' ' * (pre.length + index - min)}^"; |
| 394 } | 425 } |
| 395 throw new FormatException(message); | 426 throw new FormatException(message); |
| 396 } | 427 } |
| 397 | 428 |
| 398 /// Internal non-verifying constructor. Only call with validated arguments. | 429 /// Internal non-verifying constructor. Only call with validated arguments. |
| 399 Uri._internal(this.scheme, | 430 Uri._internal(this.scheme, |
| 400 this.userInfo, | 431 this._userInfo, |
| 401 this._host, | 432 this._host, |
| 402 this._port, | 433 this._port, |
| 403 this._path, | 434 this._path, |
| 404 this.query, | 435 this._query, |
| 405 this.fragment); | 436 this._fragment); |
| 406 | 437 |
| 407 /** | 438 /** |
| 408 * Creates a new URI from its components. | 439 * Creates a new URI from its components. |
| 409 * | 440 * |
| 410 * Each component is set through a named argument. Any number of | 441 * Each component is set through a named argument. Any number of |
| 411 * components can be provided. The default value for the components | 442 * components can be provided. The [path] and [query] components can be set |
| 412 * not provided is the empry string, except for [port] which has a | 443 * using either of two different named arguments. |
| 413 * default value of 0. The [path] and [query] components can be set | |
| 414 * using two different named arguments. | |
| 415 * | 444 * |
| 416 * The scheme component is set through [scheme]. The scheme is | 445 * The scheme component is set through [scheme]. The scheme is |
| 417 * normalized to all lowercase letters. | 446 * normalized to all lowercase letters. If the scheme is omitted or empty, |
| 447 * the URI will not have a scheme part. |
| 418 * | 448 * |
| 419 * The user info part of the authority component is set through | 449 * The user info part of the authority component is set through |
| 420 * [userInfo]. | 450 * [userInfo]. It defaults to the empty string, which will be omitted |
| 451 * from the string representation of the URI. |
| 421 * | 452 * |
| 422 * The host part of the authority component is set through | 453 * The host part of the authority component is set through |
| 423 * [host]. The host can either be a hostname, an IPv4 address or an | 454 * [host]. The host can either be a hostname, an IPv4 address or an |
| 424 * IPv6 address, contained in '[' and ']'. If the host contains a | 455 * IPv6 address, contained in '[' and ']'. If the host contains a |
| 425 * ':' character, the '[' and ']' are added if not already provided. | 456 * ':' character, the '[' and ']' are added if not already provided. |
| 426 * The host is normalized to all lowercase letters. | 457 * The host is normalized to all lowercase letters. |
| 427 * | 458 * |
| 428 * The port part of the authority component is set through | 459 * The port part of the authority component is set through |
| 429 * [port]. The port is normalized for scheme http and https where | 460 * [port]. |
| 430 * port 80 and port 443 respectively is set. | 461 * If [port] is omitted or `null`, it implies the default port for |
| 462 * the URI's scheme, and is equivalent to passing that port explicitly. |
| 463 * The recognized schemes, and their default ports, are "http" (80) and |
| 464 * "https" (443). All other schemes are considered as having zero as the |
| 465 * default port. |
| 466 * |
| 467 * If any of `userInfo`, `host` or `port` are provided, |
| 468 * the URI will have an autority according to [hasAuthority]. |
| 431 * | 469 * |
| 432 * The path component is set through either [path] or | 470 * The path component is set through either [path] or |
| 433 * [pathSegments]. When [path] is used, the provided string is | 471 * [pathSegments]. When [path] is used, it should be a valid URI path, |
| 434 * expected to be fully percent-encoded, and is used in its literal | 472 * but invalid characters, except the general delimiters ':/@[]?#', |
| 435 * form. When [pathSegments] is used, each of the provided segments | 473 * will be escaped if necessary. |
| 436 * is percent-encoded and joined using the forward slash | 474 * When [pathSegments] is used, each of the provided segments |
| 475 * is first percent-encoded and then joined using the forward slash |
| 437 * separator. The percent-encoding of the path segments encodes all | 476 * separator. The percent-encoding of the path segments encodes all |
| 438 * characters except for the unreserved characters and the following | 477 * characters except for the unreserved characters and the following |
| 439 * list of characters: `!$&'()*+,;=:@`. If the other components | 478 * list of characters: `!$&'()*+,;=:@`. If the other components |
| 440 * calls for an absolute path a leading slash `/` is prepended if | 479 * calls for an absolute path a leading slash `/` is prepended if |
| 441 * not already there. | 480 * not already there. |
| 442 * | 481 * |
| 443 * The query component is set through either [query] or | 482 * The query component is set through either [query] or |
| 444 * [queryParameters]. When [query] is used the provided string is | 483 * [queryParameters]. When [query] is used the provided string should |
| 445 * expected to be fully percent-encoded and is used in its literal | 484 * be a valid URI query, but invalid characters other than general delimiters, |
| 446 * form. When [queryParameters] is used the query is built from the | 485 * will be escaped if necessary. |
| 486 * When [queryParameters] is used the query is built from the |
| 447 * provided map. Each key and value in the map is percent-encoded | 487 * provided map. Each key and value in the map is percent-encoded |
| 448 * and joined using equal and ampersand characters. The | 488 * and joined using equal and ampersand characters. The |
| 449 * percent-encoding of the keys and values encodes all characters | 489 * percent-encoding of the keys and values encodes all characters |
| 450 * except for the unreserved characters. | 490 * except for the unreserved characters. |
| 491 * If both `query` and `queryParameters` are omitted or `null`, the |
| 492 * URI will have no query part. |
| 451 * | 493 * |
| 452 * The fragment component is set through [fragment]. | 494 * The fragment component is set through [fragment]. |
| 495 * It should be a valid URI fragment, but invalid characters other than |
| 496 * general delimiters, will be escaped if necessary. |
| 497 * If `fragment` is omitted or `null`, the URI will have no fragment part. |
| 453 */ | 498 */ |
| 454 factory Uri({String scheme, | 499 factory Uri({String scheme : "", |
| 455 String userInfo: "", | 500 String userInfo : "", |
| 456 String host: "", | 501 String host, |
| 457 port: 0, | 502 int port, |
| 458 String path, | 503 String path, |
| 459 Iterable<String> pathSegments, | 504 Iterable<String> pathSegments, |
| 460 String query, | 505 String query, |
| 461 Map<String, String> queryParameters, | 506 Map<String, String> queryParameters, |
| 462 fragment: ""}) { | 507 fragment}) { |
| 463 scheme = _makeScheme(scheme, _stringOrNullLength(scheme)); | 508 scheme = _makeScheme(scheme, _stringOrNullLength(scheme)); |
| 464 userInfo = _makeUserInfo(userInfo, 0, _stringOrNullLength(userInfo)); | 509 userInfo = _makeUserInfo(userInfo, 0, _stringOrNullLength(userInfo)); |
| 465 host = _makeHost(host, 0, _stringOrNullLength(host), false); | 510 host = _makeHost(host, 0, _stringOrNullLength(host), false); |
| 466 query = _makeQuery(query, 0, _stringOrNullLength(query), queryParameters); | 511 query = _makeQuery(query, 0, _stringOrNullLength(query), queryParameters); |
| 467 fragment = _makeFragment(fragment, 0, _stringOrNullLength(fragment)); | 512 fragment = _makeFragment(fragment, 0, _stringOrNullLength(fragment)); |
| 468 port = _makePort(port, scheme); | 513 port = _makePort(port, scheme); |
| 469 bool ensureLeadingSlash = (host != "" || scheme == "file"); | 514 bool isFile = (scheme == "file"); |
| 515 if (host == null && |
| 516 (userInfo.isNotEmpty || port != null || isFile)) { |
| 517 host = ""; |
| 518 } |
| 519 bool ensureLeadingSlash = (host != null || isFile); |
| 470 path = _makePath(path, 0, _stringOrNullLength(path), pathSegments, | 520 path = _makePath(path, 0, _stringOrNullLength(path), pathSegments, |
| 471 ensureLeadingSlash); | 521 ensureLeadingSlash); |
| 472 | 522 |
| 473 return new Uri._internal(scheme, userInfo, host, port, | 523 return new Uri._internal(scheme, userInfo, host, port, |
| 474 path, query, fragment); | 524 path, query, fragment); |
| 475 } | 525 } |
| 476 | 526 |
| 477 /** | 527 /** |
| 478 * Creates a new `http` URI from authority, path and query. | 528 * Creates a new `http` URI from authority, path and query. |
| 479 * | 529 * |
| 480 * Examples: | 530 * Examples: |
| 481 * | 531 * |
| 482 * ``` | 532 * ``` |
| 483 * // http://example.org/path?q=dart. | 533 * // http://example.org/path?q=dart. |
| 484 * new Uri.http("google.com", "/search", { "q" : "dart" }); | 534 * new Uri.http("google.com", "/search", { "q" : "dart" }); |
| 485 * | 535 * |
| 486 * // http://user:pass@localhost:8080 | 536 * // http://user:pass@localhost:8080 |
| 487 * new Uri.http("user:pass@localhost:8080", ""); | 537 * new Uri.http("user:pass@localhost:8080", ""); |
| 488 * | 538 * |
| 489 * // http://example.org/a%20b | 539 * // http://example.org/a%20b |
| 490 * new Uri.http("example.org", "a b"); | 540 * new Uri.http("example.org", "a b"); |
| 491 * | 541 * |
| 492 * // http://example.org/a%252F | 542 * // http://example.org/a%252F |
| 493 * new Uri.http("example.org", "/a%2F"); | 543 * new Uri.http("example.org", "/a%2F"); |
| 494 * ``` | 544 * ``` |
| 495 * | 545 * |
| 496 * The `scheme` is always set to `http`. | 546 * The `scheme` is always set to `http`. |
| 497 * | 547 * |
| 498 * The `userInfo`, `host` and `port` components are set from the | 548 * The `userInfo`, `host` and `port` components are set from the |
| 499 * [authority] argument. | 549 * [authority] argument. If `authority` is `null` or empty, |
| 550 * the created `Uri` will have no authority, and will not be directly usable |
| 551 * as an HTTP URL, which must have a non-empty host. |
| 500 * | 552 * |
| 501 * The `path` component is set from the [unencodedPath] | 553 * The `path` component is set from the [unencodedPath] |
| 502 * argument. The path passed must not be encoded as this constructor | 554 * argument. The path passed must not be encoded as this constructor |
| 503 * encodes the path. | 555 * encodes the path. |
| 504 * | 556 * |
| 505 * The `query` component is set from the optional [queryParameters] | 557 * The `query` component is set from the optional [queryParameters] |
| 506 * argument. | 558 * argument. |
| 507 */ | 559 */ |
| 508 factory Uri.http(String authority, | 560 factory Uri.http(String authority, |
| 509 String unencodedPath, | 561 String unencodedPath, |
| (...skipping 11 matching lines...) Expand all Loading... |
| 521 String unencodedPath, | 573 String unencodedPath, |
| 522 [Map<String, String> queryParameters]) { | 574 [Map<String, String> queryParameters]) { |
| 523 return _makeHttpUri("https", authority, unencodedPath, queryParameters); | 575 return _makeHttpUri("https", authority, unencodedPath, queryParameters); |
| 524 } | 576 } |
| 525 | 577 |
| 526 static Uri _makeHttpUri(String scheme, | 578 static Uri _makeHttpUri(String scheme, |
| 527 String authority, | 579 String authority, |
| 528 String unencodedPath, | 580 String unencodedPath, |
| 529 Map<String, String> queryParameters) { | 581 Map<String, String> queryParameters) { |
| 530 var userInfo = ""; | 582 var userInfo = ""; |
| 531 var host = ""; | 583 var host = null; |
| 532 var port = 0; | 584 var port = null; |
| 533 | 585 |
| 534 var hostStart = 0; | 586 if (authority != null && authority.isNotEmpty) { |
| 535 // Split off the user info. | 587 var hostStart = 0; |
| 536 bool hasUserInfo = false; | 588 // Split off the user info. |
| 537 for (int i = 0; i < authority.length; i++) { | 589 bool hasUserInfo = false; |
| 538 if (authority.codeUnitAt(i) == _AT_SIGN) { | 590 for (int i = 0; i < authority.length; i++) { |
| 539 hasUserInfo = true; | 591 if (authority.codeUnitAt(i) == _AT_SIGN) { |
| 540 userInfo = authority.substring(0, i); | 592 hasUserInfo = true; |
| 541 hostStart = i + 1; | 593 userInfo = authority.substring(0, i); |
| 542 break; | 594 hostStart = i + 1; |
| 595 break; |
| 596 } |
| 543 } | 597 } |
| 598 var hostEnd = hostStart; |
| 599 if (hostStart < authority.length && |
| 600 authority.codeUnitAt(hostStart) == _LEFT_BRACKET) { |
| 601 // IPv6 host. |
| 602 for (; hostEnd < authority.length; hostEnd++) { |
| 603 if (authority.codeUnitAt(hostEnd) == _RIGHT_BRACKET) break; |
| 604 } |
| 605 if (hostEnd == authority.length) { |
| 606 throw new FormatException("Invalid IPv6 host entry."); |
| 607 } |
| 608 parseIPv6Address(authority, hostStart + 1, hostEnd); |
| 609 hostEnd++; // Skip the closing bracket. |
| 610 if (hostEnd != authority.length && |
| 611 authority.codeUnitAt(hostEnd) != _COLON) { |
| 612 throw new FormatException("Invalid end of authority"); |
| 613 } |
| 614 } |
| 615 // Split host and port. |
| 616 bool hasPort = false; |
| 617 for (; hostEnd < authority.length; hostEnd++) { |
| 618 if (authority.codeUnitAt(hostEnd) == _COLON) { |
| 619 var portString = authority.substring(hostEnd + 1); |
| 620 // We allow the empty port - falling back to initial value. |
| 621 if (portString.isNotEmpty) port = int.parse(portString); |
| 622 break; |
| 623 } |
| 624 } |
| 625 host = authority.substring(hostStart, hostEnd); |
| 544 } | 626 } |
| 545 var hostEnd = hostStart; | |
| 546 if (hostStart < authority.length && | |
| 547 authority.codeUnitAt(hostStart) == _LEFT_BRACKET) { | |
| 548 // IPv6 host. | |
| 549 for (; hostEnd < authority.length; hostEnd++) { | |
| 550 if (authority.codeUnitAt(hostEnd) == _RIGHT_BRACKET) break; | |
| 551 } | |
| 552 if (hostEnd == authority.length) { | |
| 553 throw new FormatException("Invalid IPv6 host entry."); | |
| 554 } | |
| 555 parseIPv6Address(authority, hostStart + 1, hostEnd); | |
| 556 hostEnd++; // Skip the closing bracket. | |
| 557 if (hostEnd != authority.length && | |
| 558 authority.codeUnitAt(hostEnd) != _COLON) { | |
| 559 throw new FormatException("Invalid end of authority"); | |
| 560 } | |
| 561 } | |
| 562 // Split host and port. | |
| 563 bool hasPort = false; | |
| 564 for (; hostEnd < authority.length; hostEnd++) { | |
| 565 if (authority.codeUnitAt(hostEnd) == _COLON) { | |
| 566 var portString = authority.substring(hostEnd + 1); | |
| 567 // We allow the empty port - falling back to initial value. | |
| 568 if (portString.isNotEmpty) port = int.parse(portString); | |
| 569 break; | |
| 570 } | |
| 571 } | |
| 572 host = authority.substring(hostStart, hostEnd); | |
| 573 | |
| 574 return new Uri(scheme: scheme, | 627 return new Uri(scheme: scheme, |
| 575 userInfo: userInfo, | 628 userInfo: userInfo, |
| 576 host: host, | 629 host: host, |
| 577 port: port, | 630 port: port, |
| 578 pathSegments: unencodedPath.split("/"), | 631 pathSegments: unencodedPath.split("/"), |
| 579 queryParameters: queryParameters); | 632 queryParameters: queryParameters); |
| 580 } | 633 } |
| 581 | 634 |
| 582 /** | 635 /** |
| 583 * Creates a new file URI from an absolute or relative file path. | 636 * Creates a new file URI from an absolute or relative file path. |
| (...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 714 throw new ArgumentError("Illegal drive letter " + | 767 throw new ArgumentError("Illegal drive letter " + |
| 715 new String.fromCharCode(charCode)); | 768 new String.fromCharCode(charCode)); |
| 716 } else { | 769 } else { |
| 717 throw new UnsupportedError("Illegal drive letter " + | 770 throw new UnsupportedError("Illegal drive letter " + |
| 718 new String.fromCharCode(charCode)); | 771 new String.fromCharCode(charCode)); |
| 719 } | 772 } |
| 720 } | 773 } |
| 721 | 774 |
| 722 static _makeFileUri(String path) { | 775 static _makeFileUri(String path) { |
| 723 String sep = "/"; | 776 String sep = "/"; |
| 724 if (path.length > 0 && path[0] == sep) { | 777 if (path.startsWith(sep)) { |
| 725 // Absolute file:// URI. | 778 // Absolute file:// URI. |
| 726 return new Uri(scheme: "file", pathSegments: path.split(sep)); | 779 return new Uri(scheme: "file", pathSegments: path.split(sep)); |
| 727 } else { | 780 } else { |
| 728 // Relative URI. | 781 // Relative URI. |
| 729 return new Uri(pathSegments: path.split(sep)); | 782 return new Uri(pathSegments: path.split(sep)); |
| 730 } | 783 } |
| 731 } | 784 } |
| 732 | 785 |
| 733 static _makeWindowsFileUrl(String path) { | 786 static _makeWindowsFileUrl(String path) { |
| 734 if (path.startsWith("\\\\?\\")) { | 787 if (path.startsWith("\\\\?\\")) { |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 823 */ | 876 */ |
| 824 Map<String, String> get queryParameters { | 877 Map<String, String> get queryParameters { |
| 825 if (_queryParameters == null) { | 878 if (_queryParameters == null) { |
| 826 _queryParameters = new UnmodifiableMapView(splitQueryString(query)); | 879 _queryParameters = new UnmodifiableMapView(splitQueryString(query)); |
| 827 } | 880 } |
| 828 return _queryParameters; | 881 return _queryParameters; |
| 829 } | 882 } |
| 830 | 883 |
| 831 static int _makePort(int port, String scheme) { | 884 static int _makePort(int port, String scheme) { |
| 832 // Perform scheme specific normalization. | 885 // Perform scheme specific normalization. |
| 833 if (port == 80 && scheme == "http") { | 886 if (port != null && port == _defaultPort(scheme)) return null; |
| 834 return 0; | |
| 835 } | |
| 836 if (port == 443 && scheme == "https") { | |
| 837 return 0; | |
| 838 } | |
| 839 return port; | 887 return port; |
| 840 } | 888 } |
| 841 | 889 |
| 842 /** | 890 /** |
| 843 * Check and normalize a most name. | 891 * Check and normalize a most name. |
| 844 * | 892 * |
| 845 * If the host name starts and ends with '[' and ']', it is considered an | 893 * If the host name starts and ends with '[' and ']', it is considered an |
| 846 * IPv6 address. If [strictIPv6] is false, the address is also considered | 894 * IPv6 address. If [strictIPv6] is false, the address is also considered |
| 847 * an IPv6 address if it contains any ':' character. | 895 * an IPv6 address if it contains any ':' character. |
| 848 * | 896 * |
| 849 * If it is not an IPv6 address, it is case- and escape-normalized. | 897 * If it is not an IPv6 address, it is case- and escape-normalized. |
| 850 * This escapes all characters not valid in a reg-name, | 898 * This escapes all characters not valid in a reg-name, |
| 851 * and converts all non-escape upper-case letters to lower-case. | 899 * and converts all non-escape upper-case letters to lower-case. |
| 852 */ | 900 */ |
| 853 static String _makeHost(String host, int start, int end, bool strictIPv6) { | 901 static String _makeHost(String host, int start, int end, bool strictIPv6) { |
| 854 // TODO(lrn): Should we normalize IPv6 addresses according to RFC 5952? | 902 // TODO(lrn): Should we normalize IPv6 addresses according to RFC 5952? |
| 855 | |
| 856 if (host == null) return null; | 903 if (host == null) return null; |
| 857 if (start == end) return ""; | 904 if (start == end) return ""; |
| 858 // Host is an IPv6 address if it starts with '[' or contains a colon. | 905 // Host is an IPv6 address if it starts with '[' or contains a colon. |
| 859 if (host.codeUnitAt(start) == _LEFT_BRACKET) { | 906 if (host.codeUnitAt(start) == _LEFT_BRACKET) { |
| 860 if (host.codeUnitAt(end - 1) != _RIGHT_BRACKET) { | 907 if (host.codeUnitAt(end - 1) != _RIGHT_BRACKET) { |
| 861 _fail(host, start, 'Missing end `]` to match `[` in host'); | 908 _fail(host, start, 'Missing end `]` to match `[` in host'); |
| 862 } | 909 } |
| 863 parseIPv6Address(host, start + 1, end - 1); | 910 parseIPv6Address(host, start + 1, end - 1); |
| 864 // RFC 5952 requires hex digits to be lower case. | 911 // RFC 5952 requires hex digits to be lower case. |
| 865 return host.substring(start, end).toLowerCase(); | 912 return host.substring(start, end).toLowerCase(); |
| (...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 978 if (_LOWER_CASE_A <= char && _LOWER_CASE_Z >= char) { | 1025 if (_LOWER_CASE_A <= char && _LOWER_CASE_Z >= char) { |
| 979 allLowercase = false; | 1026 allLowercase = false; |
| 980 } | 1027 } |
| 981 } | 1028 } |
| 982 scheme = scheme.substring(0, end); | 1029 scheme = scheme.substring(0, end); |
| 983 if (!allLowercase) scheme = scheme.toLowerCase(); | 1030 if (!allLowercase) scheme = scheme.toLowerCase(); |
| 984 return scheme; | 1031 return scheme; |
| 985 } | 1032 } |
| 986 | 1033 |
| 987 static String _makeUserInfo(String userInfo, int start, int end) { | 1034 static String _makeUserInfo(String userInfo, int start, int end) { |
| 988 if (userInfo == null) return "null"; | 1035 if (userInfo == null) return ""; |
| 989 return _normalize(userInfo, start, end, _userinfoTable); | 1036 return _normalize(userInfo, start, end, _userinfoTable); |
| 990 } | 1037 } |
| 991 | 1038 |
| 992 static String _makePath(String path, int start, int end, | 1039 static String _makePath(String path, int start, int end, |
| 993 Iterable<String> pathSegments, | 1040 Iterable<String> pathSegments, |
| 994 bool ensureLeadingSlash) { | 1041 bool ensureLeadingSlash) { |
| 995 if (path == null && pathSegments == null) return ""; | 1042 if (path == null && pathSegments == null) return ""; |
| 996 if (path != null && pathSegments != null) { | 1043 if (path != null && pathSegments != null) { |
| 997 throw new ArgumentError('Both path and pathSegments specified'); | 1044 throw new ArgumentError('Both path and pathSegments specified'); |
| 998 } | 1045 } |
| 999 var result; | 1046 var result; |
| 1000 if (path != null) { | 1047 if (path != null) { |
| 1001 result = _normalize(path, start, end, _pathCharOrSlashTable); | 1048 result = _normalize(path, start, end, _pathCharOrSlashTable); |
| 1002 } else { | 1049 } else { |
| 1003 result = pathSegments.map((s) => _uriEncode(_pathCharTable, s)).join("/"); | 1050 result = pathSegments.map((s) => _uriEncode(_pathCharTable, s)).join("/"); |
| 1004 } | 1051 } |
| 1005 if (ensureLeadingSlash && result.isNotEmpty && !result.startsWith("/")) { | 1052 if (ensureLeadingSlash && result.isNotEmpty && !result.startsWith("/")) { |
| 1006 return "/$result"; | 1053 return "/$result"; |
| 1007 } | 1054 } |
| 1008 return result; | 1055 return result; |
| 1009 } | 1056 } |
| 1010 | 1057 |
| 1011 static String _makeQuery(String query, int start, int end, | 1058 static String _makeQuery(String query, int start, int end, |
| 1012 Map<String, String> queryParameters) { | 1059 Map<String, String> queryParameters) { |
| 1013 if (query == null && queryParameters == null) return ""; | 1060 if (query == null && queryParameters == null) return null; |
| 1014 if (query != null && queryParameters != null) { | 1061 if (query != null && queryParameters != null) { |
| 1015 throw new ArgumentError('Both query and queryParameters specified'); | 1062 throw new ArgumentError('Both query and queryParameters specified'); |
| 1016 } | 1063 } |
| 1017 if (query != null) return _normalize(query, start, end, _queryCharTable); | 1064 if (query != null) return _normalize(query, start, end, _queryCharTable); |
| 1018 | 1065 |
| 1019 var result = new StringBuffer(); | 1066 var result = new StringBuffer(); |
| 1020 var first = true; | 1067 var first = true; |
| 1021 queryParameters.forEach((key, value) { | 1068 queryParameters.forEach((key, value) { |
| 1022 if (!first) { | 1069 if (!first) { |
| 1023 result.write("&"); | 1070 result.write("&"); |
| 1024 } | 1071 } |
| 1025 first = false; | 1072 first = false; |
| 1026 result.write(Uri.encodeQueryComponent(key)); | 1073 result.write(Uri.encodeQueryComponent(key)); |
| 1027 if (value != null && !value.isEmpty) { | 1074 if (value != null && !value.isEmpty) { |
| 1028 result.write("="); | 1075 result.write("="); |
| 1029 result.write(Uri.encodeQueryComponent(value)); | 1076 result.write(Uri.encodeQueryComponent(value)); |
| 1030 } | 1077 } |
| 1031 }); | 1078 }); |
| 1032 return result.toString(); | 1079 return result.toString(); |
| 1033 } | 1080 } |
| 1034 | 1081 |
| 1035 static String _makeFragment(String fragment, int start, int end) { | 1082 static String _makeFragment(String fragment, int start, int end) { |
| 1036 if (fragment == null) return ""; | 1083 if (fragment == null) return null; |
| 1037 return _normalize(fragment, start, end, _queryCharTable); | 1084 return _normalize(fragment, start, end, _queryCharTable); |
| 1038 } | 1085 } |
| 1039 | 1086 |
| 1040 static int _stringOrNullLength(String s) => (s == null) ? 0 : s.length; | 1087 static int _stringOrNullLength(String s) => (s == null) ? 0 : s.length; |
| 1041 | 1088 |
| 1042 static bool _isHexDigit(int char) { | 1089 static bool _isHexDigit(int char) { |
| 1043 if (_NINE >= char) return _ZERO <= char; | 1090 if (_NINE >= char) return _ZERO <= char; |
| 1044 char |= 0x20; | 1091 char |= 0x20; |
| 1045 return _LOWER_CASE_A <= char && _LOWER_CASE_F >= char; | 1092 return _LOWER_CASE_A <= char && _LOWER_CASE_F >= char; |
| 1046 } | 1093 } |
| (...skipping 219 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1266 * | 1313 * |
| 1267 * Returns the resolved URI. | 1314 * Returns the resolved URI. |
| 1268 * | 1315 * |
| 1269 * The algorithm for resolving a reference is described in | 1316 * The algorithm for resolving a reference is described in |
| 1270 * [RFC-3986 Section 5] | 1317 * [RFC-3986 Section 5] |
| 1271 * (http://tools.ietf.org/html/rfc3986#section-5 "RFC-1123"). | 1318 * (http://tools.ietf.org/html/rfc3986#section-5 "RFC-1123"). |
| 1272 */ | 1319 */ |
| 1273 Uri resolveUri(Uri reference) { | 1320 Uri resolveUri(Uri reference) { |
| 1274 // From RFC 3986. | 1321 // From RFC 3986. |
| 1275 String targetScheme; | 1322 String targetScheme; |
| 1276 String targetUserInfo; | 1323 String targetUserInfo = ""; |
| 1277 String targetHost; | 1324 String targetHost; |
| 1278 int targetPort; | 1325 int targetPort; |
| 1279 String targetPath; | 1326 String targetPath; |
| 1280 String targetQuery; | 1327 String targetQuery; |
| 1281 if (reference.scheme != "") { | 1328 if (reference.scheme.isNotEmpty) { |
| 1282 targetScheme = reference.scheme; | 1329 targetScheme = reference.scheme; |
| 1283 targetUserInfo = reference.userInfo; | |
| 1284 targetHost = reference.host; | |
| 1285 targetPort = reference.port; | |
| 1286 targetPath = _removeDotSegments(reference.path); | |
| 1287 targetQuery = reference.query; | |
| 1288 } else { | |
| 1289 if (reference.hasAuthority) { | 1330 if (reference.hasAuthority) { |
| 1290 targetUserInfo = reference.userInfo; | 1331 targetUserInfo = reference.userInfo; |
| 1291 targetHost = reference.host; | 1332 targetHost = reference.host; |
| 1292 targetPort = reference.port; | 1333 targetPort = reference.hasPort ? reference.port : null; |
| 1334 } |
| 1335 targetPath = _removeDotSegments(reference.path); |
| 1336 if (reference.hasQuery) { |
| 1337 targetQuery = reference.query; |
| 1338 } |
| 1339 } else { |
| 1340 targetScheme = this.scheme; |
| 1341 if (reference.hasAuthority) { |
| 1342 targetUserInfo = reference.userInfo; |
| 1343 targetHost = reference.host; |
| 1344 targetPort = _makePort(reference.hasPort ? reference.port : null, |
| 1345 targetScheme); |
| 1293 targetPath = _removeDotSegments(reference.path); | 1346 targetPath = _removeDotSegments(reference.path); |
| 1294 targetQuery = reference.query; | 1347 if (reference.hasQuery) targetQuery = reference.query; |
| 1295 } else { | 1348 } else { |
| 1296 if (reference.path == "") { | 1349 if (reference.path == "") { |
| 1297 targetPath = this.path; | 1350 targetPath = this._path; |
| 1298 if (reference.query != "") { | 1351 if (reference.hasQuery) { |
| 1299 targetQuery = reference.query; | 1352 targetQuery = reference.query; |
| 1300 } else { | 1353 } else { |
| 1301 targetQuery = this.query; | 1354 targetQuery = this._query; |
| 1302 } | 1355 } |
| 1303 } else { | 1356 } else { |
| 1304 if (reference.path.startsWith("/")) { | 1357 if (reference.path.startsWith("/")) { |
| 1305 targetPath = _removeDotSegments(reference.path); | 1358 targetPath = _removeDotSegments(reference.path); |
| 1306 } else { | 1359 } else { |
| 1307 targetPath = _removeDotSegments(_merge(this.path, reference.path)); | 1360 targetPath = _removeDotSegments(_merge(this._path, reference.path)); |
| 1308 } | 1361 } |
| 1309 targetQuery = reference.query; | 1362 if (reference.hasQuery) targetQuery = reference.query; |
| 1310 } | 1363 } |
| 1311 targetUserInfo = this.userInfo; | 1364 targetUserInfo = this._userInfo; |
| 1312 targetHost = this.host; | 1365 targetHost = this._host; |
| 1313 targetPort = this.port; | 1366 targetPort = this._port; |
| 1314 } | 1367 } |
| 1315 targetScheme = this.scheme; | |
| 1316 } | 1368 } |
| 1317 return new Uri(scheme: targetScheme, | 1369 String fragment = reference.hasFragment ? reference.fragment : null; |
| 1318 userInfo: targetUserInfo, | 1370 return new Uri._internal(targetScheme, |
| 1319 host: targetHost, | 1371 targetUserInfo, |
| 1320 port: targetPort, | 1372 targetHost, |
| 1321 path: targetPath, | 1373 targetPort, |
| 1322 query: targetQuery, | 1374 targetPath, |
| 1323 fragment: reference.fragment); | 1375 targetQuery, |
| 1376 fragment); |
| 1324 } | 1377 } |
| 1325 | 1378 |
| 1326 /** | 1379 /** |
| 1327 * Returns whether the URI has an [authority] component. | 1380 * Returns whether the URI has an [authority] component. |
| 1328 */ | 1381 */ |
| 1329 bool get hasAuthority => host != ""; | 1382 bool get hasAuthority => _host != null; |
| 1383 |
| 1384 /** |
| 1385 * Returns whether the URI has an explicit port. |
| 1386 * |
| 1387 * If the port number is the default port number |
| 1388 * (zero for unrecognized schemes, with http (80) and https (443) being |
| 1389 * recognized), |
| 1390 * then the port is made implicit and omitted from the URI. |
| 1391 */ |
| 1392 bool get hasPort => _port != null; |
| 1393 |
| 1394 /** |
| 1395 * Returns whether the URI has a query part. |
| 1396 */ |
| 1397 bool get hasQuery => _query != null; |
| 1398 |
| 1399 /** |
| 1400 * Returns whether the URI has a fragment part. |
| 1401 */ |
| 1402 bool get hasFragment => _fragment != null; |
| 1330 | 1403 |
| 1331 /** | 1404 /** |
| 1332 * Returns the origin of the URI in the form scheme://host:port for the | 1405 * Returns the origin of the URI in the form scheme://host:port for the |
| 1333 * schemes http and https. | 1406 * schemes http and https. |
| 1334 * | 1407 * |
| 1335 * It is an error if the scheme is not "http" or "https". | 1408 * It is an error if the scheme is not "http" or "https". |
| 1336 * | 1409 * |
| 1337 * See: http://www.w3.org/TR/2011/WD-html5-20110405/origin-0.html#origin | 1410 * See: http://www.w3.org/TR/2011/WD-html5-20110405/origin-0.html#origin |
| 1338 */ | 1411 */ |
| 1339 String get origin { | 1412 String get origin { |
| 1340 if (scheme == "" || _host == null || _host == "") { | 1413 if (scheme == "" || _host == null || _host == "") { |
| 1341 throw new StateError("Cannot use origin without a scheme: $this"); | 1414 throw new StateError("Cannot use origin without a scheme: $this"); |
| 1342 } | 1415 } |
| 1343 if (scheme != "http" && scheme != "https") { | 1416 if (scheme != "http" && scheme != "https") { |
| 1344 throw new StateError( | 1417 throw new StateError( |
| 1345 "Origin is only applicable schemes http and https: $this"); | 1418 "Origin is only applicable schemes http and https: $this"); |
| 1346 } | 1419 } |
| 1347 if (_port == 0) return "$scheme://$_host"; | 1420 if (_port == null) return "$scheme://$_host"; |
| 1348 return "$scheme://$_host:$_port"; | 1421 return "$scheme://$_host:$_port"; |
| 1349 } | 1422 } |
| 1350 | 1423 |
| 1351 /** | 1424 /** |
| 1352 * Returns the file path from a file URI. | 1425 * Returns the file path from a file URI. |
| 1353 * | 1426 * |
| 1354 * The returned path has either Windows or non-Windows | 1427 * The returned path has either Windows or non-Windows |
| 1355 * semantics. | 1428 * semantics. |
| 1356 * | 1429 * |
| 1357 * For non-Windows semantics the slash ("/") is used to separate | 1430 * For non-Windows semantics the slash ("/") is used to separate |
| (...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1464 if (hasDriveLetter && segments.length == 1) result.write("\\"); | 1537 if (hasDriveLetter && segments.length == 1) result.write("\\"); |
| 1465 return result.toString(); | 1538 return result.toString(); |
| 1466 } | 1539 } |
| 1467 | 1540 |
| 1468 bool get _isPathAbsolute { | 1541 bool get _isPathAbsolute { |
| 1469 if (path == null || path.isEmpty) return false; | 1542 if (path == null || path.isEmpty) return false; |
| 1470 return path.startsWith('/'); | 1543 return path.startsWith('/'); |
| 1471 } | 1544 } |
| 1472 | 1545 |
| 1473 void _writeAuthority(StringSink ss) { | 1546 void _writeAuthority(StringSink ss) { |
| 1474 _addIfNonEmpty(ss, userInfo, userInfo, "@"); | 1547 if (_userInfo.isNotEmpty) { |
| 1475 ss.write(_host == null ? "null" : _host); | 1548 ss.write(_userInfo); |
| 1476 if (_port != 0) { | 1549 ss.write("@"); |
| 1550 } |
| 1551 if (_host != null) ss.write(_host); |
| 1552 if (_port != null) { |
| 1477 ss.write(":"); | 1553 ss.write(":"); |
| 1478 ss.write(_port.toString()); | 1554 ss.write(_port); |
| 1479 } | 1555 } |
| 1480 } | 1556 } |
| 1481 | 1557 |
| 1482 String toString() { | 1558 String toString() { |
| 1483 StringBuffer sb = new StringBuffer(); | 1559 StringBuffer sb = new StringBuffer(); |
| 1484 _addIfNonEmpty(sb, scheme, scheme, ':'); | 1560 _addIfNonEmpty(sb, scheme, scheme, ':'); |
| 1485 if (hasAuthority || path.startsWith("//") || (scheme == "file")) { | 1561 if (hasAuthority || path.startsWith("//") || (scheme == "file")) { |
| 1486 // File URIS always have the authority, even if it is empty. | 1562 // File URIS always have the authority, even if it is empty. |
| 1487 // The empty URI means "localhost". | 1563 // The empty URI means "localhost". |
| 1488 sb.write("//"); | 1564 sb.write("//"); |
| 1489 _writeAuthority(sb); | 1565 _writeAuthority(sb); |
| 1490 } | 1566 } |
| 1491 sb.write(path); | 1567 sb.write(path); |
| 1492 _addIfNonEmpty(sb, query, "?", query); | 1568 if (_query != null) { sb..write("?")..write(_query); } |
| 1493 _addIfNonEmpty(sb, fragment, "#", fragment); | 1569 if (_fragment != null) { sb..write("#")..write(_fragment); } |
| 1494 return sb.toString(); | 1570 return sb.toString(); |
| 1495 } | 1571 } |
| 1496 | 1572 |
| 1497 bool operator==(other) { | 1573 bool operator==(other) { |
| 1498 if (other is! Uri) return false; | 1574 if (other is! Uri) return false; |
| 1499 Uri uri = other; | 1575 Uri uri = other; |
| 1500 return scheme == uri.scheme && | 1576 return scheme == uri.scheme && |
| 1577 hasAuthority == uri.hasAuthority && |
| 1501 userInfo == uri.userInfo && | 1578 userInfo == uri.userInfo && |
| 1502 host == uri.host && | 1579 host == uri.host && |
| 1503 port == uri.port && | 1580 port == uri.port && |
| 1504 path == uri.path && | 1581 path == uri.path && |
| 1582 hasQuery == uri.hasQuery && |
| 1505 query == uri.query && | 1583 query == uri.query && |
| 1584 hasFragment == uri.hasFragment && |
| 1506 fragment == uri.fragment; | 1585 fragment == uri.fragment; |
| 1507 } | 1586 } |
| 1508 | 1587 |
| 1509 int get hashCode { | 1588 int get hashCode { |
| 1510 int combine(part, current) { | 1589 int combine(part, current) { |
| 1511 // The sum is truncated to 30 bits to make sure it fits into a Smi. | 1590 // The sum is truncated to 30 bits to make sure it fits into a Smi. |
| 1512 return (current * 31 + part.hashCode) & 0x3FFFFFFF; | 1591 return (current * 31 + part.hashCode) & 0x3FFFFFFF; |
| 1513 } | 1592 } |
| 1514 return combine(scheme, combine(userInfo, combine(host, combine(port, | 1593 return combine(scheme, combine(userInfo, combine(host, combine(port, |
| 1515 combine(path, combine(query, combine(fragment, 1))))))); | 1594 combine(path, combine(query, combine(fragment, 1))))))); |
| (...skipping 679 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2195 0xafff, // 0x30 - 0x3f 1111111111110101 | 2274 0xafff, // 0x30 - 0x3f 1111111111110101 |
| 2196 // @ABCDEFGHIJKLMNO | 2275 // @ABCDEFGHIJKLMNO |
| 2197 0xffff, // 0x40 - 0x4f 1111111111111111 | 2276 0xffff, // 0x40 - 0x4f 1111111111111111 |
| 2198 // PQRSTUVWXYZ _ | 2277 // PQRSTUVWXYZ _ |
| 2199 0x87ff, // 0x50 - 0x5f 1111111111100001 | 2278 0x87ff, // 0x50 - 0x5f 1111111111100001 |
| 2200 // abcdefghijklmno | 2279 // abcdefghijklmno |
| 2201 0xfffe, // 0x60 - 0x6f 0111111111111111 | 2280 0xfffe, // 0x60 - 0x6f 0111111111111111 |
| 2202 // pqrstuvwxyz ~ | 2281 // pqrstuvwxyz ~ |
| 2203 0x47ff]; // 0x70 - 0x7f 1111111111100010 | 2282 0x47ff]; // 0x70 - 0x7f 1111111111100010 |
| 2204 } | 2283 } |
| OLD | NEW |