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