Chromium Code Reviews| 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:** |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 66 | 66 |
| 67 // The fragment content, or null if there is no fragment. | 67 // The fragment content, or null if there is no fragment. |
| 68 final String _fragment; | 68 final String _fragment; |
| 69 | 69 |
| 70 /** | 70 /** |
| 71 * Cache the computed return value of [pathSegements]. | 71 * Cache the computed return value of [pathSegements]. |
| 72 */ | 72 */ |
| 73 List<String> _pathSegments; | 73 List<String> _pathSegments; |
| 74 | 74 |
| 75 /** | 75 /** |
| 76 * Cache of the full normalized text representation of the URI. | |
| 77 */ | |
| 78 String _text; | |
| 79 | |
| 80 /** | |
| 76 * Cache the computed return value of [queryParameters]. | 81 * Cache the computed return value of [queryParameters]. |
| 77 */ | 82 */ |
| 78 Map<String, String> _queryParameters; | 83 Map<String, String> _queryParameters; |
| 79 Map<String, List<String>> _queryParameterLists; | 84 Map<String, List<String>> _queryParameterLists; |
| 80 | 85 |
| 81 /// Internal non-verifying constructor. Only call with validated arguments. | 86 /// Internal non-verifying constructor. Only call with validated arguments. |
| 82 Uri._internal(this.scheme, | 87 Uri._internal(this.scheme, |
| 83 this._userInfo, | 88 this._userInfo, |
| 84 this._host, | 89 this._host, |
| 85 this._port, | 90 this._port, |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 151 * use an empty map for `queryParameters`. | 156 * use an empty map for `queryParameters`. |
| 152 * | 157 * |
| 153 * If both `query` and `queryParameters` are omitted or `null`, | 158 * If both `query` and `queryParameters` are omitted or `null`, |
| 154 * the URI has no query part. | 159 * the URI has no query part. |
| 155 * | 160 * |
| 156 * The fragment component is set through [fragment]. | 161 * The fragment component is set through [fragment]. |
| 157 * It should be a valid URI fragment, but invalid characters other than | 162 * It should be a valid URI fragment, but invalid characters other than |
| 158 * general delimiters, are escaped if necessary. | 163 * general delimiters, are escaped if necessary. |
| 159 * If `fragment` is omitted or `null`, the URI has no fragment part. | 164 * If `fragment` is omitted or `null`, the URI has no fragment part. |
| 160 */ | 165 */ |
| 161 factory Uri({String scheme : "", | 166 factory Uri({String scheme, |
| 162 String userInfo : "", | 167 String userInfo, |
| 163 String host, | 168 String host, |
| 164 int port, | 169 int port, |
| 165 String path, | 170 String path, |
| 166 Iterable<String> pathSegments, | 171 Iterable<String> pathSegments, |
| 167 String query, | 172 String query, |
| 168 Map<String, dynamic/*String|Iterable<String>*/> queryParameters, | 173 Map<String, dynamic/*String|Iterable<String>*/> queryParameters, |
| 169 String fragment}) { | 174 String fragment}) { |
| 170 scheme = _makeScheme(scheme, 0, _stringOrNullLength(scheme)); | 175 scheme = _makeScheme(scheme, 0, _stringOrNullLength(scheme)); |
| 171 userInfo = _makeUserInfo(userInfo, 0, _stringOrNullLength(userInfo)); | 176 userInfo = _makeUserInfo(userInfo, 0, _stringOrNullLength(userInfo)); |
| 172 host = _makeHost(host, 0, _stringOrNullLength(host), false); | 177 host = _makeHost(host, 0, _stringOrNullLength(host), false); |
| (...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 387 // segment = *pchar | 392 // segment = *pchar |
| 388 // segment-nz = 1*pchar | 393 // segment-nz = 1*pchar |
| 389 // segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" ) | 394 // segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" ) |
| 390 // ; non-zero-length segment without any colon ":" | 395 // ; non-zero-length segment without any colon ":" |
| 391 // | 396 // |
| 392 // pchar = unreserved / pct-encoded / sub-delims / ":" / "@" | 397 // pchar = unreserved / pct-encoded / sub-delims / ":" / "@" |
| 393 // | 398 // |
| 394 // query = *( pchar / "/" / "?" ) | 399 // query = *( pchar / "/" / "?" ) |
| 395 // | 400 // |
| 396 // fragment = *( pchar / "/" / "?" ) | 401 // fragment = *( pchar / "/" / "?" ) |
| 397 const int EOI = -1; | 402 end ??= uri.length; |
| 398 | 403 |
| 399 String scheme = ""; | 404 // Special case data:URIs. Ignore case when testing. |
| 400 String userinfo = ""; | 405 if (end >= start + 5 && |
| 401 String host = null; | 406 uri.codeUnitAt(start + 4) == _COLON && |
| 402 int port = null; | 407 uri.codeUnitAt(start) | 0x20 == 0x64 /*d*/ && |
| 403 String path = null; | 408 uri.codeUnitAt(start + 1) | 0x20 == 0x61 /*a*/ && |
| 404 String query = null; | 409 uri.codeUnitAt(start + 2) | 0x20 == 0x74 /*t*/ && |
| 405 String fragment = null; | 410 uri.codeUnitAt(start + 3) | 0x20 == 0x61 /*a*/) { |
| 406 if (end == null) end = uri.length; | 411 // Data-URI. |
| 407 | 412 if (uri.startsWith("data", start)) { |
| 408 int index = start; | 413 // The case is right. |
| 409 int pathStart = start; | 414 if (start > 0 || end < uri.length) uri = uri.substring(start, end); |
| 410 // End of input-marker. | 415 return UriData._parse(uri, 5, null).uri; |
| 411 int char = EOI; | |
| 412 | |
| 413 void parseAuth() { | |
| 414 if (index == end) { | |
| 415 char = EOI; | |
| 416 return; | |
| 417 } | 416 } |
| 418 int authStart = index; | 417 return Uriata._parse(uri.substring(start + 5, end), 0, null).uri; |
|
floitsch
2016/06/28 00:28:03
UriData
I'm guessing you didn't run the tests?
Lasse Reichstein Nielsen
2016/06/28 13:00:51
I did. Turns out we never tested a data URI starti
| |
| 419 int lastColon = -1; | |
| 420 int lastAt = -1; | |
| 421 char = uri.codeUnitAt(index); | |
| 422 while (index < end) { | |
| 423 char = uri.codeUnitAt(index); | |
| 424 if (char == _SLASH || char == _QUESTION || char == _NUMBER_SIGN) { | |
| 425 break; | |
| 426 } | |
| 427 if (char == _AT_SIGN) { | |
| 428 lastAt = index; | |
| 429 lastColon = -1; | |
| 430 } else if (char == _COLON) { | |
| 431 lastColon = index; | |
| 432 } else if (char == _LEFT_BRACKET) { | |
| 433 lastColon = -1; | |
| 434 int endBracket = uri.indexOf(']', index + 1); | |
| 435 if (endBracket == -1) { | |
| 436 index = end; | |
| 437 char = EOI; | |
| 438 break; | |
| 439 } else { | |
| 440 index = endBracket; | |
| 441 } | |
| 442 } | |
| 443 index++; | |
| 444 char = EOI; | |
| 445 } | |
| 446 int hostStart = authStart; | |
| 447 int hostEnd = index; | |
| 448 if (lastAt >= 0) { | |
| 449 userinfo = _makeUserInfo(uri, authStart, lastAt); | |
| 450 hostStart = lastAt + 1; | |
| 451 } | |
| 452 if (lastColon >= 0) { | |
| 453 int portNumber; | |
| 454 if (lastColon + 1 < index) { | |
| 455 portNumber = 0; | |
| 456 for (int i = lastColon + 1; i < index; i++) { | |
| 457 int digit = uri.codeUnitAt(i); | |
| 458 if (_ZERO > digit || _NINE < digit) { | |
| 459 _fail(uri, i, "Invalid port number"); | |
| 460 } | |
| 461 portNumber = portNumber * 10 + (digit - _ZERO); | |
| 462 } | |
| 463 } | |
| 464 port = _makePort(portNumber, scheme); | |
| 465 hostEnd = lastColon; | |
| 466 } | |
| 467 host = _makeHost(uri, hostStart, hostEnd, true); | |
| 468 if (index < end) { | |
| 469 char = uri.codeUnitAt(index); | |
| 470 } | |
| 471 } | 418 } |
| 472 | 419 |
| 473 // When reaching path parsing, the current character is known to not | 420 var indices = _scanUri(uri, start, end); |
|
floitsch
2016/06/28 00:28:03
Provide example of what the indices could look lik
Lasse Reichstein Nielsen
2016/06/28 13:00:52
Done.
| |
| 474 // be part of the path. | |
| 475 const int NOT_IN_PATH = 0; | |
| 476 // When reaching path parsing, the current character is part | |
| 477 // of the a non-empty path. | |
| 478 const int IN_PATH = 1; | |
| 479 // When reaching authority parsing, authority is possible. | |
| 480 // This is only true at start or right after scheme. | |
| 481 const int ALLOW_AUTH = 2; | |
| 482 | 421 |
| 483 // Current state. | 422 int schemeEnd = indices[_schemeEndIndex]; // >0 if has scheme |
|
floitsch
2016/06/28 00:28:04
Finish with ".".
Lasse Reichstein Nielsen
2016/06/28 13:00:52
Just removed, it's documented in the _scanUri docs
| |
| 484 // Initialized to the default value that is used when exiting the | 423 int hostStart = indices[_hostStartIndex] + 1; // >0 if has authority. |
| 485 // scheme loop by reaching the end of input. | 424 int portStart = indices[_portStartIndex]; |
| 486 // All other breaks set their own state. | 425 int pathStart = indices[_pathStartIndex]; |
| 487 int state = NOT_IN_PATH; | 426 int queryStart = indices[_queryStartIndex]; |
| 488 int i = index; // Temporary alias for index to avoid bug 19550 in dart2js. | 427 int fragmentStart = indices[_fragmentStartIndex]; |
| 489 while (i < end) { | 428 |
| 490 char = uri.codeUnitAt(i); | 429 // We may discover scheme while handling special cases. |
| 491 if (char == _QUESTION || char == _NUMBER_SIGN) { | 430 String scheme; |
| 492 state = NOT_IN_PATH; | 431 |
| 493 break; | 432 // Derive some indices that weren't set. |
| 433 // If fragment but no query, set query to start at fragment. | |
| 434 if (fragmentStart < queryStart) queryStart = fragmentStart; | |
|
floitsch
2016/06/28 00:28:03
It's not clear why this wouldn't be done by the _s
Lasse Reichstein Nielsen
2016/06/28 13:00:52
It absolutely could. More to the point, the _scanU
| |
| 435 // If scheme but no authority, the pathStart isn't set. | |
| 436 if (schemeEnd >= start && hostStart <= start) pathStart = schemeEnd + 1; | |
| 437 // If scheme or authority but pathStart isn't set. | |
| 438 if (pathStart == start && (schemeEnd >= start || hostStart > start)) { | |
| 439 pathStart = queryStart; | |
| 440 } | |
| 441 // If authority and no port. | |
| 442 // (including when user-info contains : and portStart >= 0). | |
| 443 if (portStart < hostStart) portStart = pathStart; | |
| 444 | |
| 445 bool isSimple = indices[_notSimpleIndex] < start; | |
| 446 if (isSimple) { | |
| 447 // Check/do normalizations that weren't detected by the scanner. | |
| 448 // This includes removal of empty port or userInfo, | |
| 449 // or scheme specific port and path normalizations. | |
| 450 if (hostStart > schemeEnd + 3) { | |
| 451 // Always be non-simple if URI contains user-info. | |
| 452 isSimple = false; | |
| 494 } | 453 } |
|
floitsch
2016/06/28 00:28:03
Is it necessary to enter the 'else' below?
if not
Lasse Reichstein Nielsen
2016/06/28 13:00:52
Done.
| |
| 495 if (char == _SLASH) { | 454 if (portStart > start && portStart + 1 == pathStart) { |
| 496 state = (i == start) ? ALLOW_AUTH : IN_PATH; | 455 // If the port is empty, it should be omitted. |
| 497 break; | 456 // Path case, don't bother correcting it. |
| 498 } | 457 isSimple = false; |
| 499 if (char == _COLON) { | 458 } else if (hostStart == schemeEnd + 4) { |
| 500 if (i == start) _fail(uri, start, "Invalid empty scheme"); | 459 // If the userInfo is empty, it should be omitted. |
| 501 scheme = _makeScheme(uri, start, i); | 460 // (4 is length of "://@"). |
| 502 i++; | 461 // Pathological case, don't bother correcting it. |
| 503 if (scheme == "data") { | 462 isSimple = false; |
| 504 // This generates a URI that is (potentially) not path normalized. | 463 } else { |
| 505 // Applying part normalization to a non-hierarchial URI isn't | 464 // There are a few scheme-based normalizations that |
| 506 // meaningful. | 465 // the scanner couldn't check. |
| 507 return UriData._parse(uri, i, null).uri; | 466 // That means that the input is very close to simple, so just do |
| 508 } | 467 // the normalizations. |
| 509 pathStart = i; | 468 if (schemeEnd == start + 4) { |
| 510 if (i == end) { | 469 // Do scheme based normalizations for file, http. |
| 511 char = EOI; | 470 if (uri.startsWith("file", start)) { |
| 512 state = NOT_IN_PATH; | 471 scheme = "file"; |
| 513 } else { | 472 if (hostStart <= 0) { |
| 514 char = uri.codeUnitAt(i); | 473 // File URIs should have an authority. |
| 515 if (char == _QUESTION || char == _NUMBER_SIGN) { | 474 // Paths after an authority should be absolute. |
| 516 state = NOT_IN_PATH; | 475 String insert = "//"; |
| 517 } else if (char == _SLASH) { | 476 int delta = 2; |
| 518 state = ALLOW_AUTH; | 477 if (!uri.startsWith("/", pathStart)) { |
| 519 } else { | 478 insert = "///"; |
| 520 state = IN_PATH; | 479 delta = 3; |
| 480 } | |
| 481 uri = "${uri.substring(start, schemeEnd + 1)}$insert" | |
| 482 "${uri.substring(pathStart, end)}"; | |
| 483 schemeEnd -= start; | |
| 484 pathStart += 2 - start; | |
| 485 hostStart = schemeEnd + 3; | |
| 486 portStart = pathStart; | |
| 487 queryStart += delta; | |
| 488 fragmentStart += delta; | |
| 489 start = 0; | |
| 490 end = uri.length; | |
| 491 } else if (pathStart == queryStart) { | |
| 492 uri = "${uri.substring(start, pathStart)}/" | |
| 493 "${uri.substring(pathStart, end)}"; | |
| 494 queryStart += 1; | |
| 495 fragmentStart += 1; | |
| 496 } | |
| 497 } else if (uri.startsWith("http", start)) { | |
| 498 scheme = "http"; | |
| 499 // Http URIs should not have an explicit port of 80 | |
|
floitsch
2016/06/28 00:28:03
Finish with ".".
Lasse Reichstein Nielsen
2016/06/28 13:00:51
Done.
| |
| 500 if (portStart > start && portStart + 3 == pathStart && | |
| 501 uri.startsWith("80", portStart + 1)) { | |
| 502 uri = uri.substring(start, portStart) + | |
| 503 uri.substring(pathStart, end); | |
| 504 schemeEnd -= start; | |
| 505 hostStart -= start; | |
| 506 portStart -= start; | |
| 507 pathStart -= 3 + start; | |
| 508 queryStart -= 3 + start; | |
| 509 fragmentStart -= 3 + start; | |
| 510 start = 0; | |
| 511 end = uri.length; | |
| 512 } | |
| 521 } | 513 } |
| 522 } | 514 } else if (schemeEnd == start + 5 && uri.startsWith("https", start)) { |
| 523 break; | 515 scheme = "https"; |
| 524 } | 516 // Https URIs should not have an explicit port of 443 |
| 525 i++; | 517 if (portStart > start && portStart + 4 == pathStart && |
| 526 char = EOI; | 518 uri.startsWith("443", portStart + 1)) { |
| 527 } | 519 uri = uri.substring(start, portStart) + |
| 528 index = i; // Remove alias when bug is fixed. | 520 uri.substring(pathStart, end); |
| 529 | 521 schemeEnd -= start; |
| 530 if (state == ALLOW_AUTH) { | 522 hostStart -= start; |
| 531 assert(char == _SLASH); | 523 portStart -= start; |
| 532 // Have seen one slash either at start or right after scheme. | 524 pathStart -= 4 + start; |
| 533 // If two slashes, it's an authority, otherwise it's just the path. | 525 queryStart -= 4 + start; |
| 534 index++; | 526 fragmentStart -= 4 + start; |
| 535 if (index == end) { | 527 start = 0; |
| 536 char = EOI; | 528 end = uri.length; |
| 537 state = NOT_IN_PATH; | 529 } |
| 538 } else { | |
| 539 char = uri.codeUnitAt(index); | |
| 540 if (char == _SLASH) { | |
| 541 index++; | |
| 542 parseAuth(); | |
| 543 pathStart = index; | |
| 544 } | |
| 545 if (char == _QUESTION || char == _NUMBER_SIGN || char == EOI) { | |
| 546 state = NOT_IN_PATH; | |
| 547 } else { | |
| 548 state = IN_PATH; | |
| 549 } | 530 } |
| 550 } | 531 } |
| 551 } | 532 } |
| 552 | 533 |
| 553 assert(state == IN_PATH || state == NOT_IN_PATH); | 534 if (isSimple) { |
| 554 if (state == IN_PATH) { | 535 if (start > 0 || end < uri.length) { |
| 555 // Characters from pathStart to index (inclusive) are known | 536 uri = uri.substring(start, end); |
| 556 // to be part of the path. | 537 if (schemeEnd >= 0) schemeEnd -= start; |
| 557 while (++index < end) { | 538 if (hostStart > 0) { |
| 558 char = uri.codeUnitAt(index); | 539 hostStart -= start; |
| 559 if (char == _QUESTION || char == _NUMBER_SIGN) { | 540 portStart -= start; |
| 560 break; | |
| 561 } | 541 } |
| 562 char = EOI; | 542 pathStart -= start; |
| 543 queryStart -= start; | |
| 544 fragmentStart -= start; | |
| 563 } | 545 } |
| 564 state = NOT_IN_PATH; | 546 return new _SimpleUri(uri, schemeEnd, hostStart, portStart, pathStart, |
| 547 queryStart, fragmentStart, scheme); | |
| 548 | |
| 565 } | 549 } |
| 566 | 550 |
| 567 assert(state == NOT_IN_PATH); | 551 if (scheme == null) { |
| 568 bool hasAuthority = (host != null); | 552 scheme = ""; |
| 569 path = _makePath(uri, pathStart, index, null, scheme, hasAuthority); | 553 if (schemeEnd > start) { |
| 570 | 554 scheme = _makeScheme(uri, start, schemeEnd); |
| 571 if (char == _QUESTION) { | 555 } else if (schemeEnd == start) { |
| 572 int numberSignIndex = -1; | 556 _fail(uri, start, "Invalid empty scheme"); |
| 573 for (int i = index + 1; i < end; i++) { | |
| 574 if (uri.codeUnitAt(i) == _NUMBER_SIGN) { | |
| 575 numberSignIndex = i; | |
| 576 break; | |
| 577 } | |
| 578 } | 557 } |
| 579 if (numberSignIndex < 0) { | 558 } |
| 580 query = _makeQuery(uri, index + 1, end, null); | 559 String userInfo = ""; |
| 581 } else { | 560 String host; |
| 582 query = _makeQuery(uri, index + 1, numberSignIndex, null); | 561 int port; |
| 583 fragment = _makeFragment(uri, numberSignIndex + 1, end); | 562 if (hostStart > start) { |
| 563 int userInfoStart = schemeEnd + 3; | |
| 564 if (userInfoStart < hostStart) { | |
| 565 userInfo = _makeUserInfo(uri, userInfoStart, hostStart - 1); | |
| 584 } | 566 } |
| 585 } else if (char == _NUMBER_SIGN) { | 567 host = _makeHost(uri, hostStart, portStart, false); |
| 586 fragment = _makeFragment(uri, index + 1, end); | 568 if (portStart + 1 < pathStart) { |
| 569 // Should throw because invalid. | |
| 570 port = int.parse(uri.substring(portStart + 1, pathStart), onError: (_) { | |
| 571 throw new FormatException("Invalid port", uri, portStart + 1); | |
| 572 }); | |
| 573 port = _makePort(port, scheme); | |
| 574 } | |
| 575 } | |
| 576 String path = _makePath(uri, pathStart, queryStart, null, | |
| 577 scheme, host != null); | |
| 578 String query; | |
| 579 if (queryStart < fragmentStart) { | |
| 580 query = _makeQuery(uri, queryStart + 1, fragmentStart, null); | |
| 581 } | |
| 582 String fragment; | |
| 583 if (fragmentStart < end) { | |
| 584 fragment = _makeFragment(uri, fragmentStart + 1, end); | |
| 587 } | 585 } |
| 588 return new Uri._internal(scheme, | 586 return new Uri._internal(scheme, |
| 589 userinfo, | 587 userInfo, |
| 590 host, | 588 host, |
| 591 port, | 589 port, |
| 592 path, | 590 path, |
| 593 query, | 591 query, |
| 594 fragment); | 592 fragment); |
| 595 } | 593 } |
| 596 | 594 |
| 597 // Report a parse failure. | 595 // Report a parse failure. |
| 598 static void _fail(String uri, int index, String message) { | 596 static void _fail(String uri, int index, String message) { |
| 599 throw new FormatException(message, uri, index); | 597 throw new FormatException(message, uri, index); |
| (...skipping 726 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1326 final int codeUnit = scheme.codeUnitAt(i); | 1324 final int codeUnit = scheme.codeUnitAt(i); |
| 1327 if (!_isSchemeCharacter(codeUnit)) { | 1325 if (!_isSchemeCharacter(codeUnit)) { |
| 1328 _fail(scheme, i, "Illegal scheme character"); | 1326 _fail(scheme, i, "Illegal scheme character"); |
| 1329 } | 1327 } |
| 1330 if (_UPPER_CASE_A <= codeUnit && codeUnit <= _UPPER_CASE_Z) { | 1328 if (_UPPER_CASE_A <= codeUnit && codeUnit <= _UPPER_CASE_Z) { |
| 1331 containsUpperCase = true; | 1329 containsUpperCase = true; |
| 1332 } | 1330 } |
| 1333 } | 1331 } |
| 1334 scheme = scheme.substring(start, end); | 1332 scheme = scheme.substring(start, end); |
| 1335 if (containsUpperCase) scheme = scheme.toLowerCase(); | 1333 if (containsUpperCase) scheme = scheme.toLowerCase(); |
| 1334 return _canonicalizeScheme(scheme); | |
| 1335 } | |
| 1336 | |
| 1337 // Canonicalize a few often-used scheme strings. | |
| 1338 // | |
| 1339 // This improves memory usage and makes comparison faster. | |
| 1340 static String _canonicalizeScheme(String scheme) { | |
| 1341 if (scheme == "http") return "http"; | |
| 1342 if (scheme == "file") return "file"; | |
| 1343 if (scheme == "https") return "https"; | |
| 1344 if (scheme == "package") return "package"; | |
| 1336 return scheme; | 1345 return scheme; |
| 1337 } | 1346 } |
| 1338 | 1347 |
| 1339 static String _makeUserInfo(String userInfo, int start, int end) { | 1348 static String _makeUserInfo(String userInfo, int start, int end) { |
| 1340 if (userInfo == null) return ""; | 1349 if (userInfo == null) return ""; |
| 1341 return _normalize(userInfo, start, end, _userinfoTable); | 1350 return _normalize(userInfo, start, end, _userinfoTable); |
| 1342 } | 1351 } |
| 1343 | 1352 |
| 1344 static String _makePath(String path, int start, int end, | 1353 static String _makePath(String path, int start, int end, |
| 1345 Iterable<String> pathSegments, | 1354 Iterable<String> pathSegments, |
| (...skipping 579 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1925 * Uri.parse("file:C:xxx/yyy"); // Throws as a path segment | 1934 * Uri.parse("file:C:xxx/yyy"); // Throws as a path segment |
| 1926 * // cannot contain colon on Windows. | 1935 * // cannot contain colon on Windows. |
| 1927 * Uri.parse("file://server/share/file"); // \\server\share\file | 1936 * Uri.parse("file://server/share/file"); // \\server\share\file |
| 1928 * | 1937 * |
| 1929 * If the URI is not a file URI calling this throws | 1938 * If the URI is not a file URI calling this throws |
| 1930 * [UnsupportedError]. | 1939 * [UnsupportedError]. |
| 1931 * | 1940 * |
| 1932 * If the URI cannot be converted to a file path calling this throws | 1941 * If the URI cannot be converted to a file path calling this throws |
| 1933 * [UnsupportedError]. | 1942 * [UnsupportedError]. |
| 1934 */ | 1943 */ |
| 1944 // TODO(lrn): Deprecate and move functionality to File class or similar. | |
| 1945 // The core libraries should not worry about the platform. | |
| 1935 String toFilePath({bool windows}) { | 1946 String toFilePath({bool windows}) { |
| 1936 if (scheme != "" && scheme != "file") { | 1947 if (scheme != "" && scheme != "file") { |
| 1937 throw new UnsupportedError( | 1948 throw new UnsupportedError( |
| 1938 "Cannot extract a file path from a $scheme URI"); | 1949 "Cannot extract a file path from a $scheme URI"); |
| 1939 } | 1950 } |
| 1940 if (query != "") { | 1951 if (query != "") { |
| 1941 throw new UnsupportedError( | 1952 throw new UnsupportedError( |
| 1942 "Cannot extract a file path from a URI with a query component"); | 1953 "Cannot extract a file path from a URI with a query component"); |
| 1943 } | 1954 } |
| 1944 if (fragment != "") { | 1955 if (fragment != "") { |
| 1945 throw new UnsupportedError( | 1956 throw new UnsupportedError( |
| 1946 "Cannot extract a file path from a URI with a fragment component"); | 1957 "Cannot extract a file path from a URI with a fragment component"); |
| 1947 } | 1958 } |
| 1948 if (windows == null) windows = _isWindows; | 1959 if (windows == null) windows = _isWindows; |
| 1949 return windows ? _toWindowsFilePath() : _toFilePath(); | 1960 return windows ? _toWindowsFilePath(this) : _toFilePath(); |
| 1950 } | 1961 } |
| 1951 | 1962 |
| 1952 String _toFilePath() { | 1963 String _toFilePath() { |
| 1953 if (host != "") { | 1964 if (hasAuthority && host != "") { |
| 1954 throw new UnsupportedError( | 1965 throw new UnsupportedError( |
| 1955 "Cannot extract a non-Windows file path from a file URI " | 1966 "Cannot extract a non-Windows file path from a file URI " |
| 1956 "with an authority"); | 1967 "with an authority"); |
| 1957 } | 1968 } |
| 1969 // Use path segments to have any escapes unescaped. | |
| 1970 var pathSegments = this.pathSegments; | |
| 1958 _checkNonWindowsPathReservedCharacters(pathSegments, false); | 1971 _checkNonWindowsPathReservedCharacters(pathSegments, false); |
| 1959 var result = new StringBuffer(); | 1972 var result = new StringBuffer(); |
| 1960 if (_isPathAbsolute) result.write("/"); | 1973 if (hasAbsolutePath) result.write("/"); |
| 1961 result.writeAll(pathSegments, "/"); | 1974 result.writeAll(pathSegments, "/"); |
| 1962 return result.toString(); | 1975 return result.toString(); |
| 1963 } | 1976 } |
| 1964 | 1977 |
| 1965 String _toWindowsFilePath() { | 1978 static String _toWindowsFilePath(Uri uri) { |
| 1966 bool hasDriveLetter = false; | 1979 bool hasDriveLetter = false; |
| 1967 var segments = pathSegments; | 1980 var segments = uri.pathSegments; |
| 1968 if (segments.length > 0 && | 1981 if (segments.length > 0 && |
| 1969 segments[0].length == 2 && | 1982 segments[0].length == 2 && |
| 1970 segments[0].codeUnitAt(1) == _COLON) { | 1983 segments[0].codeUnitAt(1) == _COLON) { |
| 1971 _checkWindowsDriveLetter(segments[0].codeUnitAt(0), false); | 1984 _checkWindowsDriveLetter(segments[0].codeUnitAt(0), false); |
| 1972 _checkWindowsPathReservedCharacters(segments, false, 1); | 1985 _checkWindowsPathReservedCharacters(segments, false, 1); |
| 1973 hasDriveLetter = true; | 1986 hasDriveLetter = true; |
| 1974 } else { | 1987 } else { |
| 1975 _checkWindowsPathReservedCharacters(segments, false); | 1988 _checkWindowsPathReservedCharacters(segments, false, 0); |
| 1976 } | 1989 } |
| 1977 var result = new StringBuffer(); | 1990 var result = new StringBuffer(); |
| 1978 if (_isPathAbsolute && !hasDriveLetter) result.write("\\"); | 1991 if (uri.hasAbsolutePath && !hasDriveLetter) result.write(r"\"); |
| 1979 if (host != "") { | 1992 if (uri.hasAuthority) { |
| 1980 result.write("\\"); | 1993 var host = uri.host; |
| 1981 result.write(host); | 1994 if (host.isNotEmpty) { |
| 1982 result.write("\\"); | 1995 result.write(r"\"); |
| 1996 result.write(host); | |
| 1997 result.write(r"\"); | |
| 1998 } | |
| 1983 } | 1999 } |
| 1984 result.writeAll(segments, "\\"); | 2000 result.writeAll(segments, r"\"); |
| 1985 if (hasDriveLetter && segments.length == 1) result.write("\\"); | 2001 if (hasDriveLetter && segments.length == 1) result.write(r"\"); |
| 1986 return result.toString(); | 2002 return result.toString(); |
| 1987 } | 2003 } |
| 1988 | 2004 |
| 1989 bool get _isPathAbsolute { | 2005 bool get _isPathAbsolute { |
| 1990 if (path == null || path.isEmpty) return false; | 2006 return _path != null && _path.startsWith('/'); |
| 1991 return path.startsWith('/'); | |
| 1992 } | 2007 } |
| 1993 | 2008 |
| 1994 void _writeAuthority(StringSink ss) { | 2009 void _writeAuthority(StringSink ss) { |
| 1995 if (_userInfo.isNotEmpty) { | 2010 if (_userInfo.isNotEmpty) { |
| 1996 ss.write(_userInfo); | 2011 ss.write(_userInfo); |
| 1997 ss.write("@"); | 2012 ss.write("@"); |
| 1998 } | 2013 } |
| 1999 if (_host != null) ss.write(_host); | 2014 if (_host != null) ss.write(_host); |
| 2000 if (_port != null) { | 2015 if (_port != null) { |
| 2001 ss.write(":"); | 2016 ss.write(":"); |
| 2002 ss.write(_port); | 2017 ss.write(_port); |
| 2003 } | 2018 } |
| 2004 } | 2019 } |
| 2005 | 2020 |
| 2006 /** | 2021 /** |
| 2007 * Access the structure of a `data:` URI. | 2022 * Access the structure of a `data:` URI. |
| 2008 * | 2023 * |
| 2009 * Returns a [UriData] object for `data:` URIs and `null` for all other | 2024 * Returns a [UriData] object for `data:` URIs and `null` for all other |
| 2010 * URIs. | 2025 * URIs. |
| 2011 * The [UriData] object can be used to access the media type and data | 2026 * The [UriData] object can be used to access the media type and data |
| 2012 * of a `data:` URI. | 2027 * of a `data:` URI. |
| 2013 */ | 2028 */ |
| 2014 UriData get data => (scheme == "data") ? new UriData.fromUri(this) : null; | 2029 UriData get data => (scheme == "data") ? new UriData.fromUri(this) : null; |
| 2015 | 2030 |
| 2016 String toString() { | 2031 String toString() { |
| 2032 if (_text != null) return _text; | |
| 2017 StringBuffer sb = new StringBuffer(); | 2033 StringBuffer sb = new StringBuffer(); |
| 2018 _addIfNonEmpty(sb, scheme, scheme, ':'); | 2034 if (scheme.isNotEmpty) sb..write(scheme)..write(":"); |
| 2019 if (hasAuthority || path.startsWith("//") || (scheme == "file")) { | 2035 if (hasAuthority || path.startsWith("//") || (scheme == "file")) { |
| 2020 // File URIS always have the authority, even if it is empty. | 2036 // File URIS always have the authority, even if it is empty. |
| 2021 // The empty URI means "localhost". | 2037 // The empty URI means "localhost". |
| 2022 sb.write("//"); | 2038 sb.write("//"); |
| 2023 _writeAuthority(sb); | 2039 _writeAuthority(sb); |
| 2024 } | 2040 } |
| 2025 sb.write(path); | 2041 sb.write(path); |
| 2026 if (_query != null) { sb..write("?")..write(_query); } | 2042 if (_query != null) sb..write("?")..write(_query); |
| 2027 if (_fragment != null) { sb..write("#")..write(_fragment); } | 2043 if (_fragment != null) sb..write("#")..write(_fragment); |
| 2028 return sb.toString(); | 2044 _text = sb.toString(); |
| 2045 return _text; | |
| 2029 } | 2046 } |
| 2030 | 2047 |
| 2031 bool operator==(other) { | 2048 bool operator==(other) { |
| 2032 if (other is! Uri) return false; | 2049 if (other is! Uri) return false; |
| 2033 Uri uri = other; | 2050 Uri uri = other; |
| 2034 return scheme == uri.scheme && | 2051 return scheme == uri.scheme && |
| 2035 hasAuthority == uri.hasAuthority && | 2052 hasAuthority == uri.hasAuthority && |
| 2036 userInfo == uri.userInfo && | 2053 userInfo == uri.userInfo && |
| 2037 host == uri.host && | 2054 host == uri.host && |
| 2038 port == uri.port && | 2055 port == uri.port && |
| 2039 path == uri.path && | 2056 path == uri.path && |
| 2040 hasQuery == uri.hasQuery && | 2057 hasQuery == uri.hasQuery && |
| 2041 query == uri.query && | 2058 query == uri.query && |
| 2042 hasFragment == uri.hasFragment && | 2059 hasFragment == uri.hasFragment && |
| 2043 fragment == uri.fragment; | 2060 fragment == uri.fragment; |
| 2044 } | 2061 } |
| 2045 | 2062 |
| 2046 int get hashCode { | 2063 int get hashCode { |
| 2047 int combine(part, current) { | 2064 return (_text ?? toString()).hashCode; |
| 2048 // The sum is truncated to 30 bits to make sure it fits into a Smi. | |
| 2049 return (current * 31 + part.hashCode) & 0x3FFFFFFF; | |
| 2050 } | |
| 2051 return combine(scheme, combine(userInfo, combine(host, combine(port, | |
| 2052 combine(path, combine(query, combine(fragment, 1))))))); | |
| 2053 } | |
| 2054 | |
| 2055 static void _addIfNonEmpty(StringBuffer sb, String test, | |
| 2056 String first, String second) { | |
| 2057 if ("" != test) { | |
| 2058 sb.write(first); | |
| 2059 sb.write(second); | |
| 2060 } | |
| 2061 } | 2065 } |
| 2062 | 2066 |
| 2063 /** | 2067 /** |
| 2064 * Encode the string [component] using percent-encoding to make it | 2068 * Encode the string [component] using percent-encoding to make it |
| 2065 * safe for literal use as a URI component. | 2069 * safe for literal use as a URI component. |
| 2066 * | 2070 * |
| 2067 * All characters except uppercase and lowercase letters, digits and | 2071 * All characters except uppercase and lowercase letters, digits and |
| 2068 * the characters `-_.!~*'()` are percent-encoded. This is the | 2072 * the characters `-_.!~*'()` are percent-encoded. This is the |
| 2069 * set of characters specified in RFC 2396 and the which is | 2073 * set of characters specified in RFC 2396 and the which is |
| 2070 * specified for the encodeUriComponent in ECMA-262 version 5.1. | 2074 * specified for the encodeUriComponent in ECMA-262 version 5.1. |
| (...skipping 1281 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3352 // All non-escape RFC-2396 uric characters. | 3356 // All non-escape RFC-2396 uric characters. |
| 3353 // | 3357 // |
| 3354 // uric = reserved | unreserved | escaped | 3358 // uric = reserved | unreserved | escaped |
| 3355 // reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," | 3359 // reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," |
| 3356 // unreserved = alphanum | mark | 3360 // unreserved = alphanum | mark |
| 3357 // mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" | 3361 // mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" |
| 3358 // | 3362 // |
| 3359 // This is the same characters as in a URI query (which is URI pchar plus '?') | 3363 // This is the same characters as in a URI query (which is URI pchar plus '?') |
| 3360 static const _uricTable = Uri._queryCharTable; | 3364 static const _uricTable = Uri._queryCharTable; |
| 3361 } | 3365 } |
| 3366 | |
| 3367 // -------------------------------------------------------------------- | |
| 3368 // Constants used to read the scanner result. | |
| 3369 | |
| 3370 const int _nopIndex = 0; | |
| 3371 const int _schemeEndIndex = 1; | |
| 3372 const int _hostStartIndex = 2; | |
| 3373 const int _portStartIndex = 3; | |
| 3374 const int _pathStartIndex = 4; | |
| 3375 const int _queryStartIndex = 5; | |
| 3376 const int _fragmentStartIndex = 6; | |
| 3377 const int _notSimpleIndex = 7; | |
| 3378 | |
| 3379 // Initial state for scanner. | |
| 3380 const int _start = 00; | |
| 3381 | |
| 3382 final _scannerTables = _createTables(); | |
| 3383 | |
| 3384 // ---------------------------------------------------------------------- | |
| 3385 // Code to create the URI scanner table. | |
| 3386 | |
| 3387 const int _schemeOrPath = 01; | |
| 3388 const int _authOrPath = 02; | |
| 3389 const int _authOrPathSlash = 03; | |
| 3390 const int _uinfoOrHost0 = 04; | |
| 3391 const int _uinfoOrHost = 05; | |
| 3392 const int _uinfoOrPort0 = 06; | |
| 3393 const int _uinfoOrPort = 07; | |
| 3394 const int _ipv6Host = 08; | |
| 3395 const int _relPathSeg = 09; | |
| 3396 const int _pathSeg = 10; | |
| 3397 const int _path = 11; | |
| 3398 const int _query = 12; | |
| 3399 const int _fragment = 13; | |
| 3400 const int _schemeOrPathDot = 14; | |
| 3401 const int _schemeOrPathDot2 = 15; | |
| 3402 const int _relPathSegDot = 16; | |
| 3403 const int _relPathSegDot2 = 17; | |
| 3404 const int _pathSegDot = 18; | |
| 3405 const int _pathSegDot2 = 19; | |
| 3406 | |
| 3407 const int _scheme0 = 20; | |
| 3408 const int _scheme = 21; | |
| 3409 | |
| 3410 const int _stateCount = 22; | |
| 3411 const int _nonSimpleEndStates = 14; | |
|
floitsch
2016/06/28 00:28:04
This needs a comment and should be separated from
Lasse Reichstein Nielsen
2016/06/28 13:00:52
Commented and moved. Most of the states have been
| |
| 3412 | |
| 3413 const int _nop = _nopIndex << 5; | |
| 3414 const int _schemeEnd = _schemeEndIndex << 5; | |
| 3415 const int _hostStart = _hostStartIndex << 5; | |
| 3416 const int _portStart = _portStartIndex << 5; | |
| 3417 const int _pathStart = _pathStartIndex << 5; | |
| 3418 const int _queryStart = _queryStartIndex << 5; | |
| 3419 const int _fragmentStart = _fragmentStartIndex << 5; | |
| 3420 const int _notSimple = _notSimpleIndex << 5; | |
| 3421 | |
| 3422 | |
| 3423 void _setChars(Uint8List target, String chars, int value) { | |
|
floitsch
2016/06/28 00:28:03
Needs dartdocs.
Lasse Reichstein Nielsen
2016/06/28 13:00:51
Done.
| |
| 3424 for (int i = 0; i < chars.length; i++) { | |
| 3425 var char = chars.codeUnitAt(i); | |
| 3426 target[char ^ 0x60] = value; | |
|
floitsch
2016/06/28 00:28:04
Needs explanation what the ^ 0x60 does.
Lasse Reichstein Nielsen
2016/06/28 13:00:52
Now described on the _scannerTables declaration.
| |
| 3427 } | |
| 3428 } | |
| 3429 | |
| 3430 void _setRange(Uint8List target, String range, int value) { | |
|
floitsch
2016/06/28 00:28:03
Needs dartdocs.
Lasse Reichstein Nielsen
2016/06/28 13:00:51
Done.
| |
| 3431 for (int i = range.codeUnitAt(0), n = range.codeUnitAt(1); i <= n; i++) { | |
| 3432 target[i ^ 0x60] = value; | |
| 3433 } | |
| 3434 } | |
| 3435 | |
| 3436 List<Uint8List> _createTables() { | |
|
floitsch
2016/06/28 00:28:04
Needs documentation.
Lasse Reichstein Nielsen
2016/06/28 13:00:52
Done.
| |
| 3437 // TODO(lrn): Use a precomputed table. | |
| 3438 var tables = new List<Uint8List>.generate(_stateCount, | |
| 3439 (_) => new Uint8List(96)); | |
| 3440 | |
| 3441 const unreserved = | |
| 3442 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-._~" ; | |
| 3443 const subDelims = r"!$&'()*+,;="; | |
| 3444 const pchar = "$unreserved$subDelims"; | |
|
floitsch
2016/06/28 00:28:03
what does the "p" stand for?
Lasse Reichstein Nielsen
2016/06/28 13:00:52
This refers to the "pchar" character group in RFC
| |
| 3445 | |
| 3446 build(index, defaultValue) => | |
| 3447 tables[index]..fillRange(0, 96, defaultValue); | |
| 3448 | |
| 3449 var b; | |
| 3450 // Validate as path, if it is a scheme, we handle it later. | |
| 3451 b = build(_start, _schemeOrPath | _notSimple); | |
| 3452 _setChars(b, pchar, _schemeOrPath); | |
| 3453 _setChars(b, ".", _schemeOrPathDot); | |
| 3454 _setChars(b, "%", _schemeOrPath | _notSimple); | |
| 3455 _setChars(b, ":", _authOrPath | _schemeEnd); // Handle later. | |
| 3456 _setChars(b, "/", _authOrPathSlash); | |
| 3457 _setChars(b, "?", _query | _queryStart); | |
| 3458 _setChars(b, "#", _fragment | _fragmentStart); | |
| 3459 | |
| 3460 b = build(_schemeOrPathDot, _schemeOrPath | _notSimple); | |
| 3461 _setChars(b, pchar, _schemeOrPath); | |
| 3462 _setChars(b, ".", _schemeOrPathDot2); | |
| 3463 _setChars(b, "%", _schemeOrPath | _notSimple); | |
| 3464 _setChars(b, ':', _authOrPath | _schemeEnd); | |
| 3465 _setChars(b, "/", _pathSeg | _notSimple); | |
| 3466 _setChars(b, "?", _query | _queryStart); | |
| 3467 _setChars(b, "#", _fragment | _fragmentStart); | |
| 3468 | |
| 3469 b = build(_schemeOrPathDot2, _schemeOrPath | _notSimple); | |
| 3470 _setChars(b, pchar, _schemeOrPath); | |
| 3471 _setChars(b, "%", _schemeOrPath | _notSimple); | |
| 3472 _setChars(b, ':', _authOrPath | _schemeEnd); | |
| 3473 _setChars(b, "/", _relPathSeg); | |
| 3474 _setChars(b, "?", _query | _queryStart); | |
| 3475 _setChars(b, "#", _fragment | _fragmentStart); | |
| 3476 | |
| 3477 b = build(_schemeOrPath, _schemeOrPath | _notSimple); | |
| 3478 _setChars(b, pchar, _schemeOrPath); | |
| 3479 _setChars(b, ':', _authOrPath | _schemeEnd); | |
| 3480 _setChars(b, "/", _pathSeg); | |
| 3481 _setChars(b, "?", _query | _queryStart); | |
| 3482 _setChars(b, "#", _fragment | _fragmentStart); | |
| 3483 | |
| 3484 b = build(_authOrPath, _path | _notSimple); | |
| 3485 _setChars(b, pchar, _path); | |
| 3486 _setChars(b, "/", _authOrPathSlash); | |
| 3487 _setChars(b, ".", _pathSegDot); | |
| 3488 _setChars(b, "?", _query | _queryStart); | |
| 3489 _setChars(b, "#", _fragment | _fragmentStart); | |
| 3490 | |
| 3491 b = build(_authOrPathSlash, _path | _notSimple); | |
| 3492 _setChars(b, pchar, _path); | |
| 3493 _setChars(b, "/", _uinfoOrHost0 | _hostStart); | |
| 3494 _setChars(b, ".", _pathSegDot); | |
| 3495 _setChars(b, "?", _query | _queryStart); | |
| 3496 _setChars(b, "#", _fragment | _fragmentStart); | |
| 3497 | |
| 3498 b = build(_uinfoOrHost0, _uinfoOrHost | _notSimple); | |
| 3499 _setChars(b, pchar, _uinfoOrHost); | |
| 3500 _setRange(b, "AZ", _uinfoOrHost | _notSimple); | |
| 3501 _setChars(b, ":", _uinfoOrPort0 | _portStart); | |
| 3502 _setChars(b, "@", _uinfoOrHost0 | _hostStart); | |
| 3503 _setChars(b, "[", _ipv6Host | _notSimple); | |
| 3504 _setChars(b, "/", _pathSeg | _pathStart); | |
| 3505 _setChars(b, "?", _query | _queryStart); | |
| 3506 _setChars(b, "#", _fragment | _fragmentStart); | |
| 3507 | |
| 3508 b = build(_uinfoOrHost, _uinfoOrHost | _notSimple); | |
| 3509 _setChars(b, pchar, _uinfoOrHost); | |
| 3510 _setRange(b, "AZ", _uinfoOrHost | _notSimple); | |
| 3511 _setChars(b, ":", _uinfoOrPort0 | _portStart); | |
| 3512 _setChars(b, "@", _uinfoOrHost0 | _hostStart); | |
| 3513 _setChars(b, "/", _pathSeg | _pathStart); | |
| 3514 _setChars(b, "?", _query | _queryStart); | |
| 3515 _setChars(b, "#", _fragment | _fragmentStart); | |
| 3516 | |
| 3517 b = build(_uinfoOrPort0, _uinfoOrPort | _notSimple); | |
| 3518 _setChars(b, "0", _uinfoOrPort | _notSimple); | |
| 3519 _setRange(b, "19", _uinfoOrPort); | |
| 3520 _setChars(b, "@", _uinfoOrHost0 | _hostStart); | |
| 3521 _setChars(b, "/", _pathSeg | _pathStart); | |
| 3522 _setChars(b, "?", _query | _queryStart); | |
| 3523 _setChars(b, "#", _fragment | _fragmentStart); | |
| 3524 | |
| 3525 b = build(_uinfoOrPort, _uinfoOrPort | _notSimple); | |
| 3526 _setRange(b, "09", _uinfoOrPort); | |
| 3527 _setChars(b, "@", _uinfoOrHost0 | _hostStart); | |
| 3528 _setChars(b, "/", _pathSeg | _pathStart); | |
| 3529 _setChars(b, "?", _query | _queryStart); | |
| 3530 _setChars(b, "#", _fragment | _fragmentStart); | |
| 3531 | |
| 3532 b = build(_ipv6Host, _ipv6Host); | |
| 3533 _setChars(b, "]", _uinfoOrHost); | |
| 3534 | |
| 3535 b = build(_relPathSeg, _path | _notSimple); | |
| 3536 _setChars(b, pchar, _path); | |
| 3537 _setChars(b, ".", _relPathSegDot); | |
| 3538 _setChars(b, "/", _pathSeg | _notSimple); | |
| 3539 _setChars(b, "?", _query | _queryStart); | |
| 3540 _setChars(b, "#", _fragment | _fragmentStart); | |
| 3541 | |
| 3542 b = build(_relPathSegDot, _path | _notSimple); | |
| 3543 _setChars(b, pchar, _path); | |
| 3544 _setChars(b, ".", _relPathSegDot2); | |
| 3545 _setChars(b, "/", _pathSeg | _notSimple); | |
| 3546 _setChars(b, "?", _query | _queryStart); | |
| 3547 _setChars(b, "#", _fragment | _fragmentStart); | |
| 3548 | |
| 3549 b = build(_relPathSegDot2, _path | _notSimple); | |
| 3550 _setChars(b, pchar, _path); | |
| 3551 _setChars(b, ".", _path); | |
| 3552 _setChars(b, "/", _relPathSeg); | |
| 3553 _setChars(b, "?", _query | _queryStart); | |
| 3554 _setChars(b, "#", _fragment | _fragmentStart); | |
| 3555 | |
| 3556 b = build(_pathSeg, _path | _notSimple); | |
| 3557 _setChars(b, pchar, _path); | |
| 3558 _setChars(b, ".", _pathSegDot); | |
| 3559 _setChars(b, "/", _pathSeg | _notSimple); | |
| 3560 _setChars(b, "?", _query | _queryStart); | |
| 3561 _setChars(b, "#", _fragment | _fragmentStart); | |
| 3562 | |
| 3563 b = build(_pathSegDot, _path | _notSimple); | |
| 3564 _setChars(b, pchar, _path); | |
| 3565 _setChars(b, ".", _pathSegDot2); | |
| 3566 _setChars(b, "/", _pathSeg | _notSimple); | |
| 3567 _setChars(b, "?", _query | _queryStart); | |
| 3568 _setChars(b, "#", _fragment | _fragmentStart); | |
| 3569 | |
| 3570 b = build(_pathSegDot2, _path | _notSimple); | |
| 3571 _setChars(b, pchar, _path); | |
| 3572 _setChars(b, ".", _path); | |
| 3573 _setChars(b, "/", _pathSeg | _notSimple); | |
| 3574 _setChars(b, "?", _query | _queryStart); | |
| 3575 _setChars(b, "#", _fragment | _fragmentStart); | |
| 3576 | |
| 3577 b = build(_path, _path | _notSimple); | |
| 3578 _setChars(b, pchar, _path); | |
| 3579 _setChars(b, "/", _pathSeg); | |
| 3580 _setChars(b, "?", _query | _queryStart); | |
| 3581 _setChars(b, "#", _fragment | _fragmentStart); | |
| 3582 | |
| 3583 b = build(_query, _query | _notSimple); | |
| 3584 _setChars(b, pchar, _query); | |
| 3585 _setChars(b, "?", _query); | |
| 3586 _setChars(b, "#", _fragment | _fragmentStart); | |
| 3587 | |
| 3588 b = build(_fragment, _fragment | _notSimple); | |
| 3589 _setChars(b, pchar, _fragment); | |
| 3590 _setChars(b, "?", _fragment); | |
| 3591 | |
| 3592 // A separate two-state validator for scheme names. | |
| 3593 // Only accepts lower-case letters. | |
| 3594 b = build(_scheme0, _scheme | _notSimple); | |
| 3595 _setRange(b, "az", _scheme); | |
| 3596 | |
| 3597 b = build(_scheme, _scheme | _notSimple); | |
| 3598 _setRange(b, "az", _scheme); | |
| 3599 _setRange(b, "09", _scheme); | |
| 3600 _setChars(b, "+-.", _scheme); | |
| 3601 | |
| 3602 return tables; | |
| 3603 } | |
| 3604 | |
| 3605 // -------------------------------------------------------------------- | |
| 3606 // Code that uses the URI scanner table. | |
| 3607 | |
| 3608 int _scan(String uri, int start, int end, int state, List<int> indices) { | |
| 3609 var tables = _scannerTables; | |
| 3610 assert(end <= uri.length); | |
| 3611 for (int i = start; i < end; i++) { | |
| 3612 var table = tables[state]; | |
| 3613 // xor 0x60 to move range 0x20-0x7f into 0x00-0x5f | |
| 3614 int char = uri.codeUnitAt(i) ^ 0x60; | |
| 3615 // use 0x1f (nee 0x7f) to represent all unhandled characters. | |
|
floitsch
2016/06/28 00:28:03
Use
Lasse Reichstein Nielsen
2016/06/28 13:00:52
Done.
| |
| 3616 if (char > 0x5f) char = 0x1f; // TODO: check if negating test is better. | |
|
floitsch
2016/06/28 00:28:03
TODO(ldap)
Lasse Reichstein Nielsen
2016/06/28 13:00:52
Or check it. It isn't faster.
| |
| 3617 int transition = table[char]; | |
| 3618 state = transition & 0x1f; | |
| 3619 indices[transition >> 5] = i; | |
| 3620 } | |
| 3621 return state; | |
| 3622 } | |
| 3623 | |
| 3624 List<int> _scanUri(String uri, int start, int end) { | |
| 3625 var indices = new List<int>.filled(8, start - 1); | |
| 3626 indices[_portStartIndex] = start; // equals effective pathStart if no auth. | |
|
floitsch
2016/06/28 00:28:04
"Equals"
Lasse Reichstein Nielsen
2016/06/28 13:00:51
Done.
| |
| 3627 indices[_pathStartIndex] = start; | |
| 3628 indices[_queryStartIndex] = end; | |
| 3629 indices[_fragmentStartIndex] = end; | |
| 3630 var state = _scan(uri, start, end, _start, indices); | |
| 3631 // Some states that should be non-simple, but the URI ended early. | |
| 3632 if (state >= _nonSimpleEndStates) { | |
| 3633 indices[_notSimpleIndex] = end; | |
| 3634 } | |
| 3635 int schemeEnd = indices[_schemeEndIndex]; | |
| 3636 if (schemeEnd >= start) { | |
| 3637 // Rescan the scheme part now that we know it's not a path. | |
| 3638 state = _scan(uri, start, schemeEnd, _scheme0, indices); | |
| 3639 if (state == _scheme0) { | |
| 3640 // Empty scheme. | |
| 3641 indices[_notSimpleIndex] = schemeEnd; | |
| 3642 } | |
| 3643 } | |
| 3644 return indices; | |
| 3645 } | |
| 3646 | |
| 3647 bool _isUpperCase(int char) { | |
|
floitsch
2016/06/28 00:28:04
unused?
Lasse Reichstein Nielsen
2016/06/28 13:00:52
Indeed. Gone.
| |
| 3648 return 0x41 <= char && char <= 0x5b; | |
| 3649 } | |
| 3650 | |
| 3651 class _SimpleUri implements Uri { | |
| 3652 final String _uri; | |
| 3653 final int _schemeEnd; | |
| 3654 final int _hostStart; | |
| 3655 final int _portStart; | |
| 3656 final int _pathStart; | |
| 3657 final int _queryStart; | |
| 3658 final int _fragmentStart; | |
| 3659 /// The scheme is often used to distinguish URIs. | |
| 3660 /// To make comparisons more efficient, we cache the value, and | |
| 3661 /// canonicalize a few known types. | |
| 3662 String _schemeCache; | |
| 3663 | |
| 3664 _SimpleUri( | |
| 3665 this._uri, | |
| 3666 this._schemeEnd, | |
| 3667 this._hostStart, | |
| 3668 this._portStart, | |
| 3669 this._pathStart, | |
| 3670 this._queryStart, | |
| 3671 this._fragmentStart, | |
| 3672 this._schemeCache); | |
| 3673 | |
| 3674 bool get hasScheme => _schemeEnd > 0; | |
| 3675 bool get hasAuthority => _hostStart > 0; | |
| 3676 bool get hasUserInfo => _hostStart > _schemeEnd + 4; | |
| 3677 bool get hasPort => _hostStart > 0 && _portStart + 1 < _pathStart; | |
| 3678 bool get hasQuery => _queryStart < _fragmentStart; | |
| 3679 bool get hasFragment => _fragmentStart < _uri.length; | |
| 3680 | |
| 3681 bool get _isFile => _schemeEnd == 4 && _uri.startsWith("file"); | |
| 3682 bool get _isHttp => _schemeEnd == 4 && _uri.startsWith("http"); | |
| 3683 bool get _isHttps => _schemeEnd == 5 && _uri.startsWith("https"); | |
| 3684 bool get _isPackage => _schemeEnd == 7 && _uri.startsWith("package"); | |
| 3685 bool _isScheme(String scheme) => | |
| 3686 _schemeEnd == scheme.length && _uri.startsWith(scheme); | |
| 3687 | |
| 3688 bool get hasAbsolutePath => _uri.startsWith("/", _pathStart); | |
| 3689 bool get hasEmptyPath => _pathStart == _queryStart; | |
| 3690 | |
| 3691 bool get isAbsolute => hasScheme && !hasFragment; | |
| 3692 | |
| 3693 String get scheme { | |
| 3694 if (_schemeEnd <= 0) return ""; | |
| 3695 if (_schemeCache != null) return _schemeCache; | |
| 3696 if (_isHttp) { | |
| 3697 _schemeCache = "http"; | |
| 3698 } else if (_isHttps) { | |
| 3699 _schemeCache = "https"; | |
| 3700 } else if (_isFile) { | |
| 3701 _schemeCache = "file"; | |
| 3702 } else if (_isPackage) { | |
| 3703 _schemeCache = "package"; | |
| 3704 } else { | |
| 3705 _schemeCache = _uri.substring(0, _schemeEnd); | |
| 3706 } | |
| 3707 return _schemeCache; | |
| 3708 } | |
| 3709 String get authority => _hostStart > 0 ? | |
| 3710 _uri.substring(_schemeEnd + 3, _pathStart) : ""; | |
| 3711 String get userInfo => (_hostStart > _schemeEnd + 3) ? | |
| 3712 _uri.substring(_schemeEnd + 3, _hostStart - 1) : ""; | |
| 3713 String get host => | |
| 3714 _hostStart > 0 ? _uri.substring(_hostStart, _portStart) : ""; | |
| 3715 int get port { | |
| 3716 if (hasPort) return int.parse(_uri.substring(_portStart + 1, _pathStart)); | |
| 3717 if (_isHttp) return 80; | |
| 3718 if (_isHttps) return 443; | |
| 3719 return 0; | |
| 3720 } | |
| 3721 String get path =>_uri.substring(_pathStart, _queryStart); | |
| 3722 String get query => (_queryStart < _fragmentStart) ? | |
| 3723 _uri.substring(_queryStart + 1, _fragmentStart) : ""; | |
| 3724 String get fragment => (_fragmentStart < _uri.length) ? | |
| 3725 _uri.substring(_fragmentStart + 1) : ""; | |
| 3726 | |
| 3727 String get origin { | |
| 3728 // Check original behavior - W3C spec is wonky! | |
| 3729 bool isHttp = _isHttp; | |
| 3730 if (_schemeEnd < 0 || _hostStart == _portStart) { | |
| 3731 throw new StateError("Cannot use origin without a scheme: $this"); | |
| 3732 } | |
| 3733 if (!isHttp && !_isHttps) { | |
| 3734 throw new StateError( | |
| 3735 "Origin is only applicable schemes http and https: $this"); | |
| 3736 } | |
| 3737 if (_hostStart == _schemeEnd + 3) { | |
| 3738 return _uri.substring(0, _pathStart); | |
| 3739 } | |
| 3740 // Need to drop anon-empty userInfo. | |
| 3741 return _uri.substring(0, _schemeEnd + 3) + | |
| 3742 _uri.substring(_hostStart, _pathStart); | |
| 3743 } | |
| 3744 | |
| 3745 List<String> get pathSegments { | |
| 3746 int start = _pathStart; | |
| 3747 int end = _queryStart; | |
| 3748 if (_uri.startsWith("/", start)) start++; | |
| 3749 if (start == end) return const <String>[]; | |
| 3750 List<String> parts = []; | |
| 3751 for (int i = start; i < end; i++) { | |
| 3752 var char = _uri.codeUnitAt(i); | |
| 3753 if (char == Uri._SLASH) { | |
| 3754 parts.add(_uri.substring(start, i)); | |
| 3755 start = i + 1; | |
| 3756 } | |
| 3757 } | |
| 3758 parts.add(_uri.substring(start, end)); | |
| 3759 return new List<String>.unmodifiable(parts); | |
| 3760 } | |
| 3761 | |
| 3762 Map<String, String> get queryParameters { | |
| 3763 if (!hasQuery) return const <String, String>{}; | |
| 3764 return new UnmodifiableMapView<String, String>( | |
| 3765 Uri.splitQueryString(query)); | |
| 3766 } | |
| 3767 | |
| 3768 Map<String, List<String>> get queryParametersAll { | |
| 3769 if (!hasQuery) return const <String, List<String>>{}; | |
| 3770 Map queryParameterLists = Uri._splitQueryStringAll(query); | |
| 3771 for (var key in queryParameterLists.keys) { | |
| 3772 queryParameterLists[key] = | |
| 3773 new List<String>.unmodifiable(queryParameterLists[key]); | |
| 3774 } | |
| 3775 return new Map<String, List<String>>.unmodifiable(queryParameterLists); | |
| 3776 } | |
| 3777 | |
| 3778 bool _isPort(String port) { | |
| 3779 int portDigitStart = _portStart + 1; | |
| 3780 return portDigitStart + port.length == _pathStart && | |
| 3781 _uri.startsWith(port, portDigitStart); | |
| 3782 } | |
| 3783 | |
| 3784 Uri normalizePath() => this; | |
| 3785 | |
| 3786 Uri removeFragment() { | |
| 3787 if (!hasFragment) return this; | |
| 3788 return new _SimpleUri( | |
| 3789 _uri.substring(0, _fragmentStart), | |
| 3790 _schemeEnd, _hostStart, _portStart, | |
| 3791 _pathStart, _queryStart, _fragmentStart, _schemeCache); | |
| 3792 } | |
| 3793 | |
| 3794 Uri replace({String scheme, | |
| 3795 String userInfo, | |
| 3796 String host, | |
| 3797 int port, | |
| 3798 String path, | |
| 3799 Iterable<String> pathSegments, | |
| 3800 String query, | |
| 3801 Map<String, dynamic/*String|Iterable<String>*/> queryParameters, | |
| 3802 String fragment}) { | |
| 3803 bool schemeChanged = false; | |
| 3804 if (scheme != null) { | |
| 3805 scheme = Uri._makeScheme(scheme, 0, scheme.length); | |
| 3806 schemeChanged = !_isScheme(scheme); | |
| 3807 } else { | |
| 3808 scheme = this.scheme; | |
| 3809 } | |
| 3810 bool isFile = (scheme == "file"); | |
| 3811 if (userInfo != null) { | |
| 3812 userInfo = Uri._makeUserInfo(userInfo, 0, userInfo.length); | |
| 3813 } else if (_hostStart > 0) { | |
| 3814 userInfo = _uri.substring(_schemeEnd + 3, _hostStart); | |
| 3815 } else { | |
| 3816 userInfo = ""; | |
| 3817 } | |
| 3818 if (port != null) { | |
| 3819 port = Uri._makePort(port, scheme); | |
| 3820 } else { | |
| 3821 port = this.hasPort ? this.port : null; | |
| 3822 if (schemeChanged) { | |
| 3823 // The default port might have changed. | |
| 3824 port = Uri._makePort(port, scheme); | |
| 3825 } | |
| 3826 } | |
| 3827 if (host != null) { | |
| 3828 host = Uri._makeHost(host, 0, host.length, false); | |
| 3829 } else if (_hostStart > 0) { | |
| 3830 host = _uri.substring(_hostStart, _portStart); | |
| 3831 } else if (userInfo.isNotEmpty || port != null || isFile) { | |
| 3832 host = ""; | |
| 3833 } | |
| 3834 | |
| 3835 bool hasAuthority = host != null; | |
| 3836 if (path != null || pathSegments != null) { | |
| 3837 path = Uri._makePath(path, 0, _stringOrNullLength(path), pathSegments, | |
| 3838 scheme, hasAuthority); | |
| 3839 } else { | |
| 3840 path = _uri.substring(_pathStart, _queryStart); | |
| 3841 if ((isFile || (hasAuthority && !path.isEmpty)) && | |
| 3842 !path.startsWith('/')) { | |
| 3843 path = "/" + path; | |
| 3844 } | |
| 3845 } | |
| 3846 | |
| 3847 if (query != null || queryParameters != null) { | |
| 3848 query = | |
| 3849 Uri._makeQuery(query, 0, _stringOrNullLength(query), queryParameters); | |
| 3850 } else if (_queryStart < _fragmentStart) { | |
| 3851 query = _uri.substring(_queryStart, _fragmentStart); | |
| 3852 } | |
| 3853 | |
| 3854 if (fragment != null) { | |
| 3855 fragment = Uri._makeFragment(fragment, 0, fragment.length); | |
| 3856 } else if (_fragmentStart < _uri.length) { | |
| 3857 fragment = _uri.substring(_fragmentStart); | |
| 3858 } | |
| 3859 | |
| 3860 return new Uri._internal( | |
| 3861 scheme, userInfo, host, port, path, query, fragment); | |
| 3862 } | |
| 3863 | |
| 3864 Uri resolve(String reference) { | |
| 3865 return resolveUri(Uri.parse(reference)); | |
| 3866 } | |
| 3867 | |
| 3868 Uri resolveUri(Uri reference) { | |
| 3869 if (reference is _SimpleUri) { | |
| 3870 return _simpleMerge(this, reference); | |
| 3871 } | |
| 3872 return _toNonSimple().resolveUri(reference); | |
| 3873 } | |
| 3874 | |
| 3875 // Merge two simple URIs. This should always result in a prefix of | |
| 3876 // one concatentated with a suffix of the other, which is again simple. | |
| 3877 // In a few cases, there might be a need for extra normalization, when | |
| 3878 // resolving on top of a known scheme. | |
| 3879 Uri _simpleMerge(_SimpleUri base, _SimpleUri ref) { | |
| 3880 if (ref.hasScheme) return ref; | |
| 3881 if (ref.hasAuthority) { | |
| 3882 if (!base.hasScheme) return ref; | |
| 3883 bool isSimple = true; | |
| 3884 if (base._isFile) { | |
| 3885 isSimple = !ref.hasEmptyPath; | |
| 3886 } else if (base._isHttp) { | |
| 3887 isSimple = !ref._isPort("80"); | |
| 3888 } else if (base._isHttps) { | |
| 3889 isSimple = !ref._isPort("443"); | |
| 3890 } | |
| 3891 if (isSimple) { | |
| 3892 var delta = base._schemeEnd + 1; | |
| 3893 var newUri = base._uri.substring(0, base._schemeEnd + 1) + | |
| 3894 ref._uri.substring(ref._schemeEnd + 1); | |
| 3895 return new _SimpleUri(newUri, | |
| 3896 base._schemeEnd, | |
| 3897 ref._hostStart + delta, | |
| 3898 ref._portStart + delta, | |
| 3899 ref._pathStart + delta, | |
| 3900 ref._queryStart + delta, | |
| 3901 ref._fragmentStart + delta, | |
| 3902 base._schemeCache); | |
| 3903 } else { | |
| 3904 // Slowcase. | |
| 3905 return _toNonSimple().resolveUri(ref); | |
| 3906 } | |
| 3907 } | |
| 3908 if (ref.hasEmptyPath) { | |
| 3909 if (ref.hasQuery) { | |
| 3910 int delta = base._queryStart - ref._queryStart; | |
| 3911 var newUri = base._uri.substring(0, base._queryStart) + | |
| 3912 ref._uri.substring(ref._queryStart); | |
| 3913 return new _SimpleUri(newUri, | |
| 3914 base._schemeEnd, | |
| 3915 base._hostStart, | |
| 3916 base._portStart, | |
| 3917 base._pathStart, | |
| 3918 ref._queryStart + delta, | |
| 3919 ref._fragmentStart + delta, | |
| 3920 base._schemeCache); | |
| 3921 } | |
| 3922 if (ref.hasFragment) { | |
| 3923 int delta = base._fragmentStart - ref._fragmentStart; | |
| 3924 var newUri = base._uri.substring(0, base._fragmentStart) + | |
| 3925 ref._uri.substring(ref._fragmentStart); | |
| 3926 return new _SimpleUri(newUri, | |
| 3927 base._schemeEnd, | |
| 3928 base._hostStart, | |
| 3929 base._portStart, | |
| 3930 base._pathStart, | |
| 3931 base._queryStart, | |
| 3932 ref._fragmentStart + delta, | |
| 3933 base._schemeCache); | |
| 3934 } | |
| 3935 return base.removeFragment(); | |
| 3936 } | |
| 3937 if (ref.hasAbsolutePath) { | |
| 3938 var delta = base._pathStart - ref._pathStart; | |
| 3939 var newUri = base._uri.substring(0, base._pathStart) + | |
| 3940 ref._uri.substring(ref._pathStart); | |
| 3941 return new _SimpleUri(newUri, | |
| 3942 base._schemeEnd, | |
| 3943 base._hostStart, | |
| 3944 base._portStart, | |
| 3945 base._pathStart, | |
| 3946 ref._queryStart + delta, | |
| 3947 ref._fragmentStart + delta, | |
| 3948 base._schemeCache); | |
| 3949 } | |
| 3950 if (base.hasEmptyPath && base.hasAuthority) { | |
| 3951 // ref has relative non-empty path. | |
| 3952 var delta = base._pathStart - ref._pathStart + 1; | |
| 3953 var newUri = "${base._uri.substring(0, base._pathStart)}/" | |
| 3954 "${ref._uri.substring(ref._pathStart)}"; | |
| 3955 return new _SimpleUri(newUri, | |
| 3956 base._schemeEnd, | |
| 3957 base._hostStart, | |
| 3958 base._portStart, | |
| 3959 base._pathStart, | |
| 3960 ref._queryStart + delta, | |
| 3961 ref._fragmentStart + delta, | |
| 3962 base._schemeCache); | |
| 3963 } | |
| 3964 // Merge paths. | |
| 3965 if (base._uri.startsWith("../", base._pathStart)) { | |
| 3966 // Complex rare case, go slow. | |
| 3967 return _toNonSimple().resolveUri(ref); | |
| 3968 } | |
| 3969 String baseUri = base._uri; | |
| 3970 String refUri = ref._uri; | |
| 3971 int baseStart = base._pathStart; | |
| 3972 int baseEnd = base._queryStart; | |
| 3973 int refStart = ref._pathStart; | |
| 3974 int refEnd = ref._queryStart; | |
| 3975 int backCount = 1; | |
| 3976 while (refStart + 3 <= refEnd && refUri.startsWith("../", refStart)) { | |
| 3977 refStart += 3; | |
| 3978 backCount += 1; | |
| 3979 } | |
| 3980 if (refStart + 2 == refEnd && refUri.startsWith("..", refStart)) { | |
| 3981 refStart += 2; | |
| 3982 backCount += 1; | |
| 3983 } | |
| 3984 | |
| 3985 const slash = 0x2f; | |
| 3986 while (baseEnd > baseStart) { | |
| 3987 baseEnd--; | |
| 3988 int char = baseUri.codeUnitAt(baseEnd); | |
| 3989 if (char == slash) { | |
| 3990 backCount--; | |
| 3991 if (backCount == 0) break; | |
| 3992 } | |
| 3993 } | |
| 3994 var delta; | |
| 3995 var newUri; | |
| 3996 if (baseEnd != baseStart || base._uri.startsWith("/", baseStart) || | |
| 3997 base.hasScheme || base.hasAuthority) { | |
| 3998 delta = baseEnd - refStart + 1; | |
| 3999 newUri = "${base._uri.substring(0, baseEnd)}/" | |
| 4000 "${ref._uri.substring(refStart)}"; | |
| 4001 } else { | |
| 4002 // We exactly removed all of a relative path. | |
| 4003 // Example: foo:bar/baz resolve vs. ../../qux -> foo:qux | |
| 4004 delta = baseEnd - refStart; | |
| 4005 newUri = "${base._uri.substring(0, baseStart)}" | |
| 4006 "${ref._uri.substring(refStart)}"; | |
| 4007 } | |
| 4008 | |
| 4009 return new _SimpleUri(newUri, | |
| 4010 base._schemeEnd, | |
| 4011 base._hostStart, | |
| 4012 base._portStart, | |
| 4013 base._pathStart, | |
| 4014 ref._queryStart + delta, | |
| 4015 ref._fragmentStart + delta, | |
| 4016 base._schemeCache); | |
| 4017 } | |
| 4018 | |
| 4019 // TODO: Deprecate Remove Kill Crush Destroy! | |
|
floitsch
2016/06/28 00:28:03
TODO(ldap).
Also, make this more informative.
Lasse Reichstein Nielsen
2016/06/28 13:00:52
Done. Whoops. :)
| |
| 4020 // Reason: requires knowing whether we are on Windows. | |
| 4021 // That's a PLATFORM SPECIFIC THING. | |
| 4022 // This should be File.pathFromUri, since File is platform specific already. | |
| 4023 String toFilePath({bool windows}) { | |
| 4024 if (_schemeEnd >= 0 && !_isFile) { | |
| 4025 throw new UnsupportedError( | |
| 4026 "Cannot extract a file path from a $scheme URI"); | |
| 4027 } | |
| 4028 if (_queryStart < _uri.length) { | |
| 4029 if (_queryStart < _fragmentStart) { | |
| 4030 throw new UnsupportedError( | |
| 4031 "Cannot extract a file path from a URI with a query component"); | |
| 4032 } | |
| 4033 throw new UnsupportedError( | |
| 4034 "Cannot extract a file path from a URI with a fragment component"); | |
| 4035 } | |
| 4036 if (windows == null) windows = Uri._isWindows; | |
| 4037 return windows ? Uri._toWindowsFilePath(this) : _toFilePath(); | |
| 4038 } | |
| 4039 | |
| 4040 String _toFilePath() { | |
| 4041 if (_hostStart < _portStart) { | |
| 4042 // Has authority and non-empty host. | |
| 4043 throw new UnsupportedError( | |
| 4044 "Cannot extract a non-Windows file path from a file URI " | |
| 4045 "with an authority"); | |
| 4046 } | |
| 4047 return this.path; | |
| 4048 } | |
| 4049 | |
| 4050 UriData get data { | |
| 4051 assert(scheme != "data"); | |
| 4052 return null; | |
| 4053 } | |
| 4054 | |
| 4055 int get hashCode => _uri.hashCode; | |
| 4056 | |
| 4057 bool operator==(Object other) { | |
| 4058 if (other is! Uri) return false; | |
| 4059 return _uri == other.toString(); | |
| 4060 } | |
| 4061 | |
| 4062 Uri _toNonSimple() { | |
| 4063 return new Uri._internal( | |
| 4064 this.scheme, | |
| 4065 this.userInfo, | |
| 4066 this.hasAuthority ? this.host: null, | |
| 4067 this.hasPort ? this.port : null, | |
| 4068 this.path, | |
| 4069 this.hasQuery ? this.query : null, | |
| 4070 this.hasFragment ? this.fragment : null | |
| 4071 ); | |
| 4072 } | |
| 4073 | |
| 4074 String toString() => _uri; | |
| 4075 } | |
| OLD | NEW |