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

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: Style fix 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
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. 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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698