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..fbc4ba7845e69b6c597d15d43036c2b3e8c546ab 100644 |
| --- a/sdk/lib/core/uri.dart |
| +++ b/sdk/lib/core/uri.dart |
| @@ -263,6 +263,191 @@ class Uri { |
| } |
| /** |
| + * Creates a new `file` URI from an absolute or relative file path. |
|
ahe
2013/08/06 14:14:30
I think backquotes are for code snippets, I'm not
Søren Gjesse
2013/08/07 12:08:00
Done.
|
| + * |
| + * The file path is passed in [path]. |
| + * |
| + * This path is interpreted using either Windows or non-Windows |
| + * semantics. For Windows semantics the backslash or slash separator |
| + * is used to separate path segments. |
|
ahe
2013/08/06 14:14:30
Remove the rest of this paragraph from "For Window
Søren Gjesse
2013/08/07 12:08:00
Done.
|
| + * |
| + * With non-Windows semantics the slash `/` is used to separate path |
|
ahe
2013/08/06 14:14:30
I'm not sure about this use of backquotes either.
Søren Gjesse
2013/08/07 12:08:00
Ended up using ("/") and ("\").
|
| + * segments. |
| + * |
| + * With Windows semantics both the backslash `\` and the slash `/` |
| + * are is used to separate path segments. An exception is when the |
| + * path is prefixed with `\\?\` in which case only the backslash `\` |
|
ahe
2013/08/06 14:14:30
You could replace "is prefixed with" by "starts wi
Søren Gjesse
2013/08/07 12:08:00
Thanks for rephrasing.
|
| + * is treated as a path separator. |
|
ahe
2013/08/06 14:14:30
Ditto for the backquotes in above paragraph.
Søren Gjesse
2013/08/07 12:08:00
Done.
|
| + * |
| + * 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. |
|
ahe
2013/08/06 14:14:30
This specification does not match the example belo
Søren Gjesse
2013/08/07 12:08:00
Extended the comment to say that the drive letter
|
| + * |
| + * 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 |
|
ahe
2013/08/06 14:14:30
"the browser" -> "a browser".
Søren Gjesse
2013/08/07 12:08:00
Done.
|
| + * 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. |
| + * |
| + * Examples using non-Windows semantics (resulting URI in comment): |
| + * |
| + * new Uri.file("xxx/yyy"); // xxx/yyy |
| + * new Uri.file("xxx/yyy/"); // xxx/yyy/ |
| + * new Uri.file("/xxx/yyy"); // file:///xxx/yyy |
| + * new Uri.file("/xxx/yyy/"); // file:///xxx/yyy/ |
| + * new Uri.file("C:"); // C: |
| + * |
| + * Examples using Windows semantics (resulting URI in comment): |
| + * |
| + * new Uri.file(r"xxx\yyy"); // xxx/yyy |
| + * new Uri.file(r"xxx\yyy\"); // xxx/yyy/ |
| + * new Uri.file(r"\xxx\yyy"); // file:///xxx/yyy |
| + * new Uri.file(r"\xxx\yyy/"); // file:///xxx/yyy/ |
| + * new Uri.file(r"C:\xxx\yyy"); // file:///C:/xxx/yyy |
| + * new Uri.file(r"C:xxx\yyy"); // file:///C:/xxx/yyy |
| + * new Uri.file(r"\\server\share\file"); // file://server/share/file |
| + * new Uri.file(r"C:"); // Throws as path with drive letter is not absolute. |
|
ahe
2013/08/06 14:14:30
Long line.
Søren Gjesse
2013/08/07 12:08:00
Done.
|
| + * |
| + * If the path passed is not a legal file path [ArgumentError] is thrown. |
| + */ |
| + factory Uri.file(String path, {bool windowsPath}) { |
| + windowsPath = windowsPath == null ? Uri._isWindows : windowsPath; |
|
ahe
2013/08/06 14:14:30
Remove Uri. prefix.
Søren Gjesse
2013/08/07 12:08:00
Done.
|
| + return windowsPath ? _makeWindowsFileUrl(path) : _makeFileUri(path); |
| + } |
| + |
| + external static bool get _isWindowsPlatform; |
| + |
| + static final bool _isWindows = _isWindowsPlatform; |
|
ahe
2013/08/06 14:14:30
For dart2js, this code is suboptimal. I think it w
Søren Gjesse
2013/08/07 12:08:00
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 _checkNonWindowsPathReservedCharacters(List<String> segments, |
| + bool argumentError) { |
|
ahe
2013/08/06 14:14:30
Indentation.
Søren Gjesse
2013/08/07 12:08:00
Done.
|
| + for (var segment in segments) { |
| + if (segment.contains("/")) { |
| + if (argumentError) { |
| + throw new ArgumentError("Illegal path character $segment"); |
| + } else { |
| + throw new UnsupportedError("Illegal path character $segment"); |
| + } |
| + } |
| + } |
| + } |
| + |
| + 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))) { |
|
ahe
2013/08/06 14:14:30
I suspect that regular expressions would be faster
Søren Gjesse
2013/08/07 12:08:00
Done.
|
| + if (argumentError) { |
| + throw new ArgumentError("Illegal path character ${segment[j]}"); |
| + } else { |
| + throw new UnsupportedError("Illegal path character ${segment[j]}"); |
| + } |
| + } |
| + } |
| + } |
| + } |
| + |
| + 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 " + |
| + new String.fromCharCode(charCode)); |
| + } else { |
| + throw new UnsupportedError("Illegal drive letter " + |
| + 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)); |
| + } |
| + } |
| + |
| + static _makeWindowsFileUrl(String path) { |
| + if (path.startsWith("\\\\?\\")) { |
| + if (path.startsWith("\\\\?\\UNC\\")) { |
| + path = "\\${path.substring(7)}"; |
| + } else { |
| + path = path.substring(4); |
| + if (path.length < 3 || |
| + path.codeUnitAt(1) != _COLON || |
| + path.codeUnitAt(2) != _BACKSLASH) { |
| + throw new ArgumentError( |
| + "Windows paths with \\\\?\\ prefix must be absolute"); |
| + } |
| + } |
| + } else { |
| + path = path.replaceAll("/", "\\"); |
| + } |
| + String sep = "\\"; |
| + if (path.length > 1 && path[1] == ":") { |
| + _checkWindowsDriveLetter(path.codeUnitAt(0), true); |
| + if (path.length == 2 || path.codeUnitAt(2) != _BACKSLASH) { |
| + throw new ArgumentError( |
| + "Windows paths with drive letter must be absolute"); |
| + } |
| + // 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 +836,120 @@ 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 starts with a path separator |
| + * unless Windows semantics is used and 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 |
|
ahe
2013/08/06 14:14:30
the browser -> a browser.
Søren Gjesse
2013/08/07 12:08:00
Done.
|
| + * 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 ends with a slash (i.e. the last path component is |
| + * empty) the returned file path will also end with a slash. |
| + * |
| + * With Windows semantics URIs starting with a drive letter cannot |
| + * be relative to the current drive on the designated drive. That is |
| + * for the URI `file:///c:abc` calling `toFilePath` will return |
| + * `c:\abc` and *not* `c:abc`. |
|
ahe
2013/08/06 14:14:30
I thought this would throw.
Søren Gjesse
2013/08/07 12:08:00
It now throws.
As discussed before I was not sure
|
| + * |
| + * Examples using non-Windows semantics (resulting of calling toFilePath in comment): |
|
ahe
2013/08/06 14:14:30
Long line.
Søren Gjesse
2013/08/07 12:08:00
Done.
|
| + * |
| + * Uri.parse("xxx/yyy"); // xxx/yyy |
| + * Uri.parse("xxx/yyy/"); // xxx/yyy/ |
| + * Uri.parse("file:///xxx/yyy"); // /xxx/yyy |
| + * Uri.parse("file:///xxx/yyy/"); // /xxx/yyy/ |
| + * Uri.parse("file:///C:"); // /C: |
| + * Uri.parse("file:///C:a"); // /C:a |
| + * |
| + * Examples using Windows semantics (resulting URI in comment): |
| + * |
| + * Uri.parse("xxx/yyy"); // xxx\yyy |
| + * Uri.parse("xxx/yyy/"); // xxx\yyy\ |
| + * Uri.parse("file:///xxx/yyy"); // \xxx\yyy |
| + * Uri.parse("file:///xxx/yyy/"); // \xxx\yyy/ |
| + * Uri.parse("file:///C:/xxx/yyy"); // C:\xxx\yyy |
| + * Uri.parse("file:C:xxx/yyy"); // C:\xxx\yyy |
| + * Uri.parse("file://server/share/file"); // \\server\share\file |
| + * |
| + * If the URI is not a file URI calling this throws |
| + * [UnsupportedError]. |
| + * |
| + * If the URI cannot be converted to a file path calling this throws |
| + * [UnsupportedError]. |
| + */ |
| + 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(); |
|
ahe
2013/08/06 14:14:30
You should probably check that all other fields bu
Søren Gjesse
2013/08/07 12:08:00
Done and added tests.
|
| + } |
| + |
| + String _toFilePath() { |
| + if (host != "") { |
| + throw new UnsupportedError( |
| + "Cannot extract a non-Windows file path from a file URI " |
| + "with an authority"); |
| + } |
| + _checkNonWindowsPathReservedCharacters(pathSegments, false); |
| + var result = new StringBuffer(); |
| + if (isAbsolute) result.write("/"); |
| + 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); |
| + if (segments[0].length > 2) { |
| + 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 +1139,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. |