Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(233)

Side by Side Diff: sdk/lib/core/uri.dart

Issue 21039005: Add support for file URIs and extracting the file path from file URIs (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Fix due to issue 12290 Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « sdk/lib/_internal/lib/core_patch.dart ('k') | tests/corelib/uri_file_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « sdk/lib/_internal/lib/core_patch.dart ('k') | tests/corelib/uri_file_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698