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

Unified Diff: sdk/lib/io/file_system_entity.dart

Issue 19263003: Add FileSystemWatcher class to dart:io. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Fix android socket. Created 7 years, 3 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « sdk/lib/io/file_impl.dart ('k') | tests/standalone/io/file_system_watcher_test.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: sdk/lib/io/file_system_entity.dart
diff --git a/sdk/lib/io/file_system_entity.dart b/sdk/lib/io/file_system_entity.dart
index b764f04be7ca151d28701ae498155411f4b86f5f..a00cb03229638eba8ab8c6330e0ec29cb9ee6692 100644
--- a/sdk/lib/io/file_system_entity.dart
+++ b/sdk/lib/io/file_system_entity.dart
@@ -317,6 +317,34 @@ abstract class FileSystemEntity {
FileStat statSync();
+
+ /**
+ * Start watch the [FileSystemEntity] for changes.
+ *
+ * The implementation uses platform-depending event-based APIs for receiving
+ * file-system notifixations, thus behvaiour depends on the platform.
+ *
+ * * `Windows`: Uses `ReadDirectoryChangesW`. The implementation supports
+ * only watching dirctories but supports recursive watching.
+ * * `Linux`: Uses `inotify`. The implementation supports watching both
+ * files and dirctories, but doesn't support recursive watching.
+ * * `Mac OS`: Uses `FSEvents`. The implementation supports watching both
+ * files and dirctories, and also recursive watching. Note that FSEvents
+ * always use recursion internally, so when disabled, some events are
+ * ignored.
+ *
+ * The system will start listen for events once the returned [Stream] is
+ * being listened to, not when the call to [watch] is issued. Note that the
+ * returned [Stream] is endless. To stop the [Stream], simply cancel the
+ * subscription.
+ */
+ Stream<FileSystemEvent> watch({int events: FileSystemEvent.ALL,
+ bool recursive: false})
+ => new _FileSystemWatcher(_trimTrailingPathSeparators(path),
+ events,
+ recursive).stream;
+
+
/**
* Finds the type of file system object that a path points to. Returns
* a [:Future<FileSystemEntityType>:] that completes with the result.
@@ -390,11 +418,214 @@ abstract class FileSystemEntity {
(_getTypeSync(path, true) == FileSystemEntityType.DIRECTORY._type);
- static _throwIfError(Object result, String msg) {
+ static _throwIfError(Object result, String msg, [String path]) {
if (result is OSError) {
- throw new FileException(msg, result);
+ throw new FileException(msg, result, path);
} else if (result is ArgumentError) {
throw result;
}
}
+
+ static String _trimTrailingPathSeparators(String path) {
+ // Don't handle argument errors here.
+ if (path is! String) return path;
+ if (Platform.operatingSystem == 'windows') {
+ while (path.length > 1 &&
+ (path.endsWith(Platform.pathSeparator) ||
+ path.endsWith('/'))) {
+ path = path.substring(0, path.length - 1);
+ }
+ } else {
+ while (path.length > 1 && path.endsWith(Platform.pathSeparator)) {
+ path = path.substring(0, path.length - 1);
+ }
+ }
+ return path;
+ }
+}
+
+
+/**
+ * Base event class emitted by FileSystemWatcher.
+ */
+class FileSystemEvent {
+ static const int CREATE = 1 << 0;
+ static const int MODIFY = 1 << 1;
+ static const int DELETE = 1 << 2;
+ static const int MOVE = 1 << 3;
+ static const int ALL = CREATE | MODIFY | DELETE | MOVE;
+
+ static const int _MODIFY_ATTRIBUTES = 1 << 4;
+
+ /**
+ * The type of event. See [FileSystemEvent] for a list of events.
+ */
+ final int type;
+
+ /**
+ * The path that triggered the event. Depending on the platform and the
+ * FileSystemEntity, the path may be relative.
+ */
+ final String path;
+
+ FileSystemEvent._(this.type, this.path);
+}
+
+
+/**
+ * File system event for newly created file system objects.
+ */
+class FileSystemCreateEvent extends FileSystemEvent {
+ FileSystemCreateEvent._(path)
+ : super._(FileSystemEvent.CREATE, path);
+
+ String toString() => "FileSystemCreateEvent('$path')";
+}
+
+
+/**
+ * File system event for modifications of file system objects.
+ */
+class FileSystemModifyEvent extends FileSystemEvent {
+ /**
+ * If the content was changed and not only the attributes, [contentChanged]
+ * is `true`.
+ */
+ final bool contentChanged;
+
+ FileSystemModifyEvent._(path, this.contentChanged)
+ : super._(FileSystemEvent.MODIFY, path);
+
+ String toString() =>
+ "FileSystemModifyEvent('$path', contentChanged=$contentChanged)";
+}
+
+
+/**
+ * File system event for deletion of file system objects.
+ */
+class FileSystemDeleteEvent extends FileSystemEvent {
+ FileSystemDeleteEvent._(path)
+ : super._(FileSystemEvent.DELETE, path);
+
+ String toString() => "FileSystemDeleteEvent('$path')";
+}
+
+
+/**
+ * File system event for moving of file system objects.
+ */
+class FileSystemMoveEvent extends FileSystemEvent {
+ /**
+ * If the underlaying implementation is able to identify the destination of
+ * the moved file, [destination] will be set. Otherwise, it will be `null`.
+ */
+ final String destination;
+
+ FileSystemMoveEvent._(path, this.destination)
+ : super._(FileSystemEvent.MOVE, path);
+
+ String toString() {
+ var buffer = new StringBuffer();
+ buffer.write("FileSystemMoveEvent('$path'");
+ if (destination != null) buffer.write(", '$destination'");
+ buffer.write(')');
+ return buffer.toString();
+ }
+}
+
+
+class _FileSystemWatcher extends NativeFieldWrapperClass1 {
+ final String _path;
+ final int _events;
+ final bool _recursive;
+
+ StreamController _controller;
+ StreamSubscription _subscription;
+
+ _FileSystemWatcher(this._path, this._events, this._recursive) {
+ _controller = new StreamController(onListen: _listen, onCancel: _cancel);
+ }
+
+ void _listen() {
+ int socketId;
+ try {
+ socketId = _watchPath(_path, _events, identical(true, _recursive));
+ } catch (e) {
+ throw new FileException(
+ "Failed to watch path",
+ _path,
+ e);
+ }
+ var socket = new _RawSocket(new _NativeSocket.watch(socketId));
+ _subscription = socket.expand((event) {
+ var events = [];
+ var pair = {};
+ if (event == RawSocketEvent.READ) {
+ String getPath(event) {
+ var path = _path;
+ if (event[2] != null) {
+ path += Platform.pathSeparator;
+ path += event[2];
+ }
+ return path;
+ }
+ while (socket.available() > 0) {
+ for (var event in _readEvents()) {
+ if (event == null) continue;
+ var path = getPath(event);
+ if ((event[0] & FileSystemEvent.CREATE) != 0) {
+ events.add(new FileSystemCreateEvent._(path));
+ }
+ if ((event[0] & FileSystemEvent.MODIFY) != 0) {
+ events.add(new FileSystemModifyEvent._(path, true));
+ }
+ if ((event[0] & FileSystemEvent._MODIFY_ATTRIBUTES) != 0) {
+ events.add(new FileSystemModifyEvent._(path, false));
+ }
+ if ((event[0] & FileSystemEvent.MOVE) != 0) {
+ int link = event[1];
+ if (link > 0) {
+ if (pair.containsKey(link)) {
+ events.add(
+ new FileSystemMoveEvent._(getPath(pair[link]), path));
+ pair.remove(link);
+ } else {
+ pair[link] = event;
+ }
+ } else {
+ events.add(new FileSystemMoveEvent._(path, null));
+ }
+ }
+ if ((event[0] & FileSystemEvent.DELETE) != 0) {
+ events.add(new FileSystemDeleteEvent._(path));
+ }
+ }
+ }
+ for (var event in pair.values) {
+ events.add(new FileSystemMoveEvent._(getPath(event), null));
+ }
+ } else if (event == RawSocketEvent.CLOSED) {
+ } else if (event == RawSocketEvent.READ_CLOSED) {
+ } else {
+ assert(false);
+ }
+ return events;
+ })
+ .where((event) => (event.type & _events) != 0)
+ .listen(_controller.add, onDone: _cancel);
+ }
+
+ void _cancel() {
+ _unwatchPath();
+ if (_subscription != null) {
+ _subscription.cancel();
+ }
+ }
+
+ Stream<FileSystemEvent> get stream => _controller.stream;
+
+ external int _watchPath(String path, int events, bool recursive);
+ external void _unwatchPath();
+ external List _readEvents();
}
« no previous file with comments | « sdk/lib/io/file_impl.dart ('k') | tests/standalone/io/file_system_watcher_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698