Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 part of dart.core; | |
| 6 | |
| 7 /** | |
| 8 * A parsed URI, as specified by RFC-3986, http://tools.ietf.org/html/rfc3986. | |
| 9 */ | |
| 10 class Uri { | |
| 11 int _port; | |
| 12 | |
| 13 /* | |
| 14 * Returns the URI scheme. If there is no scheme the empty string | |
|
floitsch
2013/05/24 20:37:27
All these comments are dartdocs. -> /** instead of
Søren Gjesse
2013/05/27 10:47:21
Of cause - thanks.
| |
| 15 * will be returned. | |
|
floitsch
2013/05/24 20:37:27
No need for future.
If there is no scheme the empt
Søren Gjesse
2013/05/27 10:47:21
Done.
Similar change for all Uri component proper
| |
| 16 */ | |
| 17 final String scheme; | |
| 18 | |
| 19 /* | |
| 20 * Returns the user info part of the URI authority. If there is no | |
| 21 * user info in the authority the empty string will be returned. | |
| 22 */ | |
| 23 final String userInfo; | |
| 24 | |
| 25 /* | |
| 26 * Returns the host part of the URI authority. If there is no | |
| 27 * authority and hence no host the empty string will be returned. | |
| 28 */ | |
| 29 final String host; | |
| 30 | |
| 31 /* | |
| 32 * Returns the port part of the URI authority. If there is no | |
| 33 * port in the authority 0 will be returned. | |
| 34 */ | |
| 35 int get port => _port; | |
| 36 | |
| 37 /* | |
| 38 * Returns the URI path. The returned path is encoded. To get direct | |
| 39 * access to the decoded path use [pathSegement]. If there is no | |
|
floitsch
2013/05/24 20:37:27
pathSegments
Søren Gjesse
2013/05/27 10:47:21
Done.
| |
| 40 * path the empty string will be returned. | |
| 41 */ | |
| 42 final String path; | |
| 43 | |
| 44 /* | |
| 45 * Returns the URI query. The returned query is encoded. To get | |
| 46 * direct access to the decoded query use [queryParameters]. If | |
| 47 * there is no query the empty string will be returned. | |
| 48 */ | |
| 49 final String query; | |
| 50 | |
| 51 /* | |
| 52 * Returns the URI fragment. If there is no fragment the empty string | |
| 53 * will be returned. | |
| 54 */ | |
| 55 final String fragment; | |
| 56 | |
| 57 /** | |
| 58 * Creates a new URI object by parsing a URI string. | |
| 59 */ | |
| 60 static Uri parse(String uri) => new Uri._fromMatch(_splitRe.firstMatch(uri)); | |
| 61 | |
| 62 Uri._fromMatch(Match m) : | |
| 63 this(scheme: _emptyIfNull(m[_COMPONENT_SCHEME]), | |
| 64 userInfo: _emptyIfNull(m[_COMPONENT_USER_INFO]), | |
| 65 host: _eitherOf( | |
| 66 m[_COMPONENT_HOST], m[_COMPONENT_HOST_IPV6]), | |
| 67 port: _parseIntOrZero(m[_COMPONENT_PORT]), | |
| 68 path: _emptyIfNull(m[_COMPONENT_PATH]), | |
| 69 query: _emptyIfNull(m[_COMPONENT_QUERY_DATA]), | |
| 70 fragment: _emptyIfNull(m[_COMPONENT_FRAGMENT])); | |
| 71 | |
| 72 /* | |
| 73 * Create a new URI from its components. | |
| 74 * | |
| 75 * The URI scheme is set through [scheme]. The scheme will be | |
| 76 * normalized to all lowercase letters. | |
| 77 * | |
| 78 * The URI user info of the domain is set through [userInfo]. | |
| 79 * | |
| 80 * The URI host part of the domain is set through [host]. The host | |
| 81 * can either be a hostname, a IPv4 address or an IPv6 address, | |
| 82 * contained in '[' and ']'. If the host contains a ':' character, | |
| 83 * the host will have the '[' and ']' added. | |
| 84 * | |
| 85 * The URI port is set through [port]. The port is normalized for | |
| 86 * scheme http and https where port 80 and port 443 respectively | |
| 87 * will be set as 0. | |
|
floitsch
2013/05/24 20:37:27
is set to 0.
Søren Gjesse
2013/05/27 10:47:21
This was a broken sentence. Removed the "as 0" fro
| |
| 88 * | |
| 89 * The path component can be specified using either [path] or | |
| 90 * [pathSegments]. When [path] is used the provided string is | |
| 91 * expected to be fully percent-encoded and will be used in its | |
|
floitsch
2013/05/24 20:37:27
is used
Søren Gjesse
2013/05/27 10:47:21
Done.
| |
| 92 * literal form for the URI path. When [pathSegments] is used each | |
| 93 * of the provided segments will be percent encoded and joined using | |
| 94 * the forward slash separator. | |
| 95 * | |
| 96 * The query component can be specified using either [query] or | |
| 97 * [queryParameters]. When [query] is used the provided string is | |
| 98 * expected to be fully percent-encoded and will be used in its | |
| 99 * literal form for the URI query. When [queryParameters] is used xxx | |
|
floitsch
2013/05/24 20:37:27
xxx
Søren Gjesse
2013/05/27 10:47:21
My mark of unfinished comment - sorry.
| |
| 100 * | |
| 101 * The URI fragment is set through [fragment]. | |
| 102 */ | |
| 103 Uri({scheme, | |
| 104 this.userInfo: "", | |
| 105 this.host: "", | |
| 106 port: 0, | |
| 107 String path, | |
| 108 List<String> pathSegments, | |
| 109 String query, | |
| 110 Map<String, String> queryParameters, | |
| 111 fragment: ""}) : | |
| 112 scheme = _makeScheme(scheme), | |
| 113 path = _makePath(path, pathSegments), | |
| 114 query = _makeQuery(query, queryParameters), | |
| 115 fragment = _makeFragment(fragment) { | |
| 116 // Perform scheme specific normalization. | |
| 117 if (scheme == "http" && port == 80) { | |
| 118 _port = 0; | |
| 119 } else if (scheme == "https" && port == 443) { | |
| 120 _port = 0; | |
| 121 } else { | |
| 122 _port = port; | |
| 123 } | |
| 124 } | |
| 125 | |
| 126 /* | |
| 127 * Returns the URI path split into its segments. Each of the | |
| 128 * segments in the returned list have been decoded. If the path is | |
| 129 * empty the empty list will be returned. | |
| 130 */ | |
| 131 List<String> get pathSegments { | |
| 132 if (path == "") return []; | |
|
floitsch
2013/05/24 20:37:27
const <String>[];
?
Søren Gjesse
2013/05/27 10:47:21
Done.
| |
| 133 return path.split("/").map(Uri.decodeComponent).toList(growable: false); | |
| 134 } | |
| 135 | |
| 136 /* | |
| 137 * Returns the URI query split into a map according to the rules | |
| 138 * specified for FORM post in the HTML 4.01 specification. Each key | |
| 139 * and value in the returned map have been decoded. If there is no | |
| 140 * query the empty map will be returned. | |
| 141 */ | |
| 142 Map<String, String> get queryParameters { | |
| 143 return query.split("&").fold({}, (map, element) { | |
| 144 int index = element.indexOf("="); | |
| 145 if (index == -1) { | |
| 146 if (!element.isEmpty) map[element] = ""; | |
| 147 } else if (index != 0) { | |
| 148 var key = element.substring(0, index); | |
| 149 var value = element.substring(index + 1, element.length); | |
|
floitsch
2013/05/24 20:37:27
substring doesn't need the second argument if it i
Søren Gjesse
2013/05/27 10:47:21
Done.
| |
| 150 map[Uri.decodeQueryComponent(key)] = decodeQueryComponent(value); | |
| 151 } | |
| 152 return map; | |
| 153 }); | |
| 154 } | |
| 155 | |
| 156 static String _makeScheme(String scheme) { | |
| 157 bool isSchemeLowerCharacter(int ch) { | |
| 158 return ch < 128 && | |
| 159 ((_schemeLowerTable[ch >> 4] & (1 << (ch & 0x0f))) != 0); | |
| 160 } | |
| 161 | |
| 162 bool isSchemeCharacter(int ch) { | |
| 163 return ch < 128 && ((_schemeTable[ch >> 4] & (1 << (ch & 0x0f))) != 0); | |
| 164 } | |
| 165 | |
| 166 if (scheme == null) return ""; | |
| 167 bool allLowercase = true; | |
| 168 int length = scheme.length; | |
| 169 for (int i = 0; i < length; i++) { | |
| 170 int codeUnit = scheme.codeUnitAt(i); | |
| 171 if (!isSchemeLowerCharacter(codeUnit)) { | |
| 172 if (isSchemeCharacter(codeUnit)) { | |
| 173 allLowercase = false; | |
| 174 } else { | |
| 175 throw new ArgumentError('Illegal scheme'); | |
|
floitsch
2013/05/24 20:37:27
Add the scheme: 'Illegal scheme: $scheme'.
Søren Gjesse
2013/05/27 10:47:21
Done.
| |
| 176 } | |
| 177 } | |
| 178 } | |
| 179 | |
| 180 return allLowercase ? scheme : scheme.toLowerCase(); | |
| 181 } | |
| 182 | |
| 183 static String _makePath(String path, List<String> pathSegments) { | |
| 184 if (path == null && pathSegments == null) return ""; | |
| 185 if (path != null && pathSegments != null) { | |
| 186 throw new ArgumentError('Both path and pathSegments specified'); | |
| 187 } | |
| 188 if (path != null) return _normalize(path); | |
| 189 | |
| 190 return pathSegments.map((s) => _uriEncode(_pathCharTable, s)).join("/"); | |
| 191 } | |
| 192 | |
| 193 static String _makeQuery(String query, Map<String, String> queryParameters) { | |
| 194 if (query == null && queryParameters == null) return ""; | |
| 195 if (query != null && queryParameters != null) { | |
| 196 throw new ArgumentError('Both query and queryParameters specified'); | |
| 197 } | |
| 198 if (query != null) return _normalize(query); | |
| 199 | |
| 200 var result = new StringBuffer(); | |
| 201 var first = true; | |
| 202 queryParameters.forEach((key, value) { | |
| 203 if (!first) { | |
| 204 result.write("&"); | |
| 205 } | |
| 206 first = false; | |
| 207 result.write(Uri.encodeQueryComponent(key)); | |
| 208 if (value != null && !value.isEmpty) { | |
| 209 result.write("="); | |
| 210 result.write(Uri.encodeQueryComponent(value)); | |
| 211 } | |
| 212 }); | |
| 213 return result.toString(); | |
| 214 } | |
| 215 | |
| 216 static String _makeFragment(String fragment) { | |
| 217 if (fragment == null) return ""; | |
| 218 return _normalize(fragment); | |
| 219 } | |
| 220 | |
| 221 static String _normalize(String component) { | |
| 222 bool isNormalizedHexDigit(int digit) { | |
| 223 return (_CharCode.ZERO <= digit && digit <= _CharCode.NINE) || | |
| 224 (_CharCode.UPPER_CASE_A <= digit && digit <= _CharCode.UPPER_CASE_F); | |
| 225 } | |
| 226 | |
| 227 bool isLowerCaseHexDigit(int digit) { | |
| 228 return _CharCode.LOWER_CASE_A <= digit && digit <= _CharCode.LOWER_CASE_F; | |
| 229 } | |
| 230 | |
| 231 bool isUnreserved(int ch) { | |
| 232 return ch < 128 && | |
| 233 ((_unreservedTable[ch >> 4] & (1 << (ch & 0x0f))) != 0); | |
| 234 } | |
| 235 | |
| 236 int normalizeHexDigit(int index) { | |
| 237 var codeUnit = component.codeUnitAt(index); | |
| 238 if (isLowerCaseHexDigit(codeUnit)) { | |
| 239 return codeUnit - 0x20; | |
| 240 } else if (!isNormalizedHexDigit(codeUnit)) { | |
| 241 throw new ArgumentError("Invalid URI component"); | |
| 242 } else { | |
| 243 return codeUnit; | |
| 244 } | |
| 245 } | |
| 246 | |
| 247 int decodeHexDigitPair(int index) { | |
| 248 int byte = 0; | |
| 249 for (int i = 0; i < 2; i++) { | |
| 250 var codeUnit = component.codeUnitAt(index + i); | |
| 251 if (_CharCode.ZERO <= codeUnit && codeUnit <= _CharCode.NINE) { | |
| 252 byte = byte * 16 + codeUnit - _CharCode.ZERO; | |
| 253 } else { | |
| 254 // Check ranges A-F (0x41-0x46) and a-f (0x61-0x66). | |
| 255 codeUnit |= 0x20; | |
| 256 if (_CharCode.LOWER_CASE_A <= codeUnit && | |
| 257 codeUnit <= _CharCode.LOWER_CASE_F) { | |
| 258 byte = byte * 16 + codeUnit - 0x57; | |
|
floitsch
2013/05/24 20:37:27
what's 0x57?
Søren Gjesse
2013/05/27 10:47:21
LOWERCASE_A - 10
Change the code to which should
| |
| 259 } else { | |
| 260 throw new ArgumentError("Invalid URI encoding"); | |
|
floitsch
2013/05/24 20:37:27
add reason.
Søren Gjesse
2013/05/27 10:47:21
Done.
| |
| 261 } | |
| 262 } | |
| 263 } | |
| 264 return byte; | |
| 265 } | |
| 266 | |
| 267 // Start building the normalized component string. | |
| 268 StringBuffer result; | |
| 269 int length = component.length; | |
| 270 int index = 0; | |
| 271 int prevIndex = 0; | |
| 272 while (index < length) { | |
| 273 | |
| 274 // Copy a part of the component string to the result. | |
| 275 fillResult() { | |
| 276 if (result == null) { | |
| 277 assert(prevIndex == 0); | |
| 278 result = new StringBuffer(component.substring(prevIndex, index)); | |
| 279 } else { | |
| 280 result.write(component.substring(prevIndex, index)); | |
| 281 } | |
| 282 } | |
| 283 | |
| 284 // Normalize percent encoding to uppercase and don't encode | |
| 285 // unreserved characters. | |
| 286 if (component.codeUnitAt(index) == _CharCode.PERCENT) { | |
| 287 if (length < index + 2) { | |
| 288 throw new ArgumentError("Invalid Uri component"); | |
|
floitsch
2013/05/24 20:37:27
add reason.
Søren Gjesse
2013/05/27 10:47:21
Done.
| |
| 289 } | |
| 290 | |
| 291 var codeUnit1 = component.codeUnitAt(index + 1); | |
| 292 var codeUnit2 = component.codeUnitAt(index + 2); | |
| 293 var decodedCodeUnit = decodeHexDigitPair(index + 1); | |
| 294 if (isNormalizedHexDigit(codeUnit1) && | |
| 295 isNormalizedHexDigit(codeUnit2) && | |
| 296 !isUnreserved(decodedCodeUnit)) { | |
| 297 index += 3; | |
| 298 } else { | |
| 299 fillResult(); | |
| 300 if (isUnreserved(decodedCodeUnit)) { | |
| 301 result.writeCharCode(decodedCodeUnit); | |
| 302 } else { | |
| 303 result.write("%"); | |
| 304 result.writeCharCode(normalizeHexDigit(index + 1)); | |
| 305 result.writeCharCode(normalizeHexDigit(index + 2)); | |
| 306 } | |
| 307 index += 3; | |
| 308 prevIndex = index; | |
| 309 } | |
| 310 } else { | |
| 311 index++; | |
| 312 } | |
| 313 } | |
| 314 assert(index == length); | |
| 315 | |
| 316 if (result == null) return component; | |
| 317 return result.toString(); | |
| 318 } | |
| 319 | |
| 320 static String _emptyIfNull(String val) => val != null ? val : ''; | |
| 321 | |
| 322 static int _parseIntOrZero(String val) { | |
| 323 if (val != null && val != '') { | |
| 324 return int.parse(val); | |
| 325 } else { | |
| 326 return 0; | |
| 327 } | |
| 328 } | |
| 329 | |
| 330 static String _eitherOf(String val1, String val2) { | |
| 331 if (val1 != null) return val1; | |
| 332 if (val2 != null) return val2; | |
| 333 return ''; | |
| 334 } | |
| 335 | |
| 336 // NOTE: This code was ported from: closure-library/closure/goog/uri/utils.js | |
| 337 static final RegExp _splitRe = new RegExp( | |
| 338 '^' | |
| 339 '(?:' | |
| 340 '([^:/?#.]+)' // scheme - ignore special characters | |
| 341 // used by other URL parts such as :, | |
| 342 // ?, /, #, and . | |
| 343 ':)?' | |
| 344 '(?://' | |
| 345 '(?:([^/?#]*)@)?' // userInfo | |
| 346 '(?:' | |
| 347 r'([\w\d\-\u0100-\uffff.%]*)' | |
| 348 // host - restrict to letters, | |
| 349 // digits, dashes, dots, percent | |
| 350 // escapes, and unicode characters. | |
| 351 '|' | |
| 352 // TODO(ajohnsen): Only allow a max number of parts? | |
| 353 r'\[([A-Fa-f0-9:.]*)\])' | |
| 354 // IPv6 host - restrict to hex, | |
| 355 // dot and colon. | |
| 356 '(?::([0-9]+))?' // port | |
| 357 ')?' | |
| 358 r'([^?#[]+)?' // path | |
| 359 r'(?:\?([^#]*))?' // query | |
| 360 '(?:#(.*))?' // fragment | |
| 361 r'$'); | |
| 362 | |
| 363 static const _COMPONENT_SCHEME = 1; | |
| 364 static const _COMPONENT_USER_INFO = 2; | |
| 365 static const _COMPONENT_HOST = 3; | |
| 366 static const _COMPONENT_HOST_IPV6 = 4; | |
| 367 static const _COMPONENT_PORT = 5; | |
| 368 static const _COMPONENT_PATH = 6; | |
| 369 static const _COMPONENT_QUERY_DATA = 7; | |
| 370 static const _COMPONENT_FRAGMENT = 8; | |
| 371 | |
| 372 /** | |
| 373 * Returns `true` if the URI is absolute. | |
| 374 */ | |
| 375 bool get isAbsolute { | |
| 376 if ("" == scheme) return false; | |
| 377 if ("" != fragment) return false; | |
| 378 return true; | |
| 379 | |
| 380 /* absolute-URI = scheme ":" hier-part [ "?" query ] | |
|
floitsch
2013/05/24 20:37:27
why is this comment here?
Søren Gjesse
2013/05/27 10:47:21
It was there in the dart:uri library. Removed it.
| |
| 381 * hier-part = "//" authority path-abempty | |
| 382 * / path-absolute | |
| 383 * / path-rootless | |
| 384 * / path-empty | |
| 385 * | |
| 386 * path = path-abempty ; begins with "/" or is empty | |
| 387 * / path-absolute ; begins with "/" but not "//" | |
| 388 * / path-noscheme ; begins with a non-colon segment | |
| 389 * / path-rootless ; begins with a segment | |
| 390 * / path-empty ; zero characters | |
| 391 * | |
| 392 * path-abempty = *( "/" segment ) | |
| 393 * path-absolute = "/" [ segment-nz *( "/" segment ) ] | |
| 394 * path-noscheme = segment-nz-nc *( "/" segment ) | |
| 395 * path-rootless = segment-nz *( "/" segment ) | |
| 396 * path-empty = 0<pchar> | |
| 397 * segment = *pchar | |
| 398 * segment-nz = 1*pchar | |
| 399 * segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" ) | |
| 400 * ; non-zero-length segment without any colon ":" | |
| 401 * | |
| 402 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" | |
| 403 */ | |
| 404 } | |
| 405 | |
| 406 Uri resolve(String uri) { | |
| 407 return resolveUri(Uri.parse(uri)); | |
| 408 } | |
| 409 | |
| 410 Uri resolveUri(Uri reference) { | |
| 411 // From RFC 3986. | |
| 412 String targetScheme; | |
| 413 String targetUserInfo; | |
| 414 String targetHost; | |
| 415 int targetPort; | |
| 416 String targetPath; | |
| 417 String targetQuery; | |
| 418 if (reference.scheme != "") { | |
| 419 targetScheme = reference.scheme; | |
| 420 targetUserInfo = reference.userInfo; | |
| 421 targetHost = reference.host; | |
| 422 targetPort = reference.port; | |
| 423 targetPath = removeDotSegments(reference.path); | |
| 424 targetQuery = reference.query; | |
| 425 } else { | |
| 426 if (reference.hasAuthority) { | |
| 427 targetUserInfo = reference.userInfo; | |
| 428 targetHost = reference.host; | |
| 429 targetPort = reference.port; | |
| 430 targetPath = removeDotSegments(reference.path); | |
| 431 targetQuery = reference.query; | |
| 432 } else { | |
| 433 if (reference.path == "") { | |
| 434 targetPath = this.path; | |
| 435 if (reference.query != "") { | |
| 436 targetQuery = reference.query; | |
| 437 } else { | |
| 438 targetQuery = this.query; | |
| 439 } | |
| 440 } else { | |
| 441 if (reference.path.startsWith("/")) { | |
| 442 targetPath = removeDotSegments(reference.path); | |
| 443 } else { | |
| 444 targetPath = removeDotSegments(merge(this.path, reference.path)); | |
| 445 } | |
| 446 targetQuery = reference.query; | |
| 447 } | |
| 448 targetUserInfo = this.userInfo; | |
| 449 targetHost = this.host; | |
| 450 targetPort = this.port; | |
| 451 } | |
| 452 targetScheme = this.scheme; | |
| 453 } | |
| 454 return new Uri(scheme: targetScheme, | |
| 455 userInfo: targetUserInfo, | |
|
floitsch
2013/05/24 20:37:27
indentation.
Søren Gjesse
2013/05/27 10:47:21
Done.
| |
| 456 host: targetHost, | |
| 457 port: targetPort, | |
| 458 path: targetPath, | |
| 459 query: targetQuery, | |
| 460 fragment: reference.fragment); | |
| 461 } | |
| 462 | |
| 463 bool get hasAuthority { | |
| 464 return (userInfo != "") || (host != "") || (port != 0); | |
|
floitsch
2013/05/24 20:37:27
why is the authority linked to a non-zery port?
Søren Gjesse
2013/05/27 10:47:21
Good catch!
An authority always have a host. Chan
| |
| 465 } | |
| 466 | |
| 467 /** | |
| 468 * For http/https schemes returns URI's [origin][] - scheme://host:port. | |
|
floitsch
2013/05/24 20:37:27
Returns the URI's [origin][] for http/https scheme
Søren Gjesse
2013/05/27 10:47:21
Done.
| |
| 469 * For all other schemes throws StateError. | |
| 470 * [origin]: http://www.w3.org/TR/2011/WD-html5-20110405/origin-0.html#origin | |
| 471 */ | |
| 472 String get origin { | |
| 473 if (scheme == "") { | |
| 474 throw new StateError("Cannot use origin without a scheme"); | |
|
floitsch
2013/05/24 20:37:27
give information: "Cannot use origin without a sch
Søren Gjesse
2013/05/27 10:47:21
Done.
| |
| 475 } | |
| 476 if (scheme != "http" && scheme != "https") { | |
| 477 throw new StateError( | |
| 478 "origin is applicable to http/https schemes only. Not \'$scheme\'"); | |
| 479 } | |
| 480 StringBuffer sb = new StringBuffer(); | |
| 481 sb.write(scheme); | |
| 482 sb.write(":"); | |
| 483 if (host == null || host == "") { | |
|
floitsch
2013/05/24 20:37:27
Move check up to the checks.
Søren Gjesse
2013/05/27 10:47:21
Done.
| |
| 484 throw new StateError("Cannot use origin without a host"); | |
| 485 } | |
| 486 | |
| 487 sb.write("//"); | |
| 488 sb.write(host); | |
| 489 if (port != 0) { | |
| 490 sb.write(":"); | |
| 491 sb.write(port); | |
| 492 } | |
| 493 return sb.toString(); | |
|
floitsch
2013/05/24 20:37:27
No need for StringBuffers.
if (port == 0) return "
Søren Gjesse
2013/05/27 10:47:21
Good point. Changed.
| |
| 494 } | |
| 495 | |
| 496 String toString() { | |
| 497 StringBuffer sb = new StringBuffer(); | |
| 498 _addIfNonEmpty(sb, scheme, scheme, ':'); | |
| 499 if (hasAuthority || (scheme == "file")) { | |
| 500 sb.write("//"); | |
| 501 _addIfNonEmpty(sb, userInfo, userInfo, "@"); | |
| 502 sb.write(host == null ? "null" : | |
| 503 host.contains(':') ? '[$host]' : host); | |
| 504 if (port != 0) { | |
| 505 sb.write(":"); | |
| 506 sb.write(port.toString()); | |
| 507 } | |
| 508 } | |
| 509 sb.write(path == null ? "null" : path); | |
|
floitsch
2013/05/24 20:37:27
isn't the path null-check redundant?
Søren Gjesse
2013/05/27 10:47:21
It is - removed.
| |
| 510 _addIfNonEmpty(sb, query, "?", query); | |
| 511 _addIfNonEmpty(sb, fragment, "#", fragment); | |
| 512 return sb.toString(); | |
| 513 } | |
| 514 | |
| 515 bool operator==(other) { | |
| 516 if (other is! Uri) return false; | |
| 517 Uri uri = other; | |
| 518 return scheme == uri.scheme && | |
| 519 userInfo == uri.userInfo && | |
| 520 host == uri.host && | |
| 521 port == uri.port && | |
| 522 path == uri.path && | |
| 523 query == uri.query && | |
| 524 fragment == uri.fragment; | |
| 525 } | |
| 526 | |
| 527 int get hashCode { | |
| 528 int combine(part, current) { | |
| 529 // The sum is truncated to 30 bits to make sure it fits into a Smi. | |
| 530 return (current * 31 + part.hashCode) & 0x3FFFFFFF; | |
| 531 } | |
| 532 return combine(scheme, combine(userInfo, combine(host, combine(port, | |
| 533 combine(path, combine(query, combine(fragment, 1))))))); | |
| 534 } | |
| 535 | |
| 536 static void _addIfNonEmpty(StringBuffer sb, String test, | |
| 537 String first, String second) { | |
| 538 if ("" != test) { | |
| 539 sb.write(first == null ? "null" : first); | |
| 540 sb.write(second == null ? "null" : second); | |
| 541 } | |
| 542 } | |
| 543 | |
| 544 /** | |
| 545 * A javaScript-like URI component encoder, this encodes a URI | |
| 546 * [component] by replacing each instance of certain characters by | |
|
floitsch
2013/05/24 20:37:27
If the set of characters is fixed, add it at the e
Søren Gjesse
2013/05/27 10:47:21
Done.
| |
| 547 * one, two, three, or four escape sequences representing the UTF-8 | |
| 548 * encoding of the character (will only be four escape sequences for | |
| 549 * characters composed of two "surrogate" characters). To avoid | |
| 550 * unexpected requests to the server, you should call | |
| 551 * Uri.encodeComponent on any user-entered parameters that will be | |
| 552 * passed as part of a URI. | |
| 553 * | |
| 554 * For encoding the query part consider using | |
| 555 * [encodeQueryComponent]. | |
| 556 * | |
| 557 * When manually encoding path segments or query components remember | |
| 558 * to encode each part separately before building the path or query | |
| 559 * string. | |
| 560 * | |
| 561 * To avoid the need for explicitly encoding use the [pathSegments] | |
| 562 * and [queryParameters] optional named arguments when constructing | |
| 563 * a Uri. | |
| 564 */ | |
| 565 static String encodeComponent(String component) { | |
| 566 return _uriEncode(_unreserved2396Table, component); | |
| 567 } | |
| 568 | |
| 569 /* | |
| 570 * Encode [component] according to the HTML 4.01 rules for encoding | |
| 571 * the posting of a HTML form as a query string component. Spaces | |
| 572 * will be replaced with plus and all characters except for | |
| 573 * uppercase and lowercase letters, decimal digits, hyphen, period, | |
| 574 * underscore and tilde will be encoded. Note that the set of | |
| 575 * characters encoded is a superset of what HTML 4.01 says as it | |
| 576 * refers to RFC 1738 for reserved characters. | |
| 577 * | |
| 578 * When manually encoding query components remember to encode each | |
| 579 * part separately before building the query string. | |
| 580 * | |
| 581 * See http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2 for more | |
|
floitsch
2013/05/24 20:37:27
I think there is a way to make this a link.
Søren Gjesse
2013/05/27 10:47:21
I will postpone the adding of proper links to a se
| |
| 582 * details. | |
| 583 */ | |
| 584 static String encodeQueryComponent(String component) { | |
| 585 return _uriEncode(_unreservedTable, component, spaceToPlus: true); | |
| 586 } | |
| 587 | |
| 588 /** | |
| 589 * A javaScript-like URI component decoder. | |
|
floitsch
2013/05/24 20:37:27
Is it JavaScript like, or more general? In other w
Søren Gjesse
2013/05/27 10:47:21
Rephrased the comment and explained more precisely
| |
| 590 */ | |
| 591 static String decodeComponent(String encodedComponent) { | |
| 592 return _uriDecode(encodedComponent); | |
| 593 } | |
| 594 | |
| 595 static String decodeQueryComponent(String encodedComponent) { | |
| 596 return _uriDecode(encodedComponent, plusToSpace: true); | |
| 597 } | |
| 598 | |
| 599 /** | |
| 600 * A JavaScript-like URI encoder. Encodes a full URI string by | |
| 601 * replacing each instance of certain characters by one, two, three, | |
| 602 * or four escape sequences representing the UTF-8 encoding of the | |
| 603 * character (will only be four escape sequences for characters | |
| 604 * composed of two "surrogate" characters). This assumes that [uri] | |
| 605 * is a complete URI, so does not encode reserved characters that | |
| 606 * have special meaning in the URI: [:#;,/?:@&=+\$:] It returns the | |
|
floitsch
2013/05/24 20:37:27
nit (personal preference): `#;,/?:@&=+\$`.
Søren Gjesse
2013/05/27 10:47:21
Done.
| |
| 607 * escaped URI. | |
| 608 */ | |
| 609 static String encodeFull(String uri) { | |
| 610 return _uriEncode(_encodeFullTable, uri); | |
| 611 } | |
| 612 | |
| 613 /** | |
| 614 * A JavaScript-like URI decoder. Decodes a full URI string. This is | |
|
floitsch
2013/05/24 20:37:27
Start with what it does: "Decodes ...".
Make one s
Søren Gjesse
2013/05/27 10:47:21
Rephrased the comment and explained more precisely
| |
| 615 * an implementation of JavaScript's decodeURIComponent function. | |
| 616 * Decodes a Uniform Resource Identifier [uri] previously created by | |
| 617 * encodeURI or by a similar routine. It replaces each escape sequence | |
| 618 * in [uri] with the character that it represents. | |
| 619 */ | |
| 620 static String decodeFull(String uri) { | |
| 621 return _uriDecode(uri); | |
| 622 } | |
| 623 | |
| 624 // Tables of char-codes organized as a bit vector of 128 bits where | |
| 625 // each bit indicate whether a character code on the 0-127 needs to | |
| 626 // be escaped or not. | |
| 627 | |
| 628 // The unreserved characters of RFC 3986. | |
| 629 static const _unreservedTable = const [ | |
| 630 // LSB MSB | |
| 631 // | | | |
| 632 0x0000, // 0x00 - 0x0f 0000000000000000 | |
| 633 0x0000, // 0x10 - 0x1f 0000000000000000 | |
| 634 // -. | |
| 635 0x6000, // 0x20 - 0x2f 0000000000000110 | |
| 636 // 0123456789 | |
| 637 0x03ff, // 0x30 - 0x3f 1111111111000000 | |
| 638 // ABCDEFGHIJKLMNO | |
| 639 0xfffe, // 0x40 - 0x4f 0111111111111111 | |
| 640 // PQRSTUVWXYZ _ | |
| 641 0x87ff, // 0x50 - 0x5f 1111111111100001 | |
| 642 // abcdefghijklmno | |
| 643 0xfffe, // 0x60 - 0x6f 0111111111111111 | |
| 644 // pqrstuvwxyz ~ | |
| 645 0x47ff]; // 0x70 - 0x7f 1111111111100010 | |
| 646 | |
| 647 // The unreserved characters of RFC 2396. | |
| 648 static const _unreserved2396Table = const [ | |
| 649 // LSB MSB | |
| 650 // | | | |
| 651 0x0000, // 0x00 - 0x0f 0000000000000000 | |
| 652 0x0000, // 0x10 - 0x1f 0000000000000000 | |
| 653 // ! '()* -. | |
| 654 0x6782, // 0x20 - 0x2f 0100000111100110 | |
| 655 // 0123456789 | |
| 656 0x03ff, // 0x30 - 0x3f 1111111111000000 | |
| 657 // ABCDEFGHIJKLMNO | |
| 658 0xfffe, // 0x40 - 0x4f 0111111111111111 | |
| 659 // PQRSTUVWXYZ _ | |
| 660 0x87ff, // 0x50 - 0x5f 1111111111100001 | |
| 661 // abcdefghijklmno | |
| 662 0xfffe, // 0x60 - 0x6f 0111111111111111 | |
| 663 // pqrstuvwxyz ~ | |
| 664 0x47ff]; // 0x70 - 0x7f 1111111111100010 | |
| 665 | |
| 666 // Table of reserved characters specified by ECMAScript 5. | |
| 667 static const _encodeFullTable = const [ | |
| 668 // LSB MSB | |
| 669 // | | | |
| 670 0x0000, // 0x00 - 0x0f 0000000000000000 | |
| 671 0x0000, // 0x10 - 0x1f 0000000000000000 | |
| 672 // ! #$ &'()*+,-./ | |
| 673 0xf7da, // 0x20 - 0x2f 0101101111101111 | |
| 674 // 0123456789:; = ? | |
| 675 0xafff, // 0x30 - 0x3f 1111111111110101 | |
| 676 // @ABCDEFGHIJKLMNO | |
| 677 0xffff, // 0x40 - 0x4f 1111111111111111 | |
| 678 // PQRSTUVWXYZ _ | |
| 679 0x87ff, // 0x50 - 0x5f 1111111111100001 | |
| 680 // abcdefghijklmno | |
| 681 0xfffe, // 0x60 - 0x6f 0111111111111111 | |
| 682 // pqrstuvwxyz ~ | |
| 683 0x47ff]; // 0x70 - 0x7f 1111111111100010 | |
| 684 | |
| 685 // Characters allowed in the scheme. | |
| 686 static const _schemeTable = const [ | |
| 687 // LSB MSB | |
| 688 // | | | |
| 689 0x0000, // 0x00 - 0x0f 0000000000000000 | |
| 690 0x0000, // 0x10 - 0x1f 0000000000000000 | |
| 691 // + -. | |
| 692 0x6800, // 0x20 - 0x2f 0000000000010110 | |
| 693 // 0123456789 | |
| 694 0x03ff, // 0x30 - 0x3f 1111111111000000 | |
| 695 // ABCDEFGHIJKLMNO | |
| 696 0xfffe, // 0x40 - 0x4f 0111111111111111 | |
| 697 // PQRSTUVWXYZ | |
| 698 0x07ff, // 0x50 - 0x5f 1111111111100001 | |
| 699 // abcdefghijklmno | |
| 700 0xfffe, // 0x60 - 0x6f 0111111111111111 | |
| 701 // pqrstuvwxyz | |
| 702 0x07ff]; // 0x70 - 0x7f 1111111111100010 | |
| 703 | |
| 704 // Characters allowed in scheme except for upper case letters. | |
| 705 static const _schemeLowerTable = const [ | |
| 706 // LSB MSB | |
| 707 // | | | |
| 708 0x0000, // 0x00 - 0x0f 0000000000000000 | |
| 709 0x0000, // 0x10 - 0x1f 0000000000000000 | |
| 710 // + -. | |
| 711 0x6800, // 0x20 - 0x2f 0000000000010110 | |
| 712 // 0123456789 | |
| 713 0x03ff, // 0x30 - 0x3f 1111111111000000 | |
| 714 // | |
| 715 0x0000, // 0x40 - 0x4f 0111111111111111 | |
| 716 // | |
| 717 0x0000, // 0x50 - 0x5f 1111111111100001 | |
| 718 // abcdefghijklmno | |
| 719 0xfffe, // 0x60 - 0x6f 0111111111111111 | |
| 720 // pqrstuvwxyz | |
| 721 0x07ff]; // 0x70 - 0x7f 1111111111100010 | |
| 722 | |
| 723 // Sub delimiter characters combined with unreserved as of 3986. | |
| 724 // sub-delims = "!" / "$" / "&" / "'" / "(" / ")" | |
| 725 // / "*" / "+" / "," / ";" / "=" | |
| 726 // RFC 3986 section 2.3. | |
| 727 // unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" | |
| 728 static const _subDelimitersTable = const [ | |
| 729 // LSB MSB | |
| 730 // | | | |
| 731 0x0000, // 0x00 - 0x0f 0000000000000000 | |
| 732 0x0000, // 0x10 - 0x1f 0000000000000000 | |
| 733 // ! $ &'()*+,-. | |
| 734 0x7fd2, // 0x20 - 0x2f 0100101111111110 | |
| 735 // 0123456789 ; = | |
| 736 0x2bff, // 0x30 - 0x3f 1111111111010100 | |
| 737 // ABCDEFGHIJKLMNO | |
| 738 0xfffe, // 0x40 - 0x4f 0111111111111111 | |
| 739 // PQRSTUVWXYZ _ | |
| 740 0x87ff, // 0x50 - 0x5f 1111111111100001 | |
| 741 // abcdefghijklmno | |
| 742 0xfffe, // 0x60 - 0x6f 0111111111111111 | |
| 743 // pqrstuvwxyz ~ | |
| 744 0x47ff]; // 0x70 - 0x7f 1111111111100010 | |
| 745 | |
| 746 // Characters allowed in the path as of RFC 3986. | |
| 747 // RFC 3986 section 3.3. | |
| 748 // pchar = unreserved / pct-encoded / sub-delims / ":" / "@" | |
| 749 static const _pathCharTable = const [ | |
| 750 // LSB MSB | |
| 751 // | | | |
| 752 0x0000, // 0x00 - 0x0f 0000000000000000 | |
| 753 0x0000, // 0x10 - 0x1f 0000000000000000 | |
| 754 // ! $ &'()*+,-. | |
| 755 0x7fd2, // 0x20 - 0x2f 0100101111111110 | |
| 756 // 0123456789:; = | |
| 757 0x2fff, // 0x30 - 0x3f 1111111111110100 | |
| 758 // @ABCDEFGHIJKLMNO | |
| 759 0xffff, // 0x40 - 0x4f 1111111111111111 | |
| 760 // PQRSTUVWXYZ _ | |
| 761 0x87ff, // 0x50 - 0x5f 1111111111100001 | |
| 762 // abcdefghijklmno | |
| 763 0xfffe, // 0x60 - 0x6f 0111111111111111 | |
| 764 // pqrstuvwxyz ~ | |
| 765 0x47ff]; // 0x70 - 0x7f 1111111111100010 | |
| 766 | |
| 767 // Characters allowed in the query as of RFC 3986. | |
| 768 // RFC 3986 section 3.4. | |
| 769 // query = *( pchar / "/" / "?" ) | |
| 770 static const _queryCharTable = const [ | |
| 771 // LSB MSB | |
| 772 // | | | |
| 773 0x0000, // 0x00 - 0x0f 0000000000000000 | |
| 774 0x0000, // 0x10 - 0x1f 0000000000000000 | |
| 775 // ! $ &'()*+,-./ | |
| 776 0xffd2, // 0x20 - 0x2f 0100101111111111 | |
| 777 // 0123456789:; = ? | |
| 778 0xafff, // 0x30 - 0x3f 1111111111110101 | |
| 779 // @ABCDEFGHIJKLMNO | |
| 780 0xffff, // 0x40 - 0x4f 1111111111111111 | |
| 781 // PQRSTUVWXYZ _ | |
| 782 0x87ff, // 0x50 - 0x5f 1111111111100001 | |
| 783 // abcdefghijklmno | |
| 784 0xfffe, // 0x60 - 0x6f 0111111111111111 | |
| 785 // pqrstuvwxyz ~ | |
| 786 0x47ff]; // 0x70 - 0x7f 1111111111100010 | |
| 787 } | |
| OLD | NEW |