Chromium Code Reviews| Index: sdk/lib/core/uri.dart |
| diff --git a/sdk/lib/core/uri.dart b/sdk/lib/core/uri.dart |
| index 73ce45b906d466733aa000276c1a4b5fa424c034..88d4763df54524853cf49504b95a27ac64236a28 100644 |
| --- a/sdk/lib/core/uri.dart |
| +++ b/sdk/lib/core/uri.dart |
| @@ -263,6 +263,134 @@ class Uri { |
| } |
| /** |
| + * Creates a new `file` URI from an absolute or relative file path. |
| + * |
| + * The file path is passed in [path]. |
| + * |
| + * This path is interpreted using either Windows or non-Windows |
| + * 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.
|
| + * to separate path segments. For non-Windows semantics the slash is |
| + * used to separate path segments. If the path starts with a path |
| + * separator an absolute URI is created. Otherwise a relative URI is |
| + * created. One exception from this rule is that with Windows |
| + * semantics and a path which starts with a drive letter then an |
| + * absolute URI is created. |
| + * |
| + * The default for whether to use Windows or non-Windows semantics |
| + * determined from the platform Dart is running on. When running in |
| + * the standalone VM this is detected by the VM based on the |
| + * operating system. When running in the browser non-Windows |
| + * semantics is always used. |
| + * |
| + * To override the automatic detection of which semantics to use pass |
| + * a value for [windowsPath]. Passing `true` will use Windows |
| + * semantics and passing `false` will use non-Windows semantics. |
| + * |
| + * If the path passed is not a legal file path [ArgumentError] is thrown. |
| + */ |
|
floitsch
2013/07/29 15:16:54
Give some examples.
Søren Gjesse
2013/07/30 13:41:26
Done.
|
| + 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
|
| + windowsPath = windowsPath == null ? Uri._isWindows : windowsPath; |
| + return windowsPath ? _makeWindowsFileUrl(path) : _makeFileUri(path); |
| + } |
| + |
| + 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.
|
| + |
| + // Characters which are not allowed in file names on Windows. |
| + static final _windowsPathReservedCharacters = [ |
| + _DOUBLE_QUOTE, |
| + _ASTERISK, |
| + _SLASH, |
| + _COLON, // Colon is only allowed after drive letter. |
| + _LESS, |
| + _GREATER, |
| + _QUESTION, |
| + _BACKSLASH, // Backslash can only be separator. |
| + _BAR, |
| + ]; |
| + |
| + static _checkWindowsPathReservedCharacters(List<String> segments, |
| + bool argumentError, |
| + [int firstSegment = 0]) { |
| + for (int i = firstSegment; i < segments.length; i++) { |
| + var segment = segments[i]; |
| + for (int j = 0; j < segment.length; j++) { |
| + if (_windowsPathReservedCharacters.contains(segment.codeUnitAt(j))) { |
| + if (argumentError) { |
| + 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
|
| + } else { |
| + 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
|
| + } |
| + } |
| + } |
| + } |
| + } |
| + |
| + static _checkWindowsDriveLetter(int charCode, bool argumentError) { |
| + if ((_UPPER_CASE_A <= charCode && charCode <= _UPPER_CASE_Z) || |
| + (_LOWER_CASE_A <= charCode && charCode <= _LOWER_CASE_Z)) { |
| + return; |
| + } |
| + if (argumentError) { |
| + 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.
|
| + new String.fromCharCode(charCode)); |
| + } else { |
| + 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.
|
| + new String.fromCharCode(charCode)); |
| + } |
| + } |
| + |
| + static _makeFileUri(String path) { |
| + String sep = "/"; |
| + if (path.length > 0 && path[0] == sep) { |
| + // Absolute file:// URI. |
| + return new Uri(scheme: "file", pathSegments: path.split(sep)); |
| + } else { |
| + // Relative URI. |
| + 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.
|
| + } |
| + } |
| + |
| + 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
|
| + String sep = "\\"; |
| + if (path.length > 1 && path[1] == ":") { |
| + _checkWindowsDriveLetter(path.codeUnitAt(0), true); |
| + 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.
|
| + throw new ArgumentError( |
| + "Windows paths with drive letter must be absolute"); |
| + } |
|
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
|
| + // Absolute file://C:/ URI. |
| + var pathSegments = path.split(sep); |
| + _checkWindowsPathReservedCharacters(pathSegments, true, 1); |
| + return new Uri(scheme: "file", pathSegments: pathSegments); |
| + } |
| + |
| + if (path.length > 0 && path[0] == sep) { |
| + if (path.length > 1 && path[1] == sep) { |
| + // Absolute file:// URI with host. |
| + int pathStart = path.indexOf("\\", 2); |
| + String hostPart = |
| + pathStart == -1 ? path.substring(2) : path.substring(2, pathStart); |
| + String pathPart = |
| + pathStart == -1 ? "" : path.substring(pathStart + 1); |
| + var pathSegments = pathPart.split(sep); |
| + _checkWindowsPathReservedCharacters(pathSegments, true); |
| + return new Uri( |
| + scheme: "file", host: hostPart, pathSegments: pathSegments); |
| + } else { |
| + // Absolute file:// URI. |
| + var pathSegments = path.split(sep); |
| + _checkWindowsPathReservedCharacters(pathSegments, true); |
| + return new Uri(scheme: "file", pathSegments: pathSegments); |
| + } |
| + } else { |
| + // Relative URI. |
| + var pathSegments = path.split(sep); |
| + _checkWindowsPathReservedCharacters(pathSegments, true); |
| + return new Uri(pathSegments: pathSegments); |
| + } |
| + } |
| + |
| + /** |
| * Returns the URI path split into its segments. Each of the |
| * segments in the returned list have been decoded. If the path is |
| * empty the empty list will be returned. A leading slash `/` does |
| @@ -651,6 +779,86 @@ class Uri { |
| return "$scheme://$host:$port"; |
| } |
| + /** |
| + * Returns the file path from a file URI. |
| + * |
| + * The returned path has either Windows or non-Windows |
| + * semantics. For Windows semantics the backslash separator is used |
| + * to separate path segments. For non Windows semantics the slash is |
| + * 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
|
| + * 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.
|
| + * the first path segment is a drive letter. When Windows semantics |
| + * is used a host component in the uri in interpreted as a file |
| + * server and a UNC path is returned. |
| + * |
| + * The default for whether to use Windows or non-Windows semantics |
| + * determined from the platform Dart is running on. When running in |
| + * the standalone VM this is detected by the VM based on the |
| + * operating system. When running in the browser non-Windows |
| + * semantics is always used. |
| + * |
| + * To override the automatic detection of which semantics to use pass |
| + * a value for [windowsPath]. Passing `true` will use Windows |
| + * semantics and passing `false` will use non-Windows semantics. |
| + * |
| + * If the URI is not a file URI calling this throws |
| + * [UnsupportedError]. |
|
ahe
2013/07/29 15:30:16
StateError?
Søren Gjesse
2013/07/30 13:41:26
See above.
|
| + * |
| + * If the URI cannot be converted to a file path calling this throws |
| + * [UnsupportedError]. |
|
ahe
2013/07/29 15:30:16
StateError?
Søren Gjesse
2013/07/30 13:41:26
See above.
|
| + */ |
|
floitsch
2013/07/29 15:16:54
add examples.
Søren Gjesse
2013/07/30 13:41:26
Done.
|
| + String toFilePath({bool windowsPath}) { |
| + if (scheme != "" && scheme != "file") { |
| + throw new UnsupportedError( |
| + "Cannot extract a file path from a $scheme URI"); |
| + } |
| + if (windowsPath == null) windowsPath = _isWindows; |
| + return windowsPath ? _toWindowsFilePath() : _toFilePath(); |
| + } |
| + |
| + String _toFilePath() { |
| + if (host != "") { |
| + throw new UnsupportedError( |
|
ahe
2013/07/29 15:30:16
StateError?
Søren Gjesse
2013/07/30 13:41:26
See above.
|
| + "Cannot extract a non-Windows file path from a file URI " |
| + "with an authority"); |
| + } |
| + var result = new StringBuffer(); |
| + 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
|
| + result.writeAll(pathSegments, "/"); |
| + return result.toString(); |
| + } |
| + |
| + String _toWindowsFilePath() { |
| + bool hasDriveLetter = false; |
| + var segments = pathSegments; |
| + if (segments.length > 0 && |
| + segments[0].length >= 2 && |
| + segments[0].codeUnitAt(1) == _COLON) { |
| + _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
|
| + 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.
|
| + var originalSegments = segments; |
| + segments = new List<String>(originalSegments.length + 1); |
| + segments[0] = originalSegments[0].substring(0, 2); |
| + segments[1] = originalSegments[0].substring(2); |
| + segments.setRange(2, segments.length, originalSegments, 1); |
| + } |
| + _checkWindowsPathReservedCharacters(segments, false, 1); |
| + hasDriveLetter = true; |
| + } else { |
| + _checkWindowsPathReservedCharacters(segments, false); |
| + } |
| + var result = new StringBuffer(); |
| + if (isAbsolute && !hasDriveLetter) result.write("\\"); |
| + if (host != "") { |
| + result.write("\\"); |
| + result.write(host); |
| + result.write("\\"); |
| + } |
| + result.writeAll(segments, "\\"); |
| + if (hasDriveLetter && segments.length == 1) result.write("\\"); |
| + return result.toString(); |
| + } |
| + |
| void _writeAuthority(StringSink ss) { |
| _addIfNonEmpty(ss, userInfo, userInfo, "@"); |
| ss.write(host == null ? "null" : |
| @@ -840,17 +1048,26 @@ class Uri { |
| } |
| // Frequently used character codes. |
| + static const int _DOUBLE_QUOTE = 0x22; |
| static const int _PERCENT = 0x25; |
| + static const int _ASTERISK = 0x2A; |
| static const int _PLUS = 0x2B; |
| static const int _SLASH = 0x2F; |
| static const int _ZERO = 0x30; |
| static const int _NINE = 0x39; |
| static const int _COLON = 0x3A; |
| + static const int _LESS = 0x3C; |
| + static const int _GREATER = 0x3E; |
| + static const int _QUESTION = 0x3F; |
| static const int _AT_SIGN = 0x40; |
| static const int _UPPER_CASE_A = 0x41; |
| static const int _UPPER_CASE_F = 0x46; |
| + static const int _UPPER_CASE_Z = 0x5A; |
| + static const int _BACKSLASH = 0x5C; |
| static const int _LOWER_CASE_A = 0x61; |
| static const int _LOWER_CASE_F = 0x66; |
| + static const int _LOWER_CASE_Z = 0x7A; |
| + static const int _BAR = 0x7C; |
| /** |
| * This is the internal implementation of JavaScript's encodeURI function. |