| 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. |
| 272 * |
| 273 * With non-Windows semantics the slash ("/") is used to separate |
| 274 * path segments. |
| 275 * |
| 276 * With Windows semantics, backslash ("\") and forward-slash ("/") |
| 277 * are used to separate path segments, except if the path starts |
| 278 * with "\\?\" in which case, only backslash ("\") separates path |
| 279 * segments. |
| 280 * |
| 281 * If the path starts with a path separator an absolute URI is |
| 282 * created. Otherwise a relative URI is created. One exception from |
| 283 * this rule is that when Windows semantics is used and the path |
| 284 * starts with a drive letter followed by a colon (":") and a |
| 285 * path separator then an absolute URI is created. |
| 286 * |
| 287 * The default for whether to use Windows or non-Windows semantics |
| 288 * determined from the platform Dart is running on. When running in |
| 289 * the standalone VM this is detected by the VM based on the |
| 290 * operating system. When running in a browser non-Windows semantics |
| 291 * is always used. |
| 292 * |
| 293 * To override the automatic detection of which semantics to use pass |
| 294 * a value for [windows]. Passing `true` will use Windows |
| 295 * semantics and passing `false` will use non-Windows semantics. |
| 296 * |
| 297 * Examples using non-Windows semantics (resulting URI in comment): |
| 298 * |
| 299 * new Uri.file("xxx/yyy"); // xxx/yyy |
| 300 * new Uri.file("xxx/yyy/"); // xxx/yyy/ |
| 301 * new Uri.file("/xxx/yyy"); // file:///xxx/yyy |
| 302 * new Uri.file("/xxx/yyy/"); // file:///xxx/yyy/ |
| 303 * new Uri.file("C:"); // C: |
| 304 * |
| 305 * Examples using Windows semantics (resulting URI in comment): |
| 306 * |
| 307 * new Uri.file(r"xxx\yyy"); // xxx/yyy |
| 308 * new Uri.file(r"xxx\yyy\"); // xxx/yyy/ |
| 309 * new Uri.file(r"\xxx\yyy"); // file:///xxx/yyy |
| 310 * new Uri.file(r"\xxx\yyy/"); // file:///xxx/yyy/ |
| 311 * new Uri.file(r"C:\xxx\yyy"); // file:///C:/xxx/yyy |
| 312 * new Uri.file(r"C:xxx\yyy"); // Throws as path with drive letter |
| 313 * // is not absolute. |
| 314 * new Uri.file(r"\\server\share\file"); // file://server/share/file |
| 315 * new Uri.file(r"C:"); // Throws as path with drive letter |
| 316 * // is not absolute. |
| 317 * |
| 318 * If the path passed is not a legal file path [ArgumentError] is thrown. |
| 319 */ |
| 320 factory Uri.file(String path, {bool windows}) { |
| 321 windows = windows == null ? Uri._isWindows : windows; |
| 322 return windows ? _makeWindowsFileUrl(path) : _makeFileUri(path); |
| 323 } |
| 324 |
| 325 external static bool get _isWindows; |
| 326 |
| 327 static _checkNonWindowsPathReservedCharacters(List<String> segments, |
| 328 bool argumentError) { |
| 329 segments.forEach((segment) { |
| 330 if (segment.contains("/")) { |
| 331 if (argumentError) { |
| 332 throw new ArgumentError("Illegal path character $segment"); |
| 333 } else { |
| 334 throw new UnsupportedError("Illegal path character $segment"); |
| 335 } |
| 336 } |
| 337 }); |
| 338 } |
| 339 |
| 340 static _checkWindowsPathReservedCharacters(List<String> segments, |
| 341 bool argumentError, |
| 342 [int firstSegment = 0]) { |
| 343 segments.skip(firstSegment).forEach((segment) { |
| 344 if (segment.contains(new RegExp(r'["*/:<>?\\|]'))) { |
| 345 if (argumentError) { |
| 346 throw new ArgumentError("Illegal character in path}"); |
| 347 } else { |
| 348 throw new UnsupportedError("Illegal character in path}"); |
| 349 } |
| 350 } |
| 351 }); |
| 352 } |
| 353 |
| 354 static _checkWindowsDriveLetter(int charCode, bool argumentError) { |
| 355 if ((_UPPER_CASE_A <= charCode && charCode <= _UPPER_CASE_Z) || |
| 356 (_LOWER_CASE_A <= charCode && charCode <= _LOWER_CASE_Z)) { |
| 357 return; |
| 358 } |
| 359 if (argumentError) { |
| 360 throw new ArgumentError("Illegal drive letter " + |
| 361 new String.fromCharCode(charCode)); |
| 362 } else { |
| 363 throw new UnsupportedError("Illegal drive letter " + |
| 364 new String.fromCharCode(charCode)); |
| 365 } |
| 366 } |
| 367 |
| 368 static _makeFileUri(String path) { |
| 369 String sep = "/"; |
| 370 if (path.length > 0 && path[0] == sep) { |
| 371 // Absolute file:// URI. |
| 372 return new Uri(scheme: "file", pathSegments: path.split(sep)); |
| 373 } else { |
| 374 // Relative URI. |
| 375 return new Uri(pathSegments: path.split(sep)); |
| 376 } |
| 377 } |
| 378 |
| 379 static _makeWindowsFileUrl(String path) { |
| 380 if (path.startsWith("\\\\?\\")) { |
| 381 if (path.startsWith("\\\\?\\UNC\\")) { |
| 382 path = "\\${path.substring(7)}"; |
| 383 } else { |
| 384 path = path.substring(4); |
| 385 if (path.length < 3 || |
| 386 path.codeUnitAt(1) != _COLON || |
| 387 path.codeUnitAt(2) != _BACKSLASH) { |
| 388 throw new ArgumentError( |
| 389 "Windows paths with \\\\?\\ prefix must be absolute"); |
| 390 } |
| 391 } |
| 392 } else { |
| 393 path = path.replaceAll("/", "\\"); |
| 394 } |
| 395 String sep = "\\"; |
| 396 if (path.length > 1 && path[1] == ":") { |
| 397 _checkWindowsDriveLetter(path.codeUnitAt(0), true); |
| 398 if (path.length == 2 || path.codeUnitAt(2) != _BACKSLASH) { |
| 399 throw new ArgumentError( |
| 400 "Windows paths with drive letter must be absolute"); |
| 401 } |
| 402 // Absolute file://C:/ URI. |
| 403 var pathSegments = path.split(sep); |
| 404 _checkWindowsPathReservedCharacters(pathSegments, true, 1); |
| 405 return new Uri(scheme: "file", pathSegments: pathSegments); |
| 406 } |
| 407 |
| 408 if (path.length > 0 && path[0] == sep) { |
| 409 if (path.length > 1 && path[1] == sep) { |
| 410 // Absolute file:// URI with host. |
| 411 int pathStart = path.indexOf("\\", 2); |
| 412 String hostPart = |
| 413 pathStart == -1 ? path.substring(2) : path.substring(2, pathStart); |
| 414 String pathPart = |
| 415 pathStart == -1 ? "" : path.substring(pathStart + 1); |
| 416 var pathSegments = pathPart.split(sep); |
| 417 _checkWindowsPathReservedCharacters(pathSegments, true); |
| 418 return new Uri( |
| 419 scheme: "file", host: hostPart, pathSegments: pathSegments); |
| 420 } else { |
| 421 // Absolute file:// URI. |
| 422 var pathSegments = path.split(sep); |
| 423 _checkWindowsPathReservedCharacters(pathSegments, true); |
| 424 return new Uri(scheme: "file", pathSegments: pathSegments); |
| 425 } |
| 426 } else { |
| 427 // Relative URI. |
| 428 var pathSegments = path.split(sep); |
| 429 _checkWindowsPathReservedCharacters(pathSegments, true); |
| 430 return new Uri(pathSegments: pathSegments); |
| 431 } |
| 432 } |
| 433 |
| 434 /** |
| 266 * Returns the URI path split into its segments. Each of the | 435 * Returns the URI path split into its segments. Each of the |
| 267 * segments in the returned list have been decoded. If the path is | 436 * segments in the returned list have been decoded. If the path is |
| 268 * empty the empty list will be returned. A leading slash `/` does | 437 * empty the empty list will be returned. A leading slash `/` does |
| 269 * not affect the segments returned. | 438 * not affect the segments returned. |
| 270 * | 439 * |
| 271 * The returned list is unmodifiable and will throw [UnsupportedError] on any | 440 * The returned list is unmodifiable and will throw [UnsupportedError] on any |
| 272 * calls that would mutate it. | 441 * calls that would mutate it. |
| 273 */ | 442 */ |
| 274 List<String> get pathSegments { | 443 List<String> get pathSegments { |
| 275 if (_pathSegments == null) { | 444 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"); | 813 throw new StateError("Cannot use origin without a scheme: $this"); |
| 645 } | 814 } |
| 646 if (scheme != "http" && scheme != "https") { | 815 if (scheme != "http" && scheme != "https") { |
| 647 throw new StateError( | 816 throw new StateError( |
| 648 "Origin is only applicable schemes http and https: $this"); | 817 "Origin is only applicable schemes http and https: $this"); |
| 649 } | 818 } |
| 650 if (port == 0) return "$scheme://$host"; | 819 if (port == 0) return "$scheme://$host"; |
| 651 return "$scheme://$host:$port"; | 820 return "$scheme://$host:$port"; |
| 652 } | 821 } |
| 653 | 822 |
| 823 /** |
| 824 * Returns the file path from a file URI. |
| 825 * |
| 826 * The returned path has either Windows or non-Windows |
| 827 * semantics. |
| 828 * |
| 829 * For non-Windows semantics the slash ("/") is used to separate |
| 830 * path segments. |
| 831 * |
| 832 * For Windows semantics the backslash ("\") separator is used to |
| 833 * separate path segments. |
| 834 * |
| 835 * If the URI is absolute the path starts with a path separator |
| 836 * unless Windows semantics is used and the first path segment is a |
| 837 * drive letter. When Windows semantics is used a host component in |
| 838 * the uri in interpreted as a file server and a UNC path is |
| 839 * returned. |
| 840 * |
| 841 * The default for whether to use Windows or non-Windows semantics |
| 842 * determined from the platform Dart is running on. When running in |
| 843 * the standalone VM this is detected by the VM based on the |
| 844 * operating system. When running in a browser non-Windows semantics |
| 845 * is always used. |
| 846 * |
| 847 * To override the automatic detection of which semantics to use pass |
| 848 * a value for [windows]. Passing `true` will use Windows |
| 849 * semantics and passing `false` will use non-Windows semantics. |
| 850 * |
| 851 * If the URI ends with a slash (i.e. the last path component is |
| 852 * empty) the returned file path will also end with a slash. |
| 853 * |
| 854 * With Windows semantics URIs starting with a drive letter cannot |
| 855 * be relative to the current drive on the designated drive. That is |
| 856 * for the URI `file:///c:abc` calling `toFilePath` will throw as a |
| 857 * path segment cannot contain colon on Windows. |
| 858 * |
| 859 * Examples using non-Windows semantics (resulting of calling |
| 860 * toFilePath in comment): |
| 861 * |
| 862 * Uri.parse("xxx/yyy"); // xxx/yyy |
| 863 * Uri.parse("xxx/yyy/"); // xxx/yyy/ |
| 864 * Uri.parse("file:///xxx/yyy"); // /xxx/yyy |
| 865 * Uri.parse("file:///xxx/yyy/"); // /xxx/yyy/ |
| 866 * Uri.parse("file:///C:"); // /C: |
| 867 * Uri.parse("file:///C:a"); // /C:a |
| 868 * |
| 869 * Examples using Windows semantics (resulting URI in comment): |
| 870 * |
| 871 * Uri.parse("xxx/yyy"); // xxx\yyy |
| 872 * Uri.parse("xxx/yyy/"); // xxx\yyy\ |
| 873 * Uri.parse("file:///xxx/yyy"); // \xxx\yyy |
| 874 * Uri.parse("file:///xxx/yyy/"); // \xxx\yyy/ |
| 875 * Uri.parse("file:///C:/xxx/yyy"); // C:\xxx\yyy |
| 876 * Uri.parse("file:C:xxx/yyy"); // Throws as a path segment |
| 877 * // cannot contain colon on Windows. |
| 878 * Uri.parse("file://server/share/file"); // \\server\share\file |
| 879 * |
| 880 * If the URI is not a file URI calling this throws |
| 881 * [UnsupportedError]. |
| 882 * |
| 883 * If the URI cannot be converted to a file path calling this throws |
| 884 * [UnsupportedError]. |
| 885 */ |
| 886 String toFilePath({bool windows}) { |
| 887 if (scheme != "" && scheme != "file") { |
| 888 throw new UnsupportedError( |
| 889 "Cannot extract a file path from a $scheme URI"); |
| 890 } |
| 891 if (scheme != "" && scheme != "file") { |
| 892 throw new UnsupportedError( |
| 893 "Cannot extract a file path from a $scheme URI"); |
| 894 } |
| 895 if (query != "") { |
| 896 throw new UnsupportedError( |
| 897 "Cannot extract a file path from a URI with a query component"); |
| 898 } |
| 899 if (fragment != "") { |
| 900 throw new UnsupportedError( |
| 901 "Cannot extract a file path from a URI with a fragment component"); |
| 902 } |
| 903 if (windows == null) windows = _isWindows; |
| 904 return windows ? _toWindowsFilePath() : _toFilePath(); |
| 905 } |
| 906 |
| 907 String _toFilePath() { |
| 908 if (host != "") { |
| 909 throw new UnsupportedError( |
| 910 "Cannot extract a non-Windows file path from a file URI " |
| 911 "with an authority"); |
| 912 } |
| 913 _checkNonWindowsPathReservedCharacters(pathSegments, false); |
| 914 var result = new StringBuffer(); |
| 915 if (isAbsolute) result.write("/"); |
| 916 result.writeAll(pathSegments, "/"); |
| 917 return result.toString(); |
| 918 } |
| 919 |
| 920 String _toWindowsFilePath() { |
| 921 bool hasDriveLetter = false; |
| 922 var segments = pathSegments; |
| 923 if (segments.length > 0 && |
| 924 segments[0].length == 2 && |
| 925 segments[0].codeUnitAt(1) == _COLON) { |
| 926 _checkWindowsDriveLetter(segments[0].codeUnitAt(0), false); |
| 927 _checkWindowsPathReservedCharacters(segments, false, 1); |
| 928 hasDriveLetter = true; |
| 929 } else { |
| 930 _checkWindowsPathReservedCharacters(segments, false); |
| 931 } |
| 932 var result = new StringBuffer(); |
| 933 if (isAbsolute && !hasDriveLetter) result.write("\\"); |
| 934 if (host != "") { |
| 935 result.write("\\"); |
| 936 result.write(host); |
| 937 result.write("\\"); |
| 938 } |
| 939 result.writeAll(segments, "\\"); |
| 940 if (hasDriveLetter && segments.length == 1) result.write("\\"); |
| 941 return result.toString(); |
| 942 } |
| 943 |
| 654 void _writeAuthority(StringSink ss) { | 944 void _writeAuthority(StringSink ss) { |
| 655 _addIfNonEmpty(ss, userInfo, userInfo, "@"); | 945 _addIfNonEmpty(ss, userInfo, userInfo, "@"); |
| 656 ss.write(host == null ? "null" : | 946 ss.write(host == null ? "null" : |
| 657 host.contains(':') ? '[$host]' : host); | 947 host.contains(':') ? '[$host]' : host); |
| 658 if (port != 0) { | 948 if (port != 0) { |
| 659 ss.write(":"); | 949 ss.write(":"); |
| 660 ss.write(port.toString()); | 950 ss.write(port.toString()); |
| 661 } | 951 } |
| 662 } | 952 } |
| 663 | 953 |
| (...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 833 var key = element.substring(0, index); | 1123 var key = element.substring(0, index); |
| 834 var value = element.substring(index + 1); | 1124 var value = element.substring(index + 1); |
| 835 map[Uri.decodeQueryComponent(key, decode: decode)] = | 1125 map[Uri.decodeQueryComponent(key, decode: decode)] = |
| 836 decodeQueryComponent(value, decode: decode); | 1126 decodeQueryComponent(value, decode: decode); |
| 837 } | 1127 } |
| 838 return map; | 1128 return map; |
| 839 }); | 1129 }); |
| 840 } | 1130 } |
| 841 | 1131 |
| 842 // Frequently used character codes. | 1132 // Frequently used character codes. |
| 1133 static const int _DOUBLE_QUOTE = 0x22; |
| 843 static const int _PERCENT = 0x25; | 1134 static const int _PERCENT = 0x25; |
| 1135 static const int _ASTERISK = 0x2A; |
| 844 static const int _PLUS = 0x2B; | 1136 static const int _PLUS = 0x2B; |
| 845 static const int _SLASH = 0x2F; | 1137 static const int _SLASH = 0x2F; |
| 846 static const int _ZERO = 0x30; | 1138 static const int _ZERO = 0x30; |
| 847 static const int _NINE = 0x39; | 1139 static const int _NINE = 0x39; |
| 848 static const int _COLON = 0x3A; | 1140 static const int _COLON = 0x3A; |
| 1141 static const int _LESS = 0x3C; |
| 1142 static const int _GREATER = 0x3E; |
| 1143 static const int _QUESTION = 0x3F; |
| 849 static const int _AT_SIGN = 0x40; | 1144 static const int _AT_SIGN = 0x40; |
| 850 static const int _UPPER_CASE_A = 0x41; | 1145 static const int _UPPER_CASE_A = 0x41; |
| 851 static const int _UPPER_CASE_F = 0x46; | 1146 static const int _UPPER_CASE_F = 0x46; |
| 1147 static const int _UPPER_CASE_Z = 0x5A; |
| 1148 static const int _BACKSLASH = 0x5C; |
| 852 static const int _LOWER_CASE_A = 0x61; | 1149 static const int _LOWER_CASE_A = 0x61; |
| 853 static const int _LOWER_CASE_F = 0x66; | 1150 static const int _LOWER_CASE_F = 0x66; |
| 1151 static const int _LOWER_CASE_Z = 0x7A; |
| 1152 static const int _BAR = 0x7C; |
| 854 | 1153 |
| 855 /** | 1154 /** |
| 856 * This is the internal implementation of JavaScript's encodeURI function. | 1155 * This is the internal implementation of JavaScript's encodeURI function. |
| 857 * It encodes all characters in the string [text] except for those | 1156 * It encodes all characters in the string [text] except for those |
| 858 * that appear in [canonicalTable], and returns the escaped string. | 1157 * that appear in [canonicalTable], and returns the escaped string. |
| 859 */ | 1158 */ |
| 860 static String _uriEncode(List<int> canonicalTable, | 1159 static String _uriEncode(List<int> canonicalTable, |
| 861 String text, | 1160 String text, |
| 862 {bool spaceToPlus: false}) { | 1161 {bool spaceToPlus: false}) { |
| 863 byteToHex(int v) { | 1162 byteToHex(int v) { |
| (...skipping 281 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1145 void clear() { | 1444 void clear() { |
| 1146 throw new UnsupportedError("Cannot modify an unmodifiable map"); | 1445 throw new UnsupportedError("Cannot modify an unmodifiable map"); |
| 1147 } | 1446 } |
| 1148 void forEach(void f(K key, V value)) => _map.forEach(f); | 1447 void forEach(void f(K key, V value)) => _map.forEach(f); |
| 1149 Iterable<K> get keys => _map.keys; | 1448 Iterable<K> get keys => _map.keys; |
| 1150 Iterable<V> get values => _map.values; | 1449 Iterable<V> get values => _map.values; |
| 1151 int get length => _map.length; | 1450 int get length => _map.length; |
| 1152 bool get isEmpty => _map.isEmpty; | 1451 bool get isEmpty => _map.isEmpty; |
| 1153 bool get isNotEmpty => _map.isNotEmpty; | 1452 bool get isNotEmpty => _map.isNotEmpty; |
| 1154 } | 1453 } |
| OLD | NEW |