OLD | NEW |
| (Empty) |
1 // Copyright (c) 2013, 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 /** | |
8 * The type of an entity on the file system, such as a file, directory, or link. | |
9 * | |
10 * These constants are used by the [FileSystemEntity] class | |
11 * to indicate the object's type. | |
12 * | |
13 */ | |
14 | |
15 class FileSystemEntityType { | |
16 static const FILE = const FileSystemEntityType._internal(0); | |
17 static const DIRECTORY = const FileSystemEntityType._internal(1); | |
18 static const LINK = const FileSystemEntityType._internal(2); | |
19 static const NOT_FOUND = const FileSystemEntityType._internal(3); | |
20 static const _typeList = const [FileSystemEntityType.FILE, | |
21 FileSystemEntityType.DIRECTORY, | |
22 FileSystemEntityType.LINK, | |
23 FileSystemEntityType.NOT_FOUND]; | |
24 final int _type; | |
25 | |
26 const FileSystemEntityType._internal(this._type); | |
27 | |
28 static FileSystemEntityType _lookup(int type) => _typeList[type]; | |
29 String toString() => const ['FILE', 'DIRECTORY', 'LINK', 'NOT_FOUND'][_type]; | |
30 } | |
31 | |
32 /** | |
33 * A FileStat object represents the result of calling the POSIX stat() function | |
34 * on a file system object. It is an immutable object, representing the | |
35 * snapshotted values returned by the stat() call. | |
36 */ | |
37 class FileStat { | |
38 // These must agree with enum FileStat in file.h. | |
39 static const _TYPE = 0; | |
40 static const _CHANGED_TIME = 1; | |
41 static const _MODIFIED_TIME = 2; | |
42 static const _ACCESSED_TIME = 3; | |
43 static const _MODE = 4; | |
44 static const _SIZE = 5; | |
45 | |
46 static const _notFound = const FileStat._internalNotFound(); | |
47 | |
48 /** | |
49 * The time of the last change to the data or metadata of the file system | |
50 * object. On Windows platforms, this is instead the file creation time. | |
51 */ | |
52 final DateTime changed; | |
53 /** | |
54 * The time of the last change to the data of the file system | |
55 * object. | |
56 */ | |
57 final DateTime modified; | |
58 /** | |
59 * The time of the last access to the data of the file system | |
60 * object. On Windows platforms, this may have 1 day granularity, and be | |
61 * out of date by an hour. | |
62 */ | |
63 final DateTime accessed; | |
64 /** | |
65 * The type of the object (file, directory, or link). If the call to | |
66 * stat() fails, the type of the returned object is NOT_FOUND. | |
67 */ | |
68 final FileSystemEntityType type; | |
69 /** | |
70 * The mode of the file system object. Permissions are encoded in the lower | |
71 * 16 bits of this number, and can be decoded using the [modeString] getter. | |
72 */ | |
73 final int mode; | |
74 /** | |
75 * The size of the file system object. | |
76 */ | |
77 final int size; | |
78 | |
79 FileStat._internal(this.changed, | |
80 this.modified, | |
81 this.accessed, | |
82 this.type, | |
83 this.mode, | |
84 this.size); | |
85 | |
86 const FileStat._internalNotFound() : | |
87 changed = null, modified = null, accessed = null, | |
88 type = FileSystemEntityType.NOT_FOUND, mode = 0, size = -1; | |
89 | |
90 external static _statSync(String path); | |
91 | |
92 | |
93 /** | |
94 * Calls the operating system's stat() function on [path]. | |
95 * Returns a [FileStat] object containing the data returned by stat(). | |
96 * If the call fails, returns a [FileStat] object with .type set to | |
97 * FileSystemEntityType.NOT_FOUND and the other fields invalid. | |
98 */ | |
99 static FileStat statSync(String path) { | |
100 // Trailing path is not supported on Windows. | |
101 if (Platform.isWindows) { | |
102 path = FileSystemEntity._trimTrailingPathSeparators(path); | |
103 } | |
104 var data = _statSync(path); | |
105 if (data is OSError) return FileStat._notFound; | |
106 return new FileStat._internal( | |
107 new DateTime.fromMillisecondsSinceEpoch(data[_CHANGED_TIME]), | |
108 new DateTime.fromMillisecondsSinceEpoch(data[_MODIFIED_TIME]), | |
109 new DateTime.fromMillisecondsSinceEpoch(data[_ACCESSED_TIME]), | |
110 FileSystemEntityType._lookup(data[_TYPE]), | |
111 data[_MODE], | |
112 data[_SIZE]); | |
113 } | |
114 | |
115 /** | |
116 * Asynchronously calls the operating system's stat() function on [path]. | |
117 * Returns a Future which completes with a [FileStat] object containing | |
118 * the data returned by stat(). | |
119 * If the call fails, completes the future with a [FileStat] object with | |
120 * .type set to FileSystemEntityType.NOT_FOUND and the other fields invalid. | |
121 */ | |
122 static Future<FileStat> stat(String path) { | |
123 // Trailing path is not supported on Windows. | |
124 if (Platform.isWindows) { | |
125 path = FileSystemEntity._trimTrailingPathSeparators(path); | |
126 } | |
127 return _IOService._dispatch(_FILE_STAT, [path]).then((response) { | |
128 if (_isErrorResponse(response)) { | |
129 return FileStat._notFound; | |
130 } | |
131 // Unwrap the real list from the "I'm not an error" wrapper. | |
132 List data = response[1]; | |
133 return new FileStat._internal( | |
134 new DateTime.fromMillisecondsSinceEpoch(data[_CHANGED_TIME]), | |
135 new DateTime.fromMillisecondsSinceEpoch(data[_MODIFIED_TIME]), | |
136 new DateTime.fromMillisecondsSinceEpoch(data[_ACCESSED_TIME]), | |
137 FileSystemEntityType._lookup(data[_TYPE]), | |
138 data[_MODE], | |
139 data[_SIZE]); | |
140 }); | |
141 } | |
142 | |
143 String toString() => """ | |
144 FileStat: type $type | |
145 changed $changed | |
146 modified $modified | |
147 accessed $accessed | |
148 mode ${modeString()} | |
149 size $size"""; | |
150 | |
151 /** | |
152 * Returns the mode value as a human-readable string, in the format | |
153 * "rwxrwxrwx", reflecting the user, group, and world permissions to | |
154 * read, write, and execute the file system object, with "-" replacing the | |
155 * letter for missing permissions. Extra permission bits may be represented | |
156 * by prepending "(suid)", "(guid)", and/or "(sticky)" to the mode string. | |
157 */ | |
158 String modeString() { | |
159 var permissions = mode & 0xFFF; | |
160 var codes = const ['---', '--x', '-w-', '-wx', 'r--', 'r-x', 'rw-', 'rwx']; | |
161 var result = []; | |
162 if ((permissions & 0x800) != 0) result.add("(suid) "); | |
163 if ((permissions & 0x400) != 0) result.add("(guid) "); | |
164 if ((permissions & 0x200) != 0) result.add("(sticky) "); | |
165 result | |
166 ..add(codes[(permissions >> 6) & 0x7]) | |
167 ..add(codes[(permissions >> 3) & 0x7]) | |
168 ..add(codes[permissions & 0x7]); | |
169 return result.join(); | |
170 } | |
171 } | |
172 | |
173 | |
174 /** | |
175 * The common super class for [File], [Directory], and [Link] objects. | |
176 * | |
177 * [FileSystemEntity] objects are returned from directory listing | |
178 * operations. To determine if a FileSystemEntity is a [File], a | |
179 * [Directory], or a [Link] perform a type check: | |
180 * | |
181 * if (entity is File) (entity as File).readAsStringSync(); | |
182 * | |
183 * You can also use the [type] or [typeSync] methods to determine | |
184 * the type of a file system object. | |
185 * | |
186 * Most methods in this class occur in synchronous and asynchronous pairs, | |
187 * for example, [exists] and [existsSync]. | |
188 * Unless you have a specific reason for using the synchronous version | |
189 * of a method, prefer the asynchronous version to avoid blocking your program. | |
190 * | |
191 * Here's the exists method in action: | |
192 * | |
193 * entity.exists().then((isThere) { | |
194 * isThere ? print('exists') : print('non-existent'); | |
195 * }); | |
196 * | |
197 * | |
198 * ## Other resources | |
199 * | |
200 * * [Dart by | |
201 * Example](https://www.dartlang.org/dart-by-example/#files-directories-and-sy
mlinks) | |
202 * provides additional task-oriented code samples that show how to use various | |
203 * API from the [Directory] class and the [File] class, both subclasses of | |
204 * FileSystemEntity. | |
205 * | |
206 * * [I/O for Command-Line | |
207 * Apps](https://www.dartlang.org/docs/dart-up-and-running/ch03.html#dartio---
io-for-command-line-apps), | |
208 * a section from _A Tour of the Dart Libraries_ covers files and directories. | |
209 * | |
210 * * [Write Command-Line Apps](https://www.dartlang.org/docs/tutorials/cmdline/)
, | |
211 * a tutorial about writing command-line apps, includes information about | |
212 * files and directories. | |
213 */ | |
214 abstract class FileSystemEntity { | |
215 String get path; | |
216 | |
217 /** | |
218 * Returns a [Uri] representing the file system entity's location. | |
219 * | |
220 * The returned URI's scheme is always "file" if the entity's [path] is | |
221 * absolute, otherwise the scheme will be empty. | |
222 */ | |
223 Uri get uri => new Uri.file(path); | |
224 | |
225 /** | |
226 * Checks whether the file system entity with this path exists. Returns | |
227 * a [:Future<bool>:] that completes with the result. | |
228 * | |
229 * Since FileSystemEntity is abstract, every FileSystemEntity object | |
230 * is actually an instance of one of the subclasses [File], | |
231 * [Directory], and [Link]. Calling [exists] on an instance of one | |
232 * of these subclasses checks whether the object exists in the file | |
233 * system object exists and is of the correct type (file, directory, | |
234 * or link). To check whether a path points to an object on the | |
235 * file system, regardless of the object's type, use the [type] | |
236 * static method. | |
237 * | |
238 */ | |
239 Future<bool> exists(); | |
240 | |
241 /** | |
242 * Synchronously checks whether the file system entity with this path | |
243 * exists. | |
244 * | |
245 * Since FileSystemEntity is abstract, every FileSystemEntity object | |
246 * is actually an instance of one of the subclasses [File], | |
247 * [Directory], and [Link]. Calling [existsSync] on an instance of | |
248 * one of these subclasses checks whether the object exists in the | |
249 * file system object exists and is of the correct type (file, | |
250 * directory, or link). To check whether a path points to an object | |
251 * on the file system, regardless of the object's type, use the | |
252 * [typeSync] static method. | |
253 */ | |
254 bool existsSync(); | |
255 | |
256 /** | |
257 * Renames this file system entity. Returns a `Future<FileSystemEntity>` | |
258 * that completes with a [FileSystemEntity] instance for the renamed | |
259 * file system entity. | |
260 * | |
261 * If [newPath] identifies an existing entity of the same type, that entity | |
262 * is replaced. If [newPath] identifies an existing entity of a different | |
263 * type, the operation fails and the future completes with an exception. | |
264 */ | |
265 Future<FileSystemEntity> rename(String newPath); | |
266 | |
267 /** | |
268 * Synchronously renames this file system entity. Returns a [FileSystemEntity] | |
269 * instance for the renamed entity. | |
270 * | |
271 * If [newPath] identifies an existing entity of the same type, that entity | |
272 * is replaced. If [newPath] identifies an existing entity of a different | |
273 * type, the operation fails and an exception is thrown. | |
274 */ | |
275 FileSystemEntity renameSync(String newPath); | |
276 | |
277 /** | |
278 * Resolves the path of a file system object relative to the | |
279 * current working directory, resolving all symbolic links on | |
280 * the path and resolving all `..` and `.` path segments. | |
281 * | |
282 * [resolveSymbolicLinks] uses the operating system's native | |
283 * file system API to resolve the path, using the `realpath` function | |
284 * on linux and OS X, and the `GetFinalPathNameByHandle` function on | |
285 * Windows. If the path does not point to an existing file system object, | |
286 * `resolveSymbolicLinks` throws a `FileSystemException`. | |
287 * | |
288 * On Windows the `..` segments are resolved _before_ resolving the symbolic | |
289 * link, and on other platforms the symbolic links are _resolved to their | |
290 * target_ before applying a `..` that follows. | |
291 * | |
292 * To ensure the same behavior on all platforms resolve `..` segments before | |
293 * calling `resolveSymbolicLinks`. One way of doing this is with the `Uri` | |
294 * class: | |
295 * | |
296 * var path = Uri.parse('.').resolveUri(new Uri.file(input)).toFilePath(); | |
297 * if (path == '') path = '.'; | |
298 * new File(path).resolveSymbolicLinks().then((resolved) { | |
299 * print(resolved); | |
300 * }); | |
301 * | |
302 * since `Uri.resolve` removes `..` segments. This will result in the Windows | |
303 * behavior. | |
304 */ | |
305 Future<String> resolveSymbolicLinks() { | |
306 return _IOService._dispatch(_FILE_RESOLVE_SYMBOLIC_LINKS, [path]) | |
307 .then((response) { | |
308 if (_isErrorResponse(response)) { | |
309 throw _exceptionFromResponse(response, | |
310 "Cannot resolve symbolic links", | |
311 path); | |
312 } | |
313 return response; | |
314 }); | |
315 } | |
316 | |
317 /** | |
318 * Resolves the path of a file system object relative to the | |
319 * current working directory, resolving all symbolic links on | |
320 * the path and resolving all `..` and `.` path segments. | |
321 * | |
322 * [resolveSymbolicLinksSync] uses the operating system's native | |
323 * file system API to resolve the path, using the `realpath` function | |
324 * on linux and OS X, and the `GetFinalPathNameByHandle` function on | |
325 * Windows. If the path does not point to an existing file system object, | |
326 * `resolveSymbolicLinksSync` throws a `FileSystemException`. | |
327 * | |
328 * On Windows the `..` segments are resolved _before_ resolving the symbolic | |
329 * link, and on other platforms the symbolic links are _resolved to their | |
330 * target_ before applying a `..` that follows. | |
331 * | |
332 * To ensure the same behavior on all platforms resolve `..` segments before | |
333 * calling `resolveSymbolicLinksSync`. One way of doing this is with the `Uri` | |
334 * class: | |
335 * | |
336 * var path = Uri.parse('.').resolveUri(new Uri.file(input)).toFilePath(); | |
337 * if (path == '') path = '.'; | |
338 * var resolved = new File(path).resolveSymbolicLinksSync(); | |
339 * print(resolved); | |
340 * | |
341 * since `Uri.resolve` removes `..` segments. This will result in the Windows | |
342 * behavior. | |
343 */ | |
344 String resolveSymbolicLinksSync() { | |
345 var result = _resolveSymbolicLinks(path); | |
346 _throwIfError(result, "Cannot resolve symbolic links", path); | |
347 return result; | |
348 } | |
349 | |
350 | |
351 /** | |
352 * Calls the operating system's stat() function on the [path] of this | |
353 * [FileSystemEntity]. Identical to [:FileStat.stat(this.path):]. | |
354 * | |
355 * Returns a [:Future<FileStat>:] object containing the data returned by | |
356 * stat(). | |
357 * | |
358 * If the call fails, completes the future with a [FileStat] object | |
359 * with .type set to | |
360 * FileSystemEntityType.NOT_FOUND and the other fields invalid. | |
361 */ | |
362 Future<FileStat> stat(); | |
363 | |
364 /** | |
365 * Synchronously calls the operating system's stat() function on the | |
366 * [path] of this [FileSystemEntity]. | |
367 * Identical to [:FileStat.statSync(this.path):]. | |
368 * | |
369 * Returns a [FileStat] object containing the data returned by stat(). | |
370 * | |
371 * If the call fails, returns a [FileStat] object with .type set to | |
372 * FileSystemEntityType.NOT_FOUND and the other fields invalid. | |
373 */ | |
374 FileStat statSync(); | |
375 | |
376 /** | |
377 * Deletes this [FileSystemEntity]. | |
378 * | |
379 * If the [FileSystemEntity] is a directory, and if [recursive] is false, | |
380 * the directory must be empty. Otherwise, if [recursive] is true, the | |
381 * directory and all sub-directories and files in the directories are | |
382 * deleted. Links are not followed when deleting recursively. Only the link | |
383 * is deleted, not its target. | |
384 * | |
385 * If [recursive] is true, the [FileSystemEntity] is deleted even if the type | |
386 * of the [FileSystemEntity] doesn't match the content of the file system. | |
387 * This behavior allows [delete] to be used to unconditionally delete any file | |
388 * system object. | |
389 * | |
390 * Returns a [:Future<FileSystemEntity>:] that completes with this | |
391 * [FileSystemEntity] when the deletion is done. If the [FileSystemEntity] | |
392 * cannot be deleted, the future completes with an exception. | |
393 */ | |
394 Future<FileSystemEntity> delete({bool recursive: false}) | |
395 => _delete(recursive: recursive); | |
396 | |
397 /** | |
398 * Synchronously deletes this [FileSystemEntity]. | |
399 * | |
400 * If the [FileSystemEntity] is a directory, and if [recursive] is false, | |
401 * the directory must be empty. Otherwise, if [recursive] is true, the | |
402 * directory and all sub-directories and files in the directories are | |
403 * deleted. Links are not followed when deleting recursively. Only the link | |
404 * is deleted, not its target. | |
405 * | |
406 * If [recursive] is true, the [FileSystemEntity] is deleted even if the type | |
407 * of the [FileSystemEntity] doesn't match the content of the file system. | |
408 * This behavior allows [deleteSync] to be used to unconditionally delete any | |
409 * file system object. | |
410 * | |
411 * Throws an exception if the [FileSystemEntity] cannot be deleted. | |
412 */ | |
413 void deleteSync({bool recursive: false}) | |
414 => _deleteSync(recursive: recursive); | |
415 | |
416 | |
417 /** | |
418 * Start watching the [FileSystemEntity] for changes. | |
419 * | |
420 * The implementation uses platform-dependent event-based APIs for receiving | |
421 * file-system notifications, thus behavior depends on the platform. | |
422 * | |
423 * * `Windows`: Uses `ReadDirectoryChangesW`. The implementation only | |
424 * supports watching directories. Recursive watching is supported. | |
425 * * `Linux`: Uses `inotify`. The implementation supports watching both | |
426 * files and directories. Recursive watching is not supported. | |
427 * Note: When watching files directly, delete events might not happen | |
428 * as expected. | |
429 * * `OS X`: Uses `FSEvents`. The implementation supports watching both | |
430 * files and directories. Recursive watching is supported. | |
431 * | |
432 * The system will start listening for events once the returned [Stream] is | |
433 * being listened to, not when the call to [watch] is issued. | |
434 * | |
435 * The returned value is an endless broadcast [Stream], that only stops when | |
436 * one of the following happens: | |
437 * | |
438 * * The [Stream] is canceled, e.g. by calling `cancel` on the | |
439 * [StreamSubscription]. | |
440 * * The [FileSystemEntity] being watches, is deleted. | |
441 * | |
442 * Use `events` to specify what events to listen for. The constants in | |
443 * [FileSystemEvent] can be or'ed together to mix events. Default is | |
444 * [FileSystemEvent.ALL]. | |
445 * | |
446 * A move event may be reported as seperate delete and create events. | |
447 */ | |
448 Stream<FileSystemEvent> watch({int events: FileSystemEvent.ALL, | |
449 bool recursive: false}) | |
450 => _FileSystemWatcher._watch(_trimTrailingPathSeparators(path), | |
451 events, | |
452 recursive); | |
453 | |
454 Future<FileSystemEntity> _delete({bool recursive: false}); | |
455 void _deleteSync({bool recursive: false}); | |
456 | |
457 /** | |
458 * Checks whether two paths refer to the same object in the | |
459 * file system. Returns a [:Future<bool>:] that completes with the result. | |
460 * | |
461 * Comparing a link to its target returns false, as does comparing two links | |
462 * that point to the same target. To check the target of a link, use | |
463 * Link.target explicitly to fetch it. Directory links appearing | |
464 * inside a path are followed, though, to find the file system object. | |
465 * | |
466 * Completes the returned Future with an error if one of the paths points | |
467 * to an object that does not exist. | |
468 */ | |
469 static Future<bool> identical(String path1, String path2) { | |
470 return _IOService._dispatch(_FILE_IDENTICAL, [path1, path2]).then((response)
{ | |
471 if (_isErrorResponse(response)) { | |
472 throw _exceptionFromResponse(response, | |
473 "Error in FileSystemEntity.identical($path1, $path2)", ""); | |
474 } | |
475 return response; | |
476 }); | |
477 } | |
478 | |
479 static final RegExp _absoluteWindowsPathPattern = | |
480 new RegExp(r'^(\\\\|[a-zA-Z]:[/\\])'); | |
481 | |
482 /** | |
483 * Returns a [bool] indicating whether this object's path is absolute. | |
484 * | |
485 * On Windows, a path is absolute if it starts with \\ or a drive letter | |
486 * between a and z (upper or lower case) followed by :\ or :/. | |
487 * On non-Windows, a path is absolute if it starts with /. | |
488 */ | |
489 bool get isAbsolute { | |
490 if (Platform.isWindows) { | |
491 return path.startsWith(_absoluteWindowsPathPattern); | |
492 } else { | |
493 return path.startsWith('/'); | |
494 } | |
495 } | |
496 | |
497 /** | |
498 * Returns a [FileSystemEntity] whose path is the absolute path to [this]. | |
499 * The type of the returned instance is the type of [this]. | |
500 * | |
501 * The absolute path is computed by prefixing | |
502 * a relative path with the current working directory, and returning | |
503 * an absolute path unchanged. | |
504 */ | |
505 FileSystemEntity get absolute; | |
506 | |
507 String get _absolutePath { | |
508 if (isAbsolute) return path; | |
509 String current = Directory.current.path; | |
510 if (current.endsWith('/') || | |
511 (Platform.isWindows && current.endsWith('\\'))) { | |
512 return '$current$path'; | |
513 } else { | |
514 return '$current${Platform.pathSeparator}$path'; | |
515 } | |
516 } | |
517 | |
518 | |
519 /** | |
520 * Synchronously checks whether two paths refer to the same object in the | |
521 * file system. | |
522 * | |
523 * Comparing a link to its target returns false, as does comparing two links | |
524 * that point to the same target. To check the target of a link, use | |
525 * Link.target explicitly to fetch it. Directory links appearing | |
526 * inside a path are followed, though, to find the file system object. | |
527 * | |
528 * Throws an error if one of the paths points to an object that does not | |
529 * exist. | |
530 */ | |
531 static bool identicalSync(String path1, String path2) { | |
532 var result = _identical(path1, path2); | |
533 _throwIfError(result, 'Error in FileSystemEntity.identicalSync'); | |
534 return result; | |
535 } | |
536 | |
537 /** | |
538 * Test if [watch] is supported on the current system. | |
539 * | |
540 * OS X 10.6 and below is not supported. | |
541 */ | |
542 static bool get isWatchSupported => _FileSystemWatcher.isSupported; | |
543 | |
544 /** | |
545 * Finds the type of file system object that a path points to. Returns | |
546 * a [:Future<FileSystemEntityType>:] that completes with the result. | |
547 * | |
548 * [FileSystemEntityType] has the constant instances FILE, DIRECTORY, | |
549 * LINK, and NOT_FOUND. [type] will return LINK only if the optional | |
550 * named argument [followLinks] is false, and [path] points to a link. | |
551 * If the path does not point to a file system object, or any other error | |
552 * occurs in looking up the path, NOT_FOUND is returned. The only | |
553 * error or exception that may be put on the returned future is ArgumentError, | |
554 * caused by passing the wrong type of arguments to the function. | |
555 */ | |
556 static Future<FileSystemEntityType> type(String path, | |
557 {bool followLinks: true}) | |
558 => _getTypeAsync(path, followLinks).then(FileSystemEntityType._lookup); | |
559 | |
560 /** | |
561 * Synchronously finds the type of file system object that a path points to. | |
562 * Returns a [FileSystemEntityType]. | |
563 * | |
564 * [FileSystemEntityType] has the constant instances FILE, DIRECTORY, | |
565 * LINK, and NOT_FOUND. [type] will return LINK only if the optional | |
566 * named argument [followLinks] is false, and [path] points to a link. | |
567 * If the path does not point to a file system object, or any other error | |
568 * occurs in looking up the path, NOT_FOUND is returned. The only | |
569 * error or exception that may be thrown is ArgumentError, | |
570 * caused by passing the wrong type of arguments to the function. | |
571 */ | |
572 static FileSystemEntityType typeSync(String path, {bool followLinks: true}) | |
573 => FileSystemEntityType._lookup(_getTypeSync(path, followLinks)); | |
574 | |
575 /** | |
576 * Checks if type(path, followLinks: false) returns | |
577 * FileSystemEntityType.LINK. | |
578 */ | |
579 static Future<bool> isLink(String path) => _getTypeAsync(path, false) | |
580 .then((type) => (type == FileSystemEntityType.LINK._type)); | |
581 | |
582 /** | |
583 * Checks if type(path) returns FileSystemEntityType.FILE. | |
584 */ | |
585 static Future<bool> isFile(String path) => _getTypeAsync(path, true) | |
586 .then((type) => (type == FileSystemEntityType.FILE._type)); | |
587 | |
588 /** | |
589 * Checks if type(path) returns FileSystemEntityType.DIRECTORY. | |
590 */ | |
591 static Future<bool> isDirectory(String path) => _getTypeAsync(path, true) | |
592 .then((type) => (type == FileSystemEntityType.DIRECTORY._type)); | |
593 | |
594 /** | |
595 * Synchronously checks if typeSync(path, followLinks: false) returns | |
596 * FileSystemEntityType.LINK. | |
597 */ | |
598 static bool isLinkSync(String path) => | |
599 (_getTypeSync(path, false) == FileSystemEntityType.LINK._type); | |
600 | |
601 /** | |
602 * Synchronously checks if typeSync(path) returns | |
603 * FileSystemEntityType.FILE. | |
604 */ | |
605 static bool isFileSync(String path) => | |
606 (_getTypeSync(path, true) == FileSystemEntityType.FILE._type); | |
607 | |
608 /** | |
609 * Synchronously checks if typeSync(path) returns | |
610 * FileSystemEntityType.DIRECTORY. | |
611 */ | |
612 static bool isDirectorySync(String path) => | |
613 (_getTypeSync(path, true) == FileSystemEntityType.DIRECTORY._type); | |
614 | |
615 external static _getType(String path, bool followLinks); | |
616 external static _identical(String path1, String path2); | |
617 external static _resolveSymbolicLinks(String path); | |
618 | |
619 // Finds the next-to-last component when dividing at path separators. | |
620 static final RegExp _parentRegExp = Platform.isWindows ? | |
621 new RegExp(r'[^/\\][/\\]+[^/\\]') : | |
622 new RegExp(r'[^/]/+[^/]'); | |
623 | |
624 /** | |
625 * Removes the final path component of a path, using the platform's | |
626 * path separator to split the path. Will not remove the root component | |
627 * of a Windows path, like "C:\" or "\\server_name\". | |
628 * Ignores trailing path separators, and leaves no trailing path separators. | |
629 */ | |
630 static String parentOf(String path) { | |
631 int rootEnd = -1; | |
632 if (Platform.isWindows) { | |
633 if (path.startsWith(_absoluteWindowsPathPattern)) { | |
634 // Root ends at first / or \ after the first two characters. | |
635 rootEnd = path.indexOf(new RegExp(r'[/\\]'), 2); | |
636 if (rootEnd == -1) return path; | |
637 } else if (path.startsWith('\\') || path.startsWith('/')) { | |
638 rootEnd = 0; | |
639 } | |
640 } else if (path.startsWith('/')) { | |
641 rootEnd = 0; | |
642 } | |
643 // Ignore trailing slashes. | |
644 // All non-trivial cases have separators between two non-separators. | |
645 int pos = path.lastIndexOf(_parentRegExp); | |
646 if (pos > rootEnd) { | |
647 return path.substring(0, pos + 1); | |
648 } else if (rootEnd > -1) { | |
649 return path.substring(0, rootEnd + 1); | |
650 } else { | |
651 return '.'; | |
652 } | |
653 } | |
654 | |
655 /** | |
656 * The directory containing [this]. If [this] is a root | |
657 * directory, returns [this]. | |
658 */ | |
659 Directory get parent => new Directory(parentOf(path)); | |
660 | |
661 static int _getTypeSync(String path, bool followLinks) { | |
662 var result = _getType(path, followLinks); | |
663 _throwIfError(result, 'Error getting type of FileSystemEntity'); | |
664 return result; | |
665 } | |
666 | |
667 static Future<int> _getTypeAsync(String path, bool followLinks) { | |
668 return _IOService._dispatch(_FILE_TYPE, [path, followLinks]) | |
669 .then((response) { | |
670 if (_isErrorResponse(response)) { | |
671 throw _exceptionFromResponse(response, "Error getting type", path); | |
672 } | |
673 return response; | |
674 }); | |
675 } | |
676 | |
677 static _throwIfError(Object result, String msg, [String path]) { | |
678 if (result is OSError) { | |
679 throw new FileSystemException(msg, path, result); | |
680 } else if (result is ArgumentError) { | |
681 throw result; | |
682 } | |
683 } | |
684 | |
685 static String _trimTrailingPathSeparators(String path) { | |
686 // Don't handle argument errors here. | |
687 if (path is! String) return path; | |
688 if (Platform.isWindows) { | |
689 while (path.length > 1 && | |
690 (path.endsWith(Platform.pathSeparator) || | |
691 path.endsWith('/'))) { | |
692 path = path.substring(0, path.length - 1); | |
693 } | |
694 } else { | |
695 while (path.length > 1 && path.endsWith(Platform.pathSeparator)) { | |
696 path = path.substring(0, path.length - 1); | |
697 } | |
698 } | |
699 return path; | |
700 } | |
701 | |
702 static String _ensureTrailingPathSeparators(String path) { | |
703 // Don't handle argument errors here. | |
704 if (path is! String) return path; | |
705 if (path.isEmpty) path = '.'; | |
706 if (Platform.isWindows) { | |
707 while (!path.endsWith(Platform.pathSeparator) && !path.endsWith('/')) { | |
708 path = "$path${Platform.pathSeparator}"; | |
709 } | |
710 } else { | |
711 while (!path.endsWith(Platform.pathSeparator)) { | |
712 path = "$path${Platform.pathSeparator}"; | |
713 } | |
714 } | |
715 return path; | |
716 } | |
717 } | |
718 | |
719 | |
720 /** | |
721 * Base event class emitted by [FileSystemEntity.watch]. | |
722 */ | |
723 class FileSystemEvent { | |
724 /** | |
725 * Bitfield for [FileSystemEntity.watch], to enable [FileSystemCreateEvent]s. | |
726 */ | |
727 static const int CREATE = 1 << 0; | |
728 | |
729 /** | |
730 * Bitfield for [FileSystemEntity.watch], to enable [FileSystemModifyEvent]s. | |
731 */ | |
732 static const int MODIFY = 1 << 1; | |
733 | |
734 /** | |
735 * Bitfield for [FileSystemEntity.watch], to enable [FileSystemDeleteEvent]s. | |
736 */ | |
737 static const int DELETE = 1 << 2; | |
738 | |
739 /** | |
740 * Bitfield for [FileSystemEntity.watch], to enable [FileSystemMoveEvent]s. | |
741 */ | |
742 static const int MOVE = 1 << 3; | |
743 | |
744 /** | |
745 * Bitfield for [FileSystemEntity.watch], for enabling all of [CREATE], | |
746 * [MODIFY], [DELETE] and [MOVE]. | |
747 */ | |
748 static const int ALL = CREATE | MODIFY | DELETE | MOVE; | |
749 | |
750 static const int _MODIFY_ATTRIBUTES = 1 << 4; | |
751 static const int _DELETE_SELF = 1 << 5; | |
752 static const int _IS_DIR = 1 << 6; | |
753 | |
754 /** | |
755 * The type of event. See [FileSystemEvent] for a list of events. | |
756 */ | |
757 final int type; | |
758 | |
759 /** | |
760 * The path that triggered the event. Depending on the platform and the | |
761 * FileSystemEntity, the path may be relative. | |
762 */ | |
763 final String path; | |
764 | |
765 /** | |
766 * Is `true` if the event target was a directory. | |
767 */ | |
768 final bool isDirectory; | |
769 | |
770 FileSystemEvent._(this.type, this.path, this.isDirectory); | |
771 } | |
772 | |
773 | |
774 /** | |
775 * File system event for newly created file system objects. | |
776 */ | |
777 class FileSystemCreateEvent extends FileSystemEvent { | |
778 FileSystemCreateEvent._(path, isDirectory) | |
779 : super._(FileSystemEvent.CREATE, path, isDirectory); | |
780 | |
781 String toString() => "FileSystemCreateEvent('$path')"; | |
782 } | |
783 | |
784 | |
785 /** | |
786 * File system event for modifications of file system objects. | |
787 */ | |
788 class FileSystemModifyEvent extends FileSystemEvent { | |
789 /** | |
790 * If the content was changed and not only the attributes, [contentChanged] | |
791 * is `true`. | |
792 */ | |
793 final bool contentChanged; | |
794 | |
795 FileSystemModifyEvent._(path, isDirectory, this.contentChanged) | |
796 : super._(FileSystemEvent.MODIFY, path, isDirectory); | |
797 | |
798 String toString() => | |
799 "FileSystemModifyEvent('$path', contentChanged=$contentChanged)"; | |
800 } | |
801 | |
802 | |
803 /** | |
804 * File system event for deletion of file system objects. | |
805 */ | |
806 class FileSystemDeleteEvent extends FileSystemEvent { | |
807 FileSystemDeleteEvent._(path, isDirectory) | |
808 : super._(FileSystemEvent.DELETE, path, isDirectory); | |
809 | |
810 String toString() => "FileSystemDeleteEvent('$path')"; | |
811 } | |
812 | |
813 | |
814 /** | |
815 * File system event for moving of file system objects. | |
816 */ | |
817 class FileSystemMoveEvent extends FileSystemEvent { | |
818 /** | |
819 * If the underlaying implementation is able to identify the destination of | |
820 * the moved file, [destination] will be set. Otherwise, it will be `null`. | |
821 */ | |
822 final String destination; | |
823 | |
824 FileSystemMoveEvent._(path, isDirectory, this.destination) | |
825 : super._(FileSystemEvent.MOVE, path, isDirectory); | |
826 | |
827 String toString() { | |
828 var buffer = new StringBuffer(); | |
829 buffer.write("FileSystemMoveEvent('$path'"); | |
830 if (destination != null) buffer.write(", '$destination'"); | |
831 buffer.write(')'); | |
832 return buffer.toString(); | |
833 } | |
834 } | |
835 | |
836 | |
837 class _FileSystemWatcher { | |
838 external static Stream<FileSystemEvent> _watch( | |
839 String path, int events, bool recursive); | |
840 external static bool get isSupported; | |
841 } | |
OLD | NEW |