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, as specified by RFC-3986, http://tools.ietf.org/html/rfc3986. | 8 * A parsed URI, as specified by RFC-3986, http://tools.ietf.org/html/rfc3986. |
| 9 */ | 9 */ |
| 10 class Uri { | 10 class Uri { |
| (...skipping 245 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 256 | 256 |
| 257 return new Uri(scheme: scheme, | 257 return new Uri(scheme: scheme, |
| 258 userInfo: userInfo, | 258 userInfo: userInfo, |
| 259 host: host, | 259 host: host, |
| 260 port: port, | 260 port: port, |
| 261 pathSegments: unencodedPath.split("/"), | 261 pathSegments: unencodedPath.split("/"), |
| 262 queryParameters: queryParameters); | 262 queryParameters: queryParameters); |
| 263 } | 263 } |
| 264 | 264 |
| 265 /** | 265 /** |
| 266 * Creates a new `file` URI from an absolute or relative file path. | |
| 267 * | |
| 268 * The file path is passed in [path]. | |
| 269 * | |
| 270 * This path is interpreted using either Windows or non-Windows | |
| 271 * semantics. For Windows semantics the backslash separator is used | |
|
ahe
2013/07/29 15:30:16
For Windows semantics, one can use either backslas
Bill Hesse
2013/07/30 11:29:32
Forward slash must be supported as a Windows path
Søren Gjesse
2013/07/30 13:41:26
Done.
Søren Gjesse
2013/07/30 13:41:26
Done.
| |
| 272 * to separate path segments. For non-Windows semantics the slash is | |
| 273 * used to separate path segments. If the path starts with a path | |
| 274 * separator an absolute URI is created. Otherwise a relative URI is | |
| 275 * created. One exception from this rule is that with Windows | |
| 276 * semantics and a path which starts with a drive letter then an | |
| 277 * absolute URI is created. | |
| 278 * | |
| 279 * The default for whether to use Windows or non-Windows semantics | |
| 280 * determined from the platform Dart is running on. When running in | |
| 281 * the standalone VM this is detected by the VM based on the | |
| 282 * operating system. When running in the browser non-Windows | |
| 283 * semantics is always used. | |
| 284 * | |
| 285 * To override the automatic detection of which semantics to use pass | |
| 286 * a value for [windowsPath]. Passing `true` will use Windows | |
| 287 * semantics and passing `false` will use non-Windows semantics. | |
| 288 * | |
| 289 * If the path passed is not a legal file path [ArgumentError] is thrown. | |
| 290 */ | |
|
floitsch
2013/07/29 15:16:54
Give some examples.
Søren Gjesse
2013/07/30 13:41:26
Done.
| |
| 291 factory Uri.file(String path, {bool windowsPath}) { | |
|
floitsch
2013/07/29 15:16:54
doesn't sound like a boolean name, but so far have
Søren Gjesse
2013/07/30 13:41:26
Agree that this is a pretty bad name - sounds like
Bill Hesse
2013/07/30 15:03:15
I think that just "windows:" would be a great name
| |
| 292 windowsPath = windowsPath == null ? Uri._isWindows : windowsPath; | |
| 293 return windowsPath ? _makeWindowsFileUrl(path) : _makeFileUri(path); | |
| 294 } | |
| 295 | |
| 296 external static bool get _isWindows; | |
|
Bill Hesse
2013/07/30 11:29:32
Could you call this something else (like _isWindow
Søren Gjesse
2013/07/30 13:41:26
Good point. Done.
| |
| 297 | |
| 298 // Characters which are not allowed in file names on Windows. | |
| 299 static final _windowsPathReservedCharacters = [ | |
| 300 _DOUBLE_QUOTE, | |
| 301 _ASTERISK, | |
| 302 _SLASH, | |
| 303 _COLON, // Colon is only allowed after drive letter. | |
| 304 _LESS, | |
| 305 _GREATER, | |
| 306 _QUESTION, | |
| 307 _BACKSLASH, // Backslash can only be separator. | |
| 308 _BAR, | |
| 309 ]; | |
| 310 | |
| 311 static _checkWindowsPathReservedCharacters(List<String> segments, | |
| 312 bool argumentError, | |
| 313 [int firstSegment = 0]) { | |
| 314 for (int i = firstSegment; i < segments.length; i++) { | |
| 315 var segment = segments[i]; | |
| 316 for (int j = 0; j < segment.length; j++) { | |
| 317 if (_windowsPathReservedCharacters.contains(segment.codeUnitAt(j))) { | |
| 318 if (argumentError) { | |
| 319 throw new ArgumentError("Illegal path character ${segment[j]}"); | |
|
floitsch
2013/07/29 15:16:54
You could maybe use FormatException if you think t
Søren Gjesse
2013/07/30 13:41:26
Shouldn't we stick to argument checking throwing A
Bill Hesse
2013/07/30 15:03:15
I think that a badly formatted file path should be
| |
| 320 } else { | |
| 321 throw new UnsupportedError("Illegal path character ${segment[j]}"); | |
|
ahe
2013/07/29 15:30:16
StateError?
Søren Gjesse
2013/07/30 13:41:26
I did also think about this. However as the Uri ob
| |
| 322 } | |
| 323 } | |
| 324 } | |
| 325 } | |
| 326 } | |
| 327 | |
| 328 static _checkWindowsDriveLetter(int charCode, bool argumentError) { | |
| 329 if ((_UPPER_CASE_A <= charCode && charCode <= _UPPER_CASE_Z) || | |
| 330 (_LOWER_CASE_A <= charCode && charCode <= _LOWER_CASE_Z)) { | |
| 331 return; | |
| 332 } | |
| 333 if (argumentError) { | |
| 334 throw new ArgumentError("Illegal drive letter " + | |
|
floitsch
2013/07/29 15:16:54
ditto.
Søren Gjesse
2013/07/30 13:41:26
See above.
| |
| 335 new String.fromCharCode(charCode)); | |
| 336 } else { | |
| 337 throw new UnsupportedError("Illegal drive letter " + | |
|
ahe
2013/07/29 15:30:16
StateError?
Søren Gjesse
2013/07/30 13:41:26
See above.
| |
| 338 new String.fromCharCode(charCode)); | |
| 339 } | |
| 340 } | |
| 341 | |
| 342 static _makeFileUri(String path) { | |
| 343 String sep = "/"; | |
| 344 if (path.length > 0 && path[0] == sep) { | |
| 345 // Absolute file:// URI. | |
| 346 return new Uri(scheme: "file", pathSegments: path.split(sep)); | |
| 347 } else { | |
| 348 // Relative URI. | |
| 349 return new Uri(pathSegments: path.split(sep)); | |
|
ahe
2013/07/29 15:30:16
Check that this works as you expect for the empty
Søren Gjesse
2013/07/30 13:41:26
Added tests with empty path.
| |
| 350 } | |
| 351 } | |
| 352 | |
| 353 static _makeWindowsFileUrl(String path) { | |
|
ahe
2013/07/29 15:30:16
I'm not sure this works if the path is using / ins
Søren Gjesse
2013/07/30 13:41:26
It did not, but changed it so it now does. Also up
| |
| 354 String sep = "\\"; | |
| 355 if (path.length > 1 && path[1] == ":") { | |
| 356 _checkWindowsDriveLetter(path.codeUnitAt(0), true); | |
| 357 if (path.length == 2 || path.codeUnitAt(2) != _BACKSLASH) { | |
|
Bill Hesse
2013/07/30 11:29:32
Why not path.length >= 2?
Søren Gjesse
2013/07/30 13:41:26
Not sure I understand. If path.length is == 2 I ca
Bill Hesse
2013/07/30 15:03:15
Nevermind - I read || as && and 2 as 3.
| |
| 358 throw new ArgumentError( | |
| 359 "Windows paths with drive letter must be absolute"); | |
| 360 } | |
|
Bill Hesse
2013/07/30 11:29:32
The windows paths "\foo\bar" and "c:" (and even "c
Søren Gjesse
2013/07/30 13:41:26
I think we need to accept the first, as it is wide
| |
| 361 // Absolute file://C:/ URI. | |
| 362 var pathSegments = path.split(sep); | |
| 363 _checkWindowsPathReservedCharacters(pathSegments, true, 1); | |
| 364 return new Uri(scheme: "file", pathSegments: pathSegments); | |
| 365 } | |
| 366 | |
| 367 if (path.length > 0 && path[0] == sep) { | |
| 368 if (path.length > 1 && path[1] == sep) { | |
| 369 // Absolute file:// URI with host. | |
| 370 int pathStart = path.indexOf("\\", 2); | |
| 371 String hostPart = | |
| 372 pathStart == -1 ? path.substring(2) : path.substring(2, pathStart); | |
| 373 String pathPart = | |
| 374 pathStart == -1 ? "" : path.substring(pathStart + 1); | |
| 375 var pathSegments = pathPart.split(sep); | |
| 376 _checkWindowsPathReservedCharacters(pathSegments, true); | |
| 377 return new Uri( | |
| 378 scheme: "file", host: hostPart, pathSegments: pathSegments); | |
| 379 } else { | |
| 380 // Absolute file:// URI. | |
| 381 var pathSegments = path.split(sep); | |
| 382 _checkWindowsPathReservedCharacters(pathSegments, true); | |
| 383 return new Uri(scheme: "file", pathSegments: pathSegments); | |
| 384 } | |
| 385 } else { | |
| 386 // Relative URI. | |
| 387 var pathSegments = path.split(sep); | |
| 388 _checkWindowsPathReservedCharacters(pathSegments, true); | |
| 389 return new Uri(pathSegments: pathSegments); | |
| 390 } | |
| 391 } | |
| 392 | |
| 393 /** | |
| 266 * Returns the URI path split into its segments. Each of the | 394 * Returns the URI path split into its segments. Each of the |
| 267 * segments in the returned list have been decoded. If the path is | 395 * segments in the returned list have been decoded. If the path is |
| 268 * empty the empty list will be returned. A leading slash `/` does | 396 * empty the empty list will be returned. A leading slash `/` does |
| 269 * not affect the segments returned. | 397 * not affect the segments returned. |
| 270 * | 398 * |
| 271 * The returned list is unmodifiable and will throw [UnsupportedError] on any | 399 * The returned list is unmodifiable and will throw [UnsupportedError] on any |
| 272 * calls that would mutate it. | 400 * calls that would mutate it. |
| 273 */ | 401 */ |
| 274 List<String> get pathSegments { | 402 List<String> get pathSegments { |
| 275 if (_pathSegments == null) { | 403 if (_pathSegments == null) { |
| (...skipping 368 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 644 throw new StateError("Cannot use origin without a scheme: $this"); | 772 throw new StateError("Cannot use origin without a scheme: $this"); |
| 645 } | 773 } |
| 646 if (scheme != "http" && scheme != "https") { | 774 if (scheme != "http" && scheme != "https") { |
| 647 throw new StateError( | 775 throw new StateError( |
| 648 "Origin is only applicable schemes http and https: $this"); | 776 "Origin is only applicable schemes http and https: $this"); |
| 649 } | 777 } |
| 650 if (port == 0) return "$scheme://$host"; | 778 if (port == 0) return "$scheme://$host"; |
| 651 return "$scheme://$host:$port"; | 779 return "$scheme://$host:$port"; |
| 652 } | 780 } |
| 653 | 781 |
| 782 /** | |
| 783 * Returns the file path from a file URI. | |
| 784 * | |
| 785 * The returned path has either Windows or non-Windows | |
| 786 * semantics. For Windows semantics the backslash separator is used | |
| 787 * to separate path segments. For non Windows semantics the slash is | |
| 788 * used to separate path segments. If the URI is absolute the path | |
|
ahe
2013/07/29 15:30:16
An absolute URI means that the URI has a scheme. N
Søren Gjesse
2013/07/30 13:41:26
As
print(Uri.parse('file:fisk.txt'))
prints
| |
| 789 * statrs with a path separator unless Windows semantics is used and | |
|
ahe
2013/07/29 15:30:16
statrs -> starts
Søren Gjesse
2013/07/30 13:41:26
Done.
| |
| 790 * the first path segment is a drive letter. When Windows semantics | |
| 791 * is used a host component in the uri in interpreted as a file | |
| 792 * server and a UNC path is returned. | |
| 793 * | |
| 794 * The default for whether to use Windows or non-Windows semantics | |
| 795 * determined from the platform Dart is running on. When running in | |
| 796 * the standalone VM this is detected by the VM based on the | |
| 797 * operating system. When running in the browser non-Windows | |
| 798 * semantics is always used. | |
| 799 * | |
| 800 * To override the automatic detection of which semantics to use pass | |
| 801 * a value for [windowsPath]. Passing `true` will use Windows | |
| 802 * semantics and passing `false` will use non-Windows semantics. | |
| 803 * | |
| 804 * If the URI is not a file URI calling this throws | |
| 805 * [UnsupportedError]. | |
|
ahe
2013/07/29 15:30:16
StateError?
Søren Gjesse
2013/07/30 13:41:26
See above.
| |
| 806 * | |
| 807 * If the URI cannot be converted to a file path calling this throws | |
| 808 * [UnsupportedError]. | |
|
ahe
2013/07/29 15:30:16
StateError?
Søren Gjesse
2013/07/30 13:41:26
See above.
| |
| 809 */ | |
|
floitsch
2013/07/29 15:16:54
add examples.
Søren Gjesse
2013/07/30 13:41:26
Done.
| |
| 810 String toFilePath({bool windowsPath}) { | |
| 811 if (scheme != "" && scheme != "file") { | |
| 812 throw new UnsupportedError( | |
| 813 "Cannot extract a file path from a $scheme URI"); | |
| 814 } | |
| 815 if (windowsPath == null) windowsPath = _isWindows; | |
| 816 return windowsPath ? _toWindowsFilePath() : _toFilePath(); | |
| 817 } | |
| 818 | |
| 819 String _toFilePath() { | |
| 820 if (host != "") { | |
| 821 throw new UnsupportedError( | |
|
ahe
2013/07/29 15:30:16
StateError?
Søren Gjesse
2013/07/30 13:41:26
See above.
| |
| 822 "Cannot extract a non-Windows file path from a file URI " | |
| 823 "with an authority"); | |
| 824 } | |
| 825 var result = new StringBuffer(); | |
| 826 if (isAbsolute) result.write("/"); | |
|
ahe
2013/07/29 15:30:16
isAbsolute doesn't mean what you think it means.
Søren Gjesse
2013/07/30 13:41:26
I think this is the right thing to do. Parsing fil
| |
| 827 result.writeAll(pathSegments, "/"); | |
| 828 return result.toString(); | |
| 829 } | |
| 830 | |
| 831 String _toWindowsFilePath() { | |
| 832 bool hasDriveLetter = false; | |
| 833 var segments = pathSegments; | |
| 834 if (segments.length > 0 && | |
| 835 segments[0].length >= 2 && | |
| 836 segments[0].codeUnitAt(1) == _COLON) { | |
| 837 _checkWindowsDriveLetter(segments[0].codeUnitAt(0), false); | |
|
ahe
2013/07/29 15:30:16
Seems like you accept stuff like:
c:b
Bill Hesse
2013/07/30 11:29:32
These cases have really weird semantics, and I thi
Søren Gjesse
2013/07/30 13:41:26
Yes, file:///c:d is turned into path c:\d on Windo
Søren Gjesse
2013/07/30 13:41:26
I am OK with making it an error.
Bill Hesse
2013/07/30 15:03:15
I am now more convinced by your argument that URI
| |
| 838 if (segments[0].length > 2) { | |
|
ahe
2013/07/29 15:30:16
And then you just add another segment for 'b'?
Søren Gjesse
2013/07/30 13:41:26
See above.
| |
| 839 var originalSegments = segments; | |
| 840 segments = new List<String>(originalSegments.length + 1); | |
| 841 segments[0] = originalSegments[0].substring(0, 2); | |
| 842 segments[1] = originalSegments[0].substring(2); | |
| 843 segments.setRange(2, segments.length, originalSegments, 1); | |
| 844 } | |
| 845 _checkWindowsPathReservedCharacters(segments, false, 1); | |
| 846 hasDriveLetter = true; | |
| 847 } else { | |
| 848 _checkWindowsPathReservedCharacters(segments, false); | |
| 849 } | |
| 850 var result = new StringBuffer(); | |
| 851 if (isAbsolute && !hasDriveLetter) result.write("\\"); | |
| 852 if (host != "") { | |
| 853 result.write("\\"); | |
| 854 result.write(host); | |
| 855 result.write("\\"); | |
| 856 } | |
| 857 result.writeAll(segments, "\\"); | |
| 858 if (hasDriveLetter && segments.length == 1) result.write("\\"); | |
| 859 return result.toString(); | |
| 860 } | |
| 861 | |
| 654 void _writeAuthority(StringSink ss) { | 862 void _writeAuthority(StringSink ss) { |
| 655 _addIfNonEmpty(ss, userInfo, userInfo, "@"); | 863 _addIfNonEmpty(ss, userInfo, userInfo, "@"); |
| 656 ss.write(host == null ? "null" : | 864 ss.write(host == null ? "null" : |
| 657 host.contains(':') ? '[$host]' : host); | 865 host.contains(':') ? '[$host]' : host); |
| 658 if (port != 0) { | 866 if (port != 0) { |
| 659 ss.write(":"); | 867 ss.write(":"); |
| 660 ss.write(port.toString()); | 868 ss.write(port.toString()); |
| 661 } | 869 } |
| 662 } | 870 } |
| 663 | 871 |
| (...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 833 var key = element.substring(0, index); | 1041 var key = element.substring(0, index); |
| 834 var value = element.substring(index + 1); | 1042 var value = element.substring(index + 1); |
| 835 map[Uri.decodeQueryComponent(key, decode: decode)] = | 1043 map[Uri.decodeQueryComponent(key, decode: decode)] = |
| 836 decodeQueryComponent(value, decode: decode); | 1044 decodeQueryComponent(value, decode: decode); |
| 837 } | 1045 } |
| 838 return map; | 1046 return map; |
| 839 }); | 1047 }); |
| 840 } | 1048 } |
| 841 | 1049 |
| 842 // Frequently used character codes. | 1050 // Frequently used character codes. |
| 1051 static const int _DOUBLE_QUOTE = 0x22; | |
| 843 static const int _PERCENT = 0x25; | 1052 static const int _PERCENT = 0x25; |
| 1053 static const int _ASTERISK = 0x2A; | |
| 844 static const int _PLUS = 0x2B; | 1054 static const int _PLUS = 0x2B; |
| 845 static const int _SLASH = 0x2F; | 1055 static const int _SLASH = 0x2F; |
| 846 static const int _ZERO = 0x30; | 1056 static const int _ZERO = 0x30; |
| 847 static const int _NINE = 0x39; | 1057 static const int _NINE = 0x39; |
| 848 static const int _COLON = 0x3A; | 1058 static const int _COLON = 0x3A; |
| 1059 static const int _LESS = 0x3C; | |
| 1060 static const int _GREATER = 0x3E; | |
| 1061 static const int _QUESTION = 0x3F; | |
| 849 static const int _AT_SIGN = 0x40; | 1062 static const int _AT_SIGN = 0x40; |
| 850 static const int _UPPER_CASE_A = 0x41; | 1063 static const int _UPPER_CASE_A = 0x41; |
| 851 static const int _UPPER_CASE_F = 0x46; | 1064 static const int _UPPER_CASE_F = 0x46; |
| 1065 static const int _UPPER_CASE_Z = 0x5A; | |
| 1066 static const int _BACKSLASH = 0x5C; | |
| 852 static const int _LOWER_CASE_A = 0x61; | 1067 static const int _LOWER_CASE_A = 0x61; |
| 853 static const int _LOWER_CASE_F = 0x66; | 1068 static const int _LOWER_CASE_F = 0x66; |
| 1069 static const int _LOWER_CASE_Z = 0x7A; | |
| 1070 static const int _BAR = 0x7C; | |
| 854 | 1071 |
| 855 /** | 1072 /** |
| 856 * This is the internal implementation of JavaScript's encodeURI function. | 1073 * This is the internal implementation of JavaScript's encodeURI function. |
| 857 * It encodes all characters in the string [text] except for those | 1074 * It encodes all characters in the string [text] except for those |
| 858 * that appear in [canonicalTable], and returns the escaped string. | 1075 * that appear in [canonicalTable], and returns the escaped string. |
| 859 */ | 1076 */ |
| 860 static String _uriEncode(List<int> canonicalTable, | 1077 static String _uriEncode(List<int> canonicalTable, |
| 861 String text, | 1078 String text, |
| 862 {bool spaceToPlus: false}) { | 1079 {bool spaceToPlus: false}) { |
| 863 byteToHex(int v) { | 1080 byteToHex(int v) { |
| (...skipping 281 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1145 void clear() { | 1362 void clear() { |
| 1146 throw new UnsupportedError("Cannot modify an unmodifiable map"); | 1363 throw new UnsupportedError("Cannot modify an unmodifiable map"); |
| 1147 } | 1364 } |
| 1148 void forEach(void f(K key, V value)) => _map.forEach(f); | 1365 void forEach(void f(K key, V value)) => _map.forEach(f); |
| 1149 Iterable<K> get keys => _map.keys; | 1366 Iterable<K> get keys => _map.keys; |
| 1150 Iterable<V> get values => _map.values; | 1367 Iterable<V> get values => _map.values; |
| 1151 int get length => _map.length; | 1368 int get length => _map.length; |
| 1152 bool get isEmpty => _map.isEmpty; | 1369 bool get isEmpty => _map.isEmpty; |
| 1153 bool get isNotEmpty => _map.isNotEmpty; | 1370 bool get isNotEmpty => _map.isNotEmpty; |
| 1154 } | 1371 } |
| OLD | NEW |