OLD | NEW |
| (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 part of dart.io; | |
6 | |
7 class _Directory extends FileSystemEntity implements Directory { | |
8 final String path; | |
9 | |
10 _Directory(this.path) { | |
11 if (path is! String) { | |
12 throw new ArgumentError('${Error.safeToString(path)} ' | |
13 'is not a String'); | |
14 } | |
15 } | |
16 | |
17 external static _current(); | |
18 external static _setCurrent(path); | |
19 external static _createTemp(String path); | |
20 external static String _systemTemp(); | |
21 external static _exists(String path); | |
22 external static _create(String path); | |
23 external static _deleteNative(String path, bool recursive); | |
24 external static _rename(String path, String newPath); | |
25 external static void _fillWithDirectoryListing( | |
26 List<FileSystemEntity> list, String path, bool recursive, | |
27 bool followLinks); | |
28 | |
29 static Directory get current { | |
30 var result = _current(); | |
31 if (result is OSError) { | |
32 throw new FileSystemException( | |
33 "Getting current working directory failed", "", result); | |
34 } | |
35 return new _Directory(result); | |
36 } | |
37 | |
38 static void set current(path) { | |
39 if (path is Directory) path = path.path; | |
40 var result = _setCurrent(path); | |
41 if (result is ArgumentError) throw result; | |
42 if (result is OSError) { | |
43 throw new FileSystemException( | |
44 "Setting current working directory failed", path, result); | |
45 } | |
46 } | |
47 | |
48 Uri get uri { | |
49 return new Uri.directory(path); | |
50 } | |
51 | |
52 Future<bool> exists() { | |
53 return _IOService._dispatch(_DIRECTORY_EXISTS, [path]).then((response) { | |
54 if (_isErrorResponse(response)) { | |
55 throw _exceptionOrErrorFromResponse(response, "Exists failed"); | |
56 } | |
57 return response == 1; | |
58 }); | |
59 } | |
60 | |
61 bool existsSync() { | |
62 var result = _exists(path); | |
63 if (result is OSError) { | |
64 throw new FileSystemException("Exists failed", path, result); | |
65 } | |
66 return (result == 1); | |
67 } | |
68 | |
69 Directory get absolute => new Directory(_absolutePath); | |
70 | |
71 Future<FileStat> stat() => FileStat.stat(path); | |
72 | |
73 FileStat statSync() => FileStat.statSync(path); | |
74 | |
75 // Compute the index of the first directory in the list that exists. If | |
76 // none of the directories exist dirsToCreate.length is returned. | |
77 Future<int> _computeExistingIndex(List dirsToCreate) { | |
78 var future; | |
79 var notFound = dirsToCreate.length; | |
80 for (var i = 0; i < dirsToCreate.length; i++) { | |
81 if (future == null) { | |
82 future = dirsToCreate[i].exists().then((e) => e ? i : notFound); | |
83 } else { | |
84 future = future.then((index) { | |
85 if (index != notFound) { | |
86 return new Future.value(index); | |
87 } | |
88 return dirsToCreate[i].exists().then((e) => e ? i : notFound); | |
89 }); | |
90 } | |
91 } | |
92 if (future == null) { | |
93 return new Future.value(notFound); | |
94 } else { | |
95 return future; | |
96 } | |
97 } | |
98 | |
99 Future<Directory> create({bool recursive: false}) { | |
100 if (recursive) { | |
101 return exists().then((exists) { | |
102 if (exists) return this; | |
103 if (path != parent.path) { | |
104 return parent.create(recursive: true).then((_) { | |
105 return create(); | |
106 }); | |
107 } else { | |
108 return create(); | |
109 } | |
110 }); | |
111 } else { | |
112 return _IOService._dispatch(_DIRECTORY_CREATE, [path]).then((response) { | |
113 if (_isErrorResponse(response)) { | |
114 throw _exceptionOrErrorFromResponse(response, "Creation failed"); | |
115 } | |
116 return this; | |
117 }); | |
118 } | |
119 } | |
120 | |
121 void createSync({bool recursive: false}) { | |
122 if (recursive) { | |
123 if (existsSync()) return; | |
124 if (path != parent.path) { | |
125 parent.createSync(recursive: true); | |
126 } | |
127 } | |
128 var result = _create(path); | |
129 if (result is OSError) { | |
130 throw new FileSystemException("Creation failed", path, result); | |
131 } | |
132 } | |
133 | |
134 static Directory get systemTemp => new Directory(_systemTemp()); | |
135 | |
136 Future<Directory> createTemp([String prefix]) { | |
137 if (prefix == null) prefix = ''; | |
138 if (path == '') { | |
139 throw new ArgumentError( | |
140 "Directory.createTemp called with an empty path. " | |
141 "To use the system temp directory, use Directory.systemTemp"); | |
142 } | |
143 String fullPrefix; | |
144 if (path.endsWith('/') || (Platform.isWindows && path.endsWith('\\'))) { | |
145 fullPrefix = "$path$prefix"; | |
146 } else { | |
147 fullPrefix = "$path${Platform.pathSeparator}$prefix"; | |
148 } | |
149 return _IOService._dispatch(_DIRECTORY_CREATE_TEMP, [fullPrefix]) | |
150 .then((response) { | |
151 if (_isErrorResponse(response)) { | |
152 throw _exceptionOrErrorFromResponse( | |
153 response, "Creation of temporary directory failed"); | |
154 } | |
155 return new Directory(response); | |
156 }); | |
157 } | |
158 | |
159 Directory createTempSync([String prefix]) { | |
160 if (prefix == null) prefix = ''; | |
161 if (path == '') { | |
162 throw new ArgumentError( | |
163 "Directory.createTemp called with an empty path. " | |
164 "To use the system temp directory, use Directory.systemTemp"); | |
165 } | |
166 String fullPrefix; | |
167 if (path.endsWith('/') || (Platform.isWindows && path.endsWith('\\'))) { | |
168 fullPrefix = "$path$prefix"; | |
169 } else { | |
170 fullPrefix = "$path${Platform.pathSeparator}$prefix"; | |
171 } | |
172 var result = _createTemp(fullPrefix); | |
173 if (result is OSError) { | |
174 throw new FileSystemException("Creation of temporary directory failed", | |
175 fullPrefix, | |
176 result); | |
177 } | |
178 return new Directory(result); | |
179 } | |
180 | |
181 Future<Directory> _delete({bool recursive: false}) { | |
182 return _IOService._dispatch(_DIRECTORY_DELETE, [path, recursive]) | |
183 .then((response) { | |
184 if (_isErrorResponse(response)) { | |
185 throw _exceptionOrErrorFromResponse(response, "Deletion failed"); | |
186 } | |
187 return this; | |
188 }); | |
189 } | |
190 | |
191 void _deleteSync({bool recursive: false}) { | |
192 var result = _deleteNative(path, recursive); | |
193 if (result is OSError) { | |
194 throw new FileSystemException("Deletion failed", path, result); | |
195 } | |
196 } | |
197 | |
198 Future<Directory> rename(String newPath) { | |
199 return _IOService._dispatch(_DIRECTORY_RENAME, [path, newPath]) | |
200 .then((response) { | |
201 if (_isErrorResponse(response)) { | |
202 throw _exceptionOrErrorFromResponse(response, "Rename failed"); | |
203 } | |
204 return new Directory(newPath); | |
205 }); | |
206 } | |
207 | |
208 Directory renameSync(String newPath) { | |
209 if (newPath is !String) { | |
210 throw new ArgumentError(); | |
211 } | |
212 var result = _rename(path, newPath); | |
213 if (result is OSError) { | |
214 throw new FileSystemException("Rename failed", path, result); | |
215 } | |
216 return new Directory(newPath); | |
217 } | |
218 | |
219 Stream<FileSystemEntity> list({bool recursive: false, | |
220 bool followLinks: true}) { | |
221 return new _AsyncDirectoryLister( | |
222 FileSystemEntity._ensureTrailingPathSeparators(path), | |
223 recursive, | |
224 followLinks).stream; | |
225 } | |
226 | |
227 List<FileSystemEntity> listSync( | |
228 {bool recursive: false, bool followLinks: true}) { | |
229 if (recursive is! bool || followLinks is! bool) { | |
230 throw new ArgumentError(); | |
231 } | |
232 var result = <FileSystemEntity>[]; | |
233 _fillWithDirectoryListing( | |
234 result, | |
235 FileSystemEntity._ensureTrailingPathSeparators(path), | |
236 recursive, | |
237 followLinks); | |
238 return result; | |
239 } | |
240 | |
241 String toString() => "Directory: '$path'"; | |
242 | |
243 bool _isErrorResponse(response) => | |
244 response is List && response[0] != _SUCCESS_RESPONSE; | |
245 | |
246 _exceptionOrErrorFromResponse(response, String message) { | |
247 assert(_isErrorResponse(response)); | |
248 switch (response[_ERROR_RESPONSE_ERROR_TYPE]) { | |
249 case _ILLEGAL_ARGUMENT_RESPONSE: | |
250 return new ArgumentError(); | |
251 case _OSERROR_RESPONSE: | |
252 var err = new OSError(response[_OSERROR_RESPONSE_MESSAGE], | |
253 response[_OSERROR_RESPONSE_ERROR_CODE]); | |
254 return new FileSystemException(message, path, err); | |
255 default: | |
256 return new Exception("Unknown error"); | |
257 } | |
258 } | |
259 } | |
260 | |
261 abstract class _AsyncDirectoryListerOps { | |
262 external factory _AsyncDirectoryListerOps(int pointer); | |
263 | |
264 int getPointer(); | |
265 } | |
266 | |
267 class _AsyncDirectoryLister { | |
268 static const int LIST_FILE = 0; | |
269 static const int LIST_DIRECTORY = 1; | |
270 static const int LIST_LINK = 2; | |
271 static const int LIST_ERROR = 3; | |
272 static const int LIST_DONE = 4; | |
273 | |
274 static const int RESPONSE_TYPE = 0; | |
275 static const int RESPONSE_PATH = 1; | |
276 static const int RESPONSE_COMPLETE = 1; | |
277 static const int RESPONSE_ERROR = 2; | |
278 | |
279 final String path; | |
280 final bool recursive; | |
281 final bool followLinks; | |
282 | |
283 StreamController controller; | |
284 bool canceled = false; | |
285 bool nextRunning = false; | |
286 bool closed = false; | |
287 _AsyncDirectoryListerOps _ops; | |
288 Completer closeCompleter = new Completer(); | |
289 | |
290 _AsyncDirectoryLister(this.path, this.recursive, this.followLinks) { | |
291 controller = new StreamController(onListen: onListen, | |
292 onResume: onResume, | |
293 onCancel: onCancel, | |
294 sync: true); | |
295 } | |
296 | |
297 // Calling this function will increase the reference count on the native | |
298 // object that implements the async directory lister operations. It should | |
299 // only be called to pass the pointer to the IO Service, which will decrement | |
300 // the reference count when it is finished with it. | |
301 int _pointer() { | |
302 return (_ops == null) ? null : _ops.getPointer(); | |
303 } | |
304 | |
305 Stream get stream => controller.stream; | |
306 | |
307 void onListen() { | |
308 _IOService._dispatch(_DIRECTORY_LIST_START, [path, recursive, followLinks]) | |
309 .then((response) { | |
310 if (response is int) { | |
311 _ops = new _AsyncDirectoryListerOps(response); | |
312 next(); | |
313 } else if (response is Error) { | |
314 controller.addError(response, response.stackTrace); | |
315 close(); | |
316 } else { | |
317 error(response); | |
318 close(); | |
319 } | |
320 }); | |
321 } | |
322 | |
323 void onResume() { | |
324 if (!nextRunning) { | |
325 next(); | |
326 } | |
327 } | |
328 | |
329 Future onCancel() { | |
330 canceled = true; | |
331 // If we are active, but not requesting, close. | |
332 if (!nextRunning) { | |
333 close(); | |
334 } | |
335 | |
336 return closeCompleter.future; | |
337 } | |
338 | |
339 void next() { | |
340 if (canceled) { | |
341 close(); | |
342 return; | |
343 } | |
344 if (controller.isPaused || nextRunning) { | |
345 return; | |
346 } | |
347 var pointer = _pointer(); | |
348 if (pointer == null) { | |
349 return; | |
350 } | |
351 nextRunning = true; | |
352 _IOService._dispatch(_DIRECTORY_LIST_NEXT, [pointer]).then((result) { | |
353 nextRunning = false; | |
354 if (result is List) { | |
355 next(); | |
356 assert(result.length % 2 == 0); | |
357 for (int i = 0; i < result.length; i++) { | |
358 assert(i % 2 == 0); | |
359 switch (result[i++]) { | |
360 case LIST_FILE: | |
361 controller.add(new File(result[i])); | |
362 break; | |
363 case LIST_DIRECTORY: | |
364 controller.add(new Directory(result[i])); | |
365 break; | |
366 case LIST_LINK: | |
367 controller.add(new Link(result[i])); | |
368 break; | |
369 case LIST_ERROR: | |
370 error(result[i]); | |
371 break; | |
372 case LIST_DONE: | |
373 canceled = true; | |
374 return; | |
375 } | |
376 } | |
377 } else { | |
378 controller.addError(new FileSystemException("Internal error")); | |
379 } | |
380 }); | |
381 } | |
382 | |
383 void _cleanup() { | |
384 controller.close(); | |
385 closeCompleter.complete(); | |
386 _ops = null; | |
387 } | |
388 | |
389 void close() { | |
390 if (closed) { | |
391 return; | |
392 } | |
393 if (nextRunning) { | |
394 return; | |
395 } | |
396 closed = true; | |
397 | |
398 var pointer = _pointer(); | |
399 if (pointer == null) { | |
400 _cleanup(); | |
401 } else { | |
402 _IOService._dispatch(_DIRECTORY_LIST_STOP, [pointer]) | |
403 .whenComplete(_cleanup); | |
404 } | |
405 } | |
406 | |
407 void error(message) { | |
408 var errorType = | |
409 message[RESPONSE_ERROR][_ERROR_RESPONSE_ERROR_TYPE]; | |
410 if (errorType == _ILLEGAL_ARGUMENT_RESPONSE) { | |
411 controller.addError(new ArgumentError()); | |
412 } else if (errorType == _OSERROR_RESPONSE) { | |
413 var responseError = message[RESPONSE_ERROR]; | |
414 var err = new OSError( | |
415 responseError[_OSERROR_RESPONSE_MESSAGE], | |
416 responseError[_OSERROR_RESPONSE_ERROR_CODE]); | |
417 var errorPath = message[RESPONSE_PATH]; | |
418 if (errorPath == null) errorPath = path; | |
419 controller.addError( | |
420 new FileSystemException("Directory listing failed", | |
421 errorPath, | |
422 err)); | |
423 } else { | |
424 controller.addError( | |
425 new FileSystemException("Internal error")); | |
426 } | |
427 } | |
428 } | |
OLD | NEW |