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

Side by Side Diff: pkg/path/lib/path.dart

Issue 20130004: Handle special characters in path<->uri conversion. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 5 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 | « pkg/path/README.md ('k') | pkg/path/test/posix_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 /// A comprehensive, cross-platform path manipulation library. 5 /// A comprehensive, cross-platform path manipulation library.
6 /// 6 ///
7 /// ## Installing ## 7 /// ## Installing ##
8 /// 8 ///
9 /// Use [pub][] to install this package. Add the following to your 9 /// Use [pub][] to install this package. Add the following to your
10 /// `pubspec.yaml` file. 10 /// `pubspec.yaml` file.
11 /// 11 ///
12 /// dependencies: 12 /// dependencies:
13 /// path: any 13 /// path: any
14 /// 14 ///
15 /// Then run `pub install`. 15 /// Then run `pub install`.
16 /// 16 ///
17 /// For more information, see the [path package on pub.dartlang.org][pkg]. 17 /// For more information, see the [path package on pub.dartlang.org][pkg].
18 /// 18 ///
19 /// [pub]: http://pub.dartlang.org 19 /// [pub]: http://pub.dartlang.org
20 /// [pkg]: http://pub.dartlang.org/packages/path 20 /// [pkg]: http://pub.dartlang.org/packages/path
21 ///
22 /// ## Usage ##
23 ///
24 /// The path library was designed to be imported with a prefix, though you don't
25 /// have to if you don't want to:
26 ///
27 /// import 'package:path/path.dart' as path;
28 ///
29 /// The most common way to use the library is through the top-level functions.
30 /// These manipulate path strings based on your current working directory and
31 /// the path style (POSIX, Windows, or URLs) of the host platform. For example:
32 ///
33 /// path.join("directory", "file.txt");
34 ///
35 /// This calls the top-level [join()] function to join "directory" and
nweiz 2013/07/25 20:47:02 Nit: "[join()]" -> "[join]".
Bob Nystrom 2013/07/25 23:20:10 Done.
36 /// "file.txt" using the current platform's directory separator.
37 ///
38 /// If you want to work with paths for a specific platform regardless of the
39 /// underlying platform that the program is running on, you can create a
40 /// [Builder] and give it an explicit [Style]:
41 ///
42 /// var builder = new path.Builder(style: Style.windows);
43 /// builder.join("directory", "file.txt");
44 ///
45 /// This will join "directory" and "file.txt" using the Windows path separator,
46 /// even when the program is run on a POSIX machine.
21 library path; 47 library path;
22 48
23 @MirrorsUsed(targets: 'dart.dom.html.window, ' 49 @MirrorsUsed(targets: 'dart.dom.html.window, '
24 'dart.io.Directory.current, ' 50 'dart.io.Directory.current, '
25 'dart.io.Platform.operatingSystem') 51 'dart.io.Platform.operatingSystem')
26 import 'dart:mirrors'; 52 import 'dart:mirrors';
27 53
28 /// An internal builder for the current OS so we can provide a straight 54 /// An internal builder for the current OS so we can provide a straight
29 /// functional interface and not require users to create one. 55 /// functional interface and not require users to create one.
30 final _builder = new Builder(); 56 final _builder = new Builder();
(...skipping 29 matching lines...) Expand all
60 return _io.classes[const Symbol('Directory')] 86 return _io.classes[const Symbol('Directory')]
61 .getField(const Symbol('current')).reflectee.path; 87 .getField(const Symbol('current')).reflectee.path;
62 } else if (_html != null) { 88 } else if (_html != null) {
63 return _html.getField(const Symbol('window')) 89 return _html.getField(const Symbol('window'))
64 .reflectee.location.href; 90 .reflectee.location.href;
65 } else { 91 } else {
66 return '.'; 92 return '.';
67 } 93 }
68 } 94 }
69 95
70 /// Gets the path separator for the current platform. On Mac and Linux, this 96 /// Gets the path separator for the current platform. This is `\` on Windows
71 /// is `/`. On Windows, it's `\`. 97 /// and `/` on other platforms (including the browser).
72 String get separator => _builder.separator; 98 String get separator => _builder.separator;
73 99
74 /// Converts [path] to an absolute path by resolving it relative to the current 100 /// Converts [path] to an absolute path by resolving it relative to the current
75 /// working directory. If [path] is already an absolute path, just returns it. 101 /// working directory. If [path] is already an absolute path, just returns it.
76 /// 102 ///
77 /// path.absolute('foo/bar.txt'); // -> /your/current/dir/foo/bar.txt 103 /// path.absolute('foo/bar.txt'); // -> /your/current/dir/foo/bar.txt
78 String absolute(String path) => join(current, path); 104 String absolute(String path) => join(current, path);
79 105
80 /// Gets the part of [path] after the last separator. 106 /// Gets the part of [path] after the last separator.
81 /// 107 ///
(...skipping 676 matching lines...) Expand 10 before | Expand all | Expand 10 after
758 /// 784 ///
759 /// // Windows 785 /// // Windows
760 /// builder.toUri(r'C:\path\to\foo') 786 /// builder.toUri(r'C:\path\to\foo')
761 /// // -> Uri.parse('file:///C:/path/to/foo') 787 /// // -> Uri.parse('file:///C:/path/to/foo')
762 /// 788 ///
763 /// // URL 789 /// // URL
764 /// builder.toUri('http://dartlang.org/path/to/foo') 790 /// builder.toUri('http://dartlang.org/path/to/foo')
765 /// // -> Uri.parse('http://dartlang.org/path/to/foo') 791 /// // -> Uri.parse('http://dartlang.org/path/to/foo')
766 Uri toUri(String path) { 792 Uri toUri(String path) {
767 if (isRelative(path)) { 793 if (isRelative(path)) {
768 return Uri.parse(path.replaceAll(style.separatorPattern, '/')); 794 var parsed = _parse(path);
nweiz 2013/07/25 20:47:02 Why use [_parse] here instead of [split]?
Bob Nystrom 2013/07/25 23:20:10 This path is known to be relative, so I don't thin
nweiz 2013/07/25 23:44:27 It's not more straightforward to a reader of the c
Bob Nystrom 2013/07/25 23:58:37 This code was removed anyway, so it's a moot point
795 return style.relativePathToUri(parsed.parts);
769 } else { 796 } else {
770 return style.pathToUri(join(root, path)); 797 return style.absolutePathToUri(join(root, path));
771 } 798 }
772 } 799 }
773 800
774 _ParsedPath _parse(String path) { 801 _ParsedPath _parse(String path) {
775 var before = path; 802 var before = path;
776 803
777 // Remove the root prefix, if any. 804 // Remove the root prefix, if any.
778 var root = style.getRoot(path); 805 var root = style.getRoot(path);
779 var isRootRelative = style.getRelativeRoot(path) != null; 806 var isRootRelative = style.getRelativeRoot(path) != null;
780 if (root != null) path = path.substring(root.length); 807 if (root != null) path = path.substring(root.length);
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
814 /// start with "/". Used by UNIX, Linux, Mac OS X, and others. 841 /// start with "/". Used by UNIX, Linux, Mac OS X, and others.
815 static final posix = new _PosixStyle(); 842 static final posix = new _PosixStyle();
816 843
817 /// Windows paths use "\" (backslash) as separators. Absolute paths start with 844 /// Windows paths use "\" (backslash) as separators. Absolute paths start with
818 /// a drive letter followed by a colon (example, "C:") or two backslashes 845 /// a drive letter followed by a colon (example, "C:") or two backslashes
819 /// ("\\") for UNC paths. 846 /// ("\\") for UNC paths.
820 // TODO(rnystrom): The UNC root prefix should include the drive name too, not 847 // TODO(rnystrom): The UNC root prefix should include the drive name too, not
821 // just the "\\". 848 // just the "\\".
822 static final windows = new _WindowsStyle(); 849 static final windows = new _WindowsStyle();
823 850
824 /// URLs aren't filesystem paths, but they're supported by Pathos to make it 851 /// URLs aren't filesystem paths, but they're supported to make it easier to
825 /// easier to manipulate URL paths in the browser. 852 /// manipulate URL paths in the browser.
826 /// 853 ///
827 /// URLs use "/" (forward slash) as separators. Absolute paths either start 854 /// URLs use "/" (forward slash) as separators. Absolute paths either start
828 /// with a protocol and optional hostname (e.g. `http://dartlang.org`, 855 /// with a protocol and optional hostname (e.g. `http://dartlang.org`,
829 /// `file://`) or with "/". 856 /// `file://`) or with "/".
830 static final url = new _UrlStyle(); 857 static final url = new _UrlStyle();
831 858
832 /// The name of this path style. Will be "posix" or "windows". 859 /// The name of this path style. Will be "posix" or "windows".
833 String get name; 860 String get name;
834 861
835 /// The path separator for this style. On POSIX, this is `/`. On Windows, 862 /// The path separator for this style. On POSIX, this is `/`. On Windows,
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
878 if (relativeRootPattern == null) return null; 905 if (relativeRootPattern == null) return null;
879 // TODO(rnystrom): Use firstMatch() when #7080 is fixed. 906 // TODO(rnystrom): Use firstMatch() when #7080 is fixed.
880 var matches = relativeRootPattern.allMatches(path); 907 var matches = relativeRootPattern.allMatches(path);
881 if (matches.isEmpty) return null; 908 if (matches.isEmpty) return null;
882 return matches.first[0]; 909 return matches.first[0];
883 } 910 }
884 911
885 /// Returns the path represented by [uri] in this style. 912 /// Returns the path represented by [uri] in this style.
886 String pathFromUri(Uri uri); 913 String pathFromUri(Uri uri);
887 914
888 /// Returns the URI that represents [path]. 915 /// Returns the URI that represents the relative path made of [parts].
889 /// 916 Uri relativePathToUri(Iterable<String> parts) => new Uri(pathSegments: parts);
nweiz 2013/07/25 20:47:02 It's weird that this takes a list of parts but [ab
Bob Nystrom 2013/07/25 23:20:10 Done. Good call.
890 /// Pathos will always path an absolute path for [path]. Relative paths are 917
891 /// handled automatically by [Builder]. 918 /// Returns the URI that represents [path], which is assumed to be absolute.
892 Uri pathToUri(String path); 919 Uri absolutePathToUri(String path);
893 920
894 String toString() => name; 921 String toString() => name;
895 } 922 }
896 923
897 /// The style for POSIX paths. 924 /// The style for POSIX paths.
898 class _PosixStyle extends Style { 925 class _PosixStyle extends Style {
899 _PosixStyle(); 926 _PosixStyle();
900 927
901 static final _builder = new Builder(style: Style.posix); 928 static final _builder = new Builder(style: Style.posix);
902 929
903 final name = 'posix'; 930 final name = 'posix';
904 final separator = '/'; 931 final separator = '/';
905 final separatorPattern = new RegExp(r'/'); 932 final separatorPattern = new RegExp(r'/');
906 final needsSeparatorPattern = new RegExp(r'[^/]$'); 933 final needsSeparatorPattern = new RegExp(r'[^/]$');
907 final rootPattern = new RegExp(r'^/'); 934 final rootPattern = new RegExp(r'^/');
908 935
909 String pathFromUri(Uri uri) { 936 String pathFromUri(Uri uri) {
910 if (uri.scheme == '' || uri.scheme == 'file') { 937 if (uri.scheme == '' || uri.scheme == 'file') {
911 return Uri.decodeComponent(uri.path); 938 return Uri.decodeComponent(uri.path);
912 } 939 }
913 throw new ArgumentError("Uri $uri must have scheme 'file:'."); 940 throw new ArgumentError("Uri $uri must have scheme 'file:'.");
914 } 941 }
915 942
916 Uri pathToUri(String path) { 943 Uri absolutePathToUri(String path) {
917 var parsed = _builder._parse(path); 944 var parsed = _builder._parse(path);
918 945
919 if (parsed.parts.isEmpty) { 946 if (parsed.parts.isEmpty) {
920 // If the path is a bare root (e.g. "/"), [components] will 947 // If the path is a bare root (e.g. "/"), [components] will
921 // currently be empty. We add two empty components so the URL constructor 948 // currently be empty. We add two empty components so the URL constructor
922 // produces "file:///", with a trailing slash. 949 // produces "file:///", with a trailing slash.
923 parsed.parts.addAll(["", ""]); 950 parsed.parts.addAll(["", ""]);
924 } else if (parsed.hasTrailingSeparator) { 951 } else if (parsed.hasTrailingSeparator) {
925 // If the path has a trailing slash, add a single empty component so the 952 // If the path has a trailing slash, add a single empty component so the
926 // URI has a trailing slash as well. 953 // URI has a trailing slash as well.
(...skipping 26 matching lines...) Expand all
953 // Drive-letter paths look like "file:///C:/path/to/file". The 980 // Drive-letter paths look like "file:///C:/path/to/file". The
954 // replaceFirst removes the extra initial slash. 981 // replaceFirst removes the extra initial slash.
955 if (path.startsWith('/')) path = path.replaceFirst("/", ""); 982 if (path.startsWith('/')) path = path.replaceFirst("/", "");
956 } else { 983 } else {
957 // Network paths look like "file://hostname/path/to/file". 984 // Network paths look like "file://hostname/path/to/file".
958 path = '\\\\${uri.host}$path'; 985 path = '\\\\${uri.host}$path';
959 } 986 }
960 return Uri.decodeComponent(path.replaceAll("/", "\\")); 987 return Uri.decodeComponent(path.replaceAll("/", "\\"));
961 } 988 }
962 989
963 Uri pathToUri(String path) { 990 Uri absolutePathToUri(String path) {
964 var parsed = _builder._parse(path); 991 var parsed = _builder._parse(path);
965 if (parsed.root == r'\\') { 992 if (parsed.root == r'\\') {
966 // Network paths become "file://hostname/path/to/file". 993 // Network paths become "file://hostname/path/to/file".
967 994
968 var host = parsed.parts.removeAt(0); 995 var host = parsed.parts.removeAt(0);
969 996
970 if (parsed.parts.isEmpty) { 997 if (parsed.parts.isEmpty) {
971 // If the path is a bare root (e.g. "\\hostname"), [parsed.parts] will 998 // If the path is a bare root (e.g. "\\hostname"), [parsed.parts] will
972 // currently be empty. We add two empty components so the URL 999 // currently be empty. We add two empty components so the URL
973 // constructor produces "file://hostname/", with a trailing slash. 1000 // constructor produces "file://hostname/", with a trailing slash.
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
1006 final name = 'url'; 1033 final name = 'url';
1007 final separator = '/'; 1034 final separator = '/';
1008 final separatorPattern = new RegExp(r'/'); 1035 final separatorPattern = new RegExp(r'/');
1009 final needsSeparatorPattern = new RegExp( 1036 final needsSeparatorPattern = new RegExp(
1010 r"(^[a-zA-Z][-+.a-zA-Z\d]*://|[^/])$"); 1037 r"(^[a-zA-Z][-+.a-zA-Z\d]*://|[^/])$");
1011 final rootPattern = new RegExp(r"[a-zA-Z][-+.a-zA-Z\d]*://[^/]*"); 1038 final rootPattern = new RegExp(r"[a-zA-Z][-+.a-zA-Z\d]*://[^/]*");
1012 final relativeRootPattern = new RegExp(r"^/"); 1039 final relativeRootPattern = new RegExp(r"^/");
1013 1040
1014 String pathFromUri(Uri uri) => uri.toString(); 1041 String pathFromUri(Uri uri) => uri.toString();
1015 1042
1016 Uri pathToUri(String path) => Uri.parse(path); 1043 Uri relativePathToUri(Iterable<String> parts) {
1044 // Since [parts] is itself from a URI, it may contain percent-encoded
1045 // components. We don't want to double percent-encode them, so we
1046 // percent-decode first.
1047 return super.relativePathToUri(parts.map(Uri.decodeComponent));
nweiz 2013/07/25 20:47:02 Seems unnecessary to parse and then re-serialize t
Bob Nystrom 2013/07/25 23:20:10 Done.
1048 }
1049
1050 Uri absolutePathToUri(String path) => Uri.parse(path);
1017 } 1051 }
1018 1052
1019 // TODO(rnystrom): Make this public? 1053 // TODO(rnystrom): Make this public?
1020 class _ParsedPath { 1054 class _ParsedPath {
1021 /// The [Style] that was used to parse this path. 1055 /// The [Style] that was used to parse this path.
1022 Style style; 1056 Style style;
1023 1057
1024 /// The absolute root portion of the path, or `null` if the path is relative. 1058 /// The absolute root portion of the path, or `null` if the path is relative.
1025 /// On POSIX systems, this will be `null` or "/". On Windows, it can be 1059 /// On POSIX systems, this will be `null` or "/". On Windows, it can be
1026 /// `null`, "//" for a UNC path, or something like "C:\" for paths with drive 1060 /// `null`, "//" for a UNC path, or something like "C:\" for paths with drive
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after
1152 // doesn't count. 1186 // doesn't count.
1153 if (lastDot <= 0) return [file, '']; 1187 if (lastDot <= 0) return [file, ''];
1154 1188
1155 return [file.substring(0, lastDot), file.substring(lastDot)]; 1189 return [file.substring(0, lastDot), file.substring(lastDot)];
1156 } 1190 }
1157 1191
1158 _ParsedPath clone() => new _ParsedPath( 1192 _ParsedPath clone() => new _ParsedPath(
1159 style, root, isRootRelative, 1193 style, root, isRootRelative,
1160 new List.from(parts), new List.from(separators)); 1194 new List.from(parts), new List.from(separators));
1161 } 1195 }
OLDNEW
« no previous file with comments | « pkg/path/README.md ('k') | pkg/path/test/posix_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698