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

Side by Side Diff: runtime/bin/path_impl.dart

Issue 10417053: Add Path class to dart:io, and add unit tests for it. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Address comments, finish implementation (mostly). Created 8 years, 6 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
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 class _PathImpl implements Path {
6 final String path;
7
8 const _PathImpl(String source) : path = source;
9 _PathImpl.fromNative(String source) : path = _clean(source);
10
11 static String _clean(String source) {
12 switch (Platform.operatingSystem) {
13 case 'windows':
14 return _cleanWindows(source);
15 default:
16 return source;
17 }
18 }
19
20 static String _cleanWindows(source) {
21 // Change \ to /.
22 var clean = source.replaceAll('\\', '/');
23 // Add / before intial [Drive letter]:
24 if (clean.length >= 2 && clean[1] == ':') {
25 clean = '/$clean';
26 }
27 return clean;
28 }
29
30 bool get isEmpty() => path == '';
Mads Ager (google) 2012/06/01 08:11:31 bool get isEmpty() => path.isEmpty();
31 bool get isAbsolute() => path.startsWith('/');
32 bool get hasTrailingSlash() => path.endsWith('/');
33
34 String toString() => path;
35
36 Path relativeTo(Path base) {
Mads Ager (google) 2012/06/01 08:11:31 This is not in the interface. Remove for now and a
37 // Throws exception if not doable.
38 // Unimplemented
39 if (base.isAbsolute && path.startsWith(base.path)) {
40 if (path == base.path) return new Path('.');
41 if (path[base.path.length] == '/') {
42 return new Path(path.substring(base.path.length + 1));
43 }
44 }
45 throw new UnimplementedException(
46 "Unimplemented case of Path.relativeTo(base):\n"
47 " Only absolute paths with strict containment are handled at present.\n"
48 " Arguments: $path.relativeTo($base)");
49 }
50
51 Path join(Path further) {
52 if (further.isAbsolute) {
53 throw new IllegalArgumentException(
Mads Ager (google) 2012/06/01 08:11:31 This is not documented in the interface.
54 "Path.join called with absolute Path as argument.");
55 }
56 if (isEmpty) {
57 return further.canonicalize();
58 }
59 // We do not drop everything after the last / in the base (this).
60 if (hasTrailingSlash) {
61 return new Path('$path${further.path}').canonicalize();
62 }
63 return new Path('$path/$further.path').canonicalize();
64 }
65
66 Path safeJoin(Path further) {
67 further = further.canonicalize();
68 if (further.toString() == '..' || further.toString().startsWith('../')) {
69 throw new IllegalArgumentException(
70 'Path.safeJoin called with argument $further, which starts with ..');
71 } else {
72 return join(further);
73 }
74 }
75
76 // Note: The URI RFC names for these operations are normalize, resolve, and
77 // relativize.
78 Path canonicalize() {
79 if (isCanonical) return this;
80 return makeCanonical();
81 }
82
83 bool get isCanonical() {
84 // Contains no consecutive /s.
Mads Ager (google) 2012/06/01 08:11:31 /s -> path separators.
Bill Hesse 2012/06/15 14:23:08 Done.
85 // Contains no . components.
86 // Absolute paths have no .. components.
87 // All .. components of a relative path are initial.
88 List components = path.split('/');
Mads Ager (google) 2012/06/01 08:11:31 segments and isAbsolute instead of split and check
Bill Hesse 2012/06/15 14:23:08 Yes, I was just optimizing. This avoids a removeR
89 if (components[0] == '') { // Absolute path
90 components[0] = 'Okay';
Mads Ager (google) 2012/06/01 08:11:31 WAT?
Bill Hesse 2012/06/15 14:23:08 Done.
91 } else { // Relative path starting with .. components.
92 for (int pos = 0;
93 pos < components.length && components[pos] == '..';
94 ++pos) {
95 components[pos] = 'Okay';
Mads Ager (google) 2012/06/01 08:11:31 Ditto?
Bill Hesse 2012/06/15 14:23:08 Done.
96 }
97 }
98 if (components.isEmpty()) return true;
Mads Ager (google) 2012/06/01 08:11:31 ? You have just accessed components[0]. We need mo
Bill Hesse 2012/06/15 14:23:08 Done.
99 if (components.last() == '') components.removeLast(); // Path ends with /.
100 // No remaining components can be ., .., or empty.
101 return !components.some((c) => c == '..' || c == '.' || c == '');
102 }
103
104 Path makeCanonical() {
105 bool isAbs = isAbsolute;
Mads Ager (google) 2012/06/01 08:11:31 Why? Isn't 'isAbsolute' a fine name?
Bill Hesse 2012/06/15 14:23:08 I was just optimizing. It is used about 5 times.
106 List components = path.split('/');
Mads Ager (google) 2012/06/01 08:11:31 path.split -> segments?
Bill Hesse 2012/06/15 14:23:08 Done.
107 Expect.isNotNull(components);
Mads Ager (google) 2012/06/01 08:11:31 If this is needed here it probably is elsewhere to
Bill Hesse 2012/06/15 14:23:08 Done.
108 String drive;
109 if (isAbs) {
110 components.removeRange(0, 1);
111 }
112 if (isAbs &&
Mads Ager (google) 2012/06/01 08:11:31 Move this if inside the if above instead of repeat
Bill Hesse 2012/06/15 14:23:08 This is only hit in the exact case it needs to be
113 !components.isEmpty() &&
114 components[0].length == 2 &&
115 components[0][1] == ':') {
116 drive = components[0];
117 components.removeRange(0, 1);
118 }
119 List newComponents = [];
120 for (String segment in components) {
121 switch (segment) {
122 case '..':
123 // Absolute paths drop leading .. markers, including after a drive.
124 if (newComponents.isEmpty()) {
125 if (isAbs) {
126 // Do nothing: drop the segment.
127 } else {
128 newComponents.add('..');
129 }
130 } else if (newComponents.last() == '..') {
131 newComponents.add('..');
132 } else {
133 newComponents.removeLast();
134 }
135 break;
136 case '.':
137 case '':
138 // Do nothing - drop the segment.
139 break;
140 default:
141 newComponents.add(segment);
142 break;
143 }
144 }
145
146 List segmentsToJoin = [];
147 if (isAbs) {
148 segmentsToJoin.add('');
149 if (drive != null) {
150 segmentsToJoin.add(drive);
151 }
152 }
153 if (newComponents.isEmpty()) {
154 if (isAbs) {
155 segmentsToJoin.add('');
156 } else {
157 segmentsToJoin.add('.');
158 }
159 } else {
160 segmentsToJoin.addAll(newComponents);
161 if (hasTrailingSlash) {
162 segmentsToJoin.add('');
163 }
164 }
165 return new Path(Strings.join(segmentsToJoin, '/'));
166 }
167
168
169 String toNativePath() {
170 if (Platform.operatingSystem == 'windows') {
171 String nativePath = path;
172 // Drop '/' before a drive letter.
173 if (nativePath.startsWith('/') && nativePath[2] == ':') {
174 nativePath = nativePath.substring(1);
175 }
176 nativePath = nativePath.replace('/', '\\');
177 return nativePath;
178 }
179 return path;
180 }
181
182 List<String> segments() {
183 List result = path.split('/');
184 if (isAbsolute) result.removeRange(0, 1);
185 if (hasTrailingSlash) result.removeLast();
186 return result;
187 }
188
189 String get filenameWithoutExtension() {
190 var name = filename;
191 int pos = name.lastIndexOf('.');
192 return (pos < 0) ? name : name.substring(0, pos);
193 }
194
195 String get extension() {
196 var name = filename;
197 int pos = name.lastIndexOf('.');
198 return (pos < 0) ? '' : name.substring(pos + 1);
199 }
200
201 Path get directoryPath() {
202 int pos = path.lastIndexOf('/');
203 if (pos < 0) return new Path('');
204 while (pos > 0 && path[pos - 1] == '/') --pos;
205 return new Path((pos > 0) ? path.substring(0, pos) : '/');
206 }
207
208 String get filename() {
209 int pos = path.lastIndexOf('/');
210 return path.substring(pos + 1);
211 }
212 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698