OLD | NEW |
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.io; | 5 part of dart.io; |
6 | 6 |
7 /** | 7 /** |
8 * A Path is an immutable wrapper of a String, with additional member functions | 8 * A Path is an immutable wrapper of a String, with additional member functions |
9 * for useful path manipulations and queries. | 9 * for useful path manipulations and queries. |
10 * On the Windows platform, Path also converts from and to native paths. | 10 * On the Windows platform, Path also converts from native paths to paths using |
| 11 * '/' as a path separator, and vice versa. |
11 * | 12 * |
12 * Joining of paths and path normalization handle '.' and '..' in the usual way. | 13 * Joining of paths and path normalization handle '.' and '..' in the usual way. |
13 */ | 14 */ |
14 abstract class Path { | 15 abstract class Path { |
15 /** | 16 /** |
16 * Creates a Path from a String that uses the native filesystem's conventions. | 17 * Creates a Path from a String that uses the native filesystem's conventions. |
17 * | 18 * |
18 * On Windows, this converts '\' to '/' and has special handling for drive | 19 * On Windows, this converts '\' to '/' and has special handling for drive |
19 * letters and shares. | 20 * letters and shares. |
20 * | 21 * |
21 * If the path starts with a drive letter, like 'C:', a '/' is added | 22 * If the path starts with a drive letter, like 'C:', a '/' is added |
22 * before the drive letter. | 23 * before the drive letter. |
23 * | 24 * |
24 * new Path(r'c:\a\b').toString() == '/c:/a/b' | 25 * new Path(r'c:\a\b').toString() == '/c:/a/b' |
25 * | 26 * |
26 * A path starting with '/c:/' (or any other character instead of 'c') is | 27 * A path starting with a drive letter is |
27 * treated specially. Backwards links ('..') cannot cancel the drive letter. | 28 * treated specially. Backwards links ('..') cannot cancel the drive letter. |
28 * | 29 * |
29 * If the path is a share path this is recorded in the Path object and | 30 * If the path is a share path this is recorded in the Path object and |
30 * maintained in operations on the Path object. | 31 * maintained in operations on the Path object. |
31 * | 32 * |
32 * var share = new Path(r'\\share\a\b\c'); | 33 * var share = new Path(r'\\share\a\b\c'); |
33 * share.isWindowsShare == true | 34 * share.isWindowsShare == true |
34 * share.toString() == '/share/a/b/c' | 35 * share.toString() == '/share/a/b/c' |
35 * share.toNativePath() == r'\\share\a\b\c' | 36 * share.toNativePath() == r'\\share\a\b\c' |
36 * share.append('final').isWindowsShare == true | 37 * share.append('final').isWindowsShare == true |
37 */ | 38 */ |
38 factory Path(String source) => new _Path(source); | 39 factory Path(String source) => new _Path(source); |
39 | 40 |
40 /** | 41 /** |
41 * Creates a Path from the String [source]. [source] is used as-is, so if | 42 * Creates a Path from the String [source]. [source] is used as-is, so if |
42 * the string does not consist of segments separated by forward slashes, the | 43 * the string does not consist of segments separated by forward slashes, the |
43 * behavior may not be as expected. Paths are immutable. | 44 * behavior may not be as expected. Paths are immutable. |
44 */ | 45 */ |
45 factory Path.raw(String source) => new _Path.raw(source); | 46 factory Path.raw(String source) => new _Path.raw(source); |
46 | 47 |
47 /** | 48 /** |
48 * Is this path the empty string? | 49 * Is this path the empty string? |
49 */ | 50 */ |
50 bool get isEmpty; | 51 bool get isEmpty; |
51 | 52 |
52 /** | 53 /** |
53 * Is this path an absolute path, beginning with a path separator? | 54 * Is this path an absolute path, beginning with a '/'? Note that |
| 55 * Windows paths beginning with '\' or with a drive letter are absolute, |
| 56 * and a leading '/' is added when they are converted to a Path. |
54 */ | 57 */ |
55 bool get isAbsolute; | 58 bool get isAbsolute; |
56 | 59 |
57 /** | 60 /** |
58 * Is this path a Windows share path? | 61 * Is this path a Windows share path? |
59 */ | 62 */ |
60 bool get isWindowsShare; | 63 bool get isWindowsShare; |
61 | 64 |
62 /** | 65 /** |
63 * Does this path end with a path separator? | 66 * Does this path end with a '/'? |
64 */ | 67 */ |
65 bool get hasTrailingSeparator; | 68 bool get hasTrailingSeparator; |
66 | 69 |
67 /** | 70 /** |
68 * Does this path contain no consecutive path separators, no segments that | 71 * Does this path contain no consecutive '/'s, no segments that |
69 * are '.' unless the path is exactly '.', and segments that are '..' only | 72 * are '.' unless the path is exactly '.', and segments that are '..' only |
70 * as the leading segments on a relative path? | 73 * as the leading segments on a relative path? |
71 */ | 74 */ |
72 bool get isCanonical; | 75 bool get isCanonical; |
73 | 76 |
74 /** | 77 /** |
75 * Make a path canonical by dropping segments that are '.', cancelling | 78 * Make a path canonical by dropping segments that are '.', cancelling |
76 * segments that are '..' with preceding segments, if possible, | 79 * segments that are '..' with preceding segments, if possible, |
77 * and combining consecutive path separators. Leading '..' segments | 80 * and combining consecutive '/'s. Leading '..' segments |
78 * are kept on relative paths, and dropped from absolute paths. | 81 * are kept on relative paths, and dropped from absolute paths. |
79 */ | 82 */ |
80 Path canonicalize(); | 83 Path canonicalize(); |
81 | 84 |
82 /** | 85 /** |
83 * Joins the relative path [further] to this path. Canonicalizes the | 86 * Joins the relative path [further] to this path. Canonicalizes the |
84 * resulting joined path using [canonicalize], | 87 * resulting joined path using [canonicalize], |
85 * interpreting '.' and '..' as directory traversal commands, and removing | 88 * interpreting '.' and '..' as directory traversal commands, and removing |
86 * consecutive path separators. | 89 * consecutive '/'s. |
87 * | 90 * |
88 * If [further] is an absolute path, an IllegalArgument exception is thrown. | 91 * If [further] is an absolute path, an IllegalArgument exception is thrown. |
89 * | 92 * |
90 * Examples: | 93 * Examples: |
91 * `new Path('/a/b/c').join(new Path('d/e'))` returns the Path object | 94 * `new Path('/a/b/c').join(new Path('d/e'))` returns the Path object |
92 * containing `'a/b/c/d/e'`. | 95 * containing `'a/b/c/d/e'`. |
93 * | 96 * |
94 * `new Path('a/b/../c/').join(new Path('d/./e//')` returns the Path | 97 * `new Path('a/b/../c/').join(new Path('d/./e//')` returns the Path |
95 * containing `'a/c/d/e/'`. | 98 * containing `'a/c/d/e/'`. |
96 * | 99 * |
97 * `new Path('a/b/c').join(new Path('d/../../e')` returns the Path | 100 * `new Path('a/b/c').join(new Path('d/../../e')` returns the Path |
98 * containing `'a/b/e'`. | 101 * containing `'a/b/e'`. |
99 * | 102 * |
100 * Note that the join operation does not drop the last segment of the | 103 * Note that the join operation does not drop the last segment of the |
101 * base path, the way URL joining does. That would be accomplished with | 104 * base path, the way URL joining does. To join basepath to further using |
102 * basepath.directoryPath.join(further). | 105 * URL semantics, use |
| 106 * [:basepath.directoryPath.join(further):]. |
103 * | 107 * |
104 * If you want to avoid joins that traverse | 108 * If you want to avoid joins that traverse |
105 * parent directories in the base, you can check whether | 109 * parent directories in the base, you can check whether |
106 * `further.canonicalize()` starts with '../' or equals '..'. | 110 * `further.canonicalize()` starts with '../' or equals '..'. |
107 */ | 111 */ |
108 Path join(Path further); | 112 Path join(Path further); |
109 | 113 |
110 | 114 |
111 /** | 115 /** |
112 * Returns a path [:relative:] such that | 116 * Returns a path [:relative:] such that |
113 * [:base.join(relative) == this.canonicalize():]. | 117 * [:base.join(relative) == this.canonicalize():]. |
114 * Throws an exception if such a path is impossible. | 118 * Throws an exception if such a path is impossible. |
115 * For example, if [base] is '../../a/b' and [this] is '.'. | 119 * For example, if [base] is '../../a/b' and [this] is '.'. |
116 * The computation is independent of the file system and current directory. | 120 * The computation is independent of the file system and current directory. |
| 121 * |
| 122 * To compute a relative path using URL semantics, where the final |
| 123 * path component of the base is dropped unless it ends with a slash, |
| 124 * call [: a.relativeTo(b.directoryPath) :] instead of [: a.relativeTo(b) :]. |
117 */ | 125 */ |
118 Path relativeTo(Path base); | 126 Path relativeTo(Path base); |
119 | 127 |
120 /** | 128 /** |
121 * Converts a path to a string using the native filesystem's conventions. | 129 * Converts a path to a string using the native filesystem's conventions. |
122 * | 130 * |
123 * On Windows, converts path separators to backwards slashes, and removes | 131 * On Windows, converts '/'s to backwards slashes, and removes |
124 * the leading path separator if the path starts with a drive specification. | 132 * the leading '/' if the path starts with a drive specification. |
125 * For most valid Windows paths, this should be the inverse of the | 133 * For most valid Windows paths, this should be the inverse of the |
126 * constructor Path.fromNative. | 134 * conversion that the constructor new Path() performs. If the path is |
| 135 * a Windows share, restores the '\\' at the start of the path. |
127 */ | 136 */ |
128 String toNativePath(); | 137 String toNativePath(); |
129 | 138 |
130 /** | 139 /** |
131 * Returns the path as a string. If this path is constructed using | 140 * Returns the path as a string. If this path is constructed using |
132 * new Path.raw(), or new Path() on a non-Windows system, the | 141 * new Path.raw(), or new Path() on a non-Windows system, the |
133 * returned value is the original string argument to the constructor. | 142 * returned value is the original string argument to the constructor. |
134 */ | 143 */ |
135 String toString(); | 144 String toString(); |
136 | 145 |
137 /** | 146 /** |
138 * Gets the segments of a Path. Paths beginning or ending with the | 147 * Gets the segments of a Path. The segments are just the result of |
139 * path separator do not have leading or terminating empty segments. | 148 * splitting the path on any '/' characters, except that a '/' at the |
140 * Other than that, the segments are just the result of splitting the | 149 * beginning does not create an empty segment before it, and a '/' at |
141 * path on the path separator. | 150 * the end does not create an empty segment after it. |
142 * | 151 * |
143 * new Path('/a/b/c/d').segments() == ['a', 'b', 'c', d']; | 152 * new Path('/a/b/c/d').segments() == ['a', 'b', 'c', d']; |
144 * new Path(' foo bar //../') == [' foo bar ', '', '..']; | 153 * new Path(' foo bar //../') == [' foo bar ', '', '..']; |
145 */ | 154 */ |
146 List<String> segments(); | 155 List<String> segments(); |
147 | 156 |
148 /** | 157 /** |
149 * Appends [finalSegment] to a path as a new segment. Adds a path separator | 158 * Appends [finalSegment] to a path as a new segment. Adds a '/' |
150 * between the path and [finalSegment] if the path does not already end in | 159 * between the path and [finalSegment] if the path does not already end in |
151 * a path separator. The path is not canonicalized, and [finalSegment] may | 160 * a '/'. The path is not canonicalized, and [finalSegment] may |
152 * contain path separators. | 161 * contain '/'s. |
153 */ | 162 */ |
154 Path append(String finalSegment); | 163 Path append(String finalSegment); |
155 | 164 |
156 /** | 165 /** |
157 * Drops the final path separator and whatever follows it from this Path, | 166 * Drops the final '/' and whatever follows it from this Path, |
158 * and returns the resulting Path object. If the only path separator in | 167 * and returns the resulting Path object. If the only '/' in |
159 * this Path is the first character, returns '/' instead of the empty string. | 168 * this Path is the first character, returns '/' instead of the empty string. |
160 * If there is no path separator in the Path, returns the empty string. | 169 * If there is no '/' in the Path, returns the empty string. |
161 * | 170 * |
162 * new Path('../images/dot.gif').directoryPath == '../images' | 171 * new Path('../images/dot.gif').directoryPath == '../images' |
163 * new Path('/usr/geoffrey/www/').directoryPath == '/usr/geoffrey/www' | 172 * new Path('/usr/geoffrey/www/').directoryPath == '/usr/geoffrey/www' |
164 * new Path('lost_file_old').directoryPath == '' | 173 * new Path('lost_file_old').directoryPath == '' |
165 * new Path('/src').directoryPath == '/' | 174 * new Path('/src').directoryPath == '/' |
166 * Note: new Path('/D:/src').directoryPath == '/D:' | 175 * Note: new Path('/D:/src').directoryPath == '/D:' |
167 */ | 176 */ |
168 Path get directoryPath; | 177 Path get directoryPath; |
169 | 178 |
170 /** | 179 /** |
171 * The part of the path after the last path separator, or the entire path if | 180 * The part of the path after the last '/', or the entire path if |
172 * it contains no path separator. | 181 * it contains no '/'. |
173 * | 182 * |
174 * new Path('images/DSC_0027.jpg).filename == 'DSC_0027.jpg' | 183 * new Path('images/DSC_0027.jpg).filename == 'DSC_0027.jpg' |
175 * new Path('users/fred/').filename == '' | 184 * new Path('users/fred/').filename == '' |
176 */ | 185 */ |
177 String get filename; | 186 String get filename; |
178 | 187 |
179 /** | 188 /** |
180 * The part of [filename] before the last '.', or the entire filename if it | 189 * The part of [filename] before the last '.', or the entire filename if it |
181 * contains no '.'. If [filename] is '.' or '..' it is unchanged. | 190 * contains no '.'. If [filename] is '.' or '..' it is unchanged. |
182 * | 191 * |
183 * new Path('/c:/My Documents/Heidi.txt').filenameWithoutExtension | 192 * new Path('/c:/My Documents/Heidi.txt').filenameWithoutExtension |
184 * would return 'Heidi'. | 193 * would return 'Heidi'. |
185 * new Path('not what I would call a path').filenameWithoutExtension | 194 * new Path('not what I would call a path').filenameWithoutExtension |
186 * would return 'not what I would call a path'. | 195 * would return 'not what I would call a path'. |
187 */ | 196 */ |
188 String get filenameWithoutExtension; | 197 String get filenameWithoutExtension; |
189 | 198 |
190 /** | 199 /** |
191 * The part of [filename] after the last '.', or '' if [filename] | 200 * The part of [filename] after the last '.', or '' if [filename] |
192 * contains no '.'. If [filename] is '.' or '..', returns ''. | 201 * contains no '.'. If [filename] is '.' or '..', returns ''. |
193 * | 202 * |
194 * new Path('tiger.svg').extension == 'svg' | 203 * new Path('tiger.svg').extension == 'svg' |
195 * new Path('/src/dart/dart_secrets').extension == '' | 204 * new Path('/src/dart/dart_secrets').extension == '' |
196 */ | 205 */ |
197 String get extension; | 206 String get extension; |
198 } | 207 } |
OLD | NEW |