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..168a57b37b6b9deddabe5b74340b8cd0348dac9b 100644 |
--- a/sdk/lib/io/file_system_entity.dart |
+++ b/sdk/lib/io/file_system_entity.dart |
@@ -317,6 +317,12 @@ abstract class FileSystemEntity { |
FileStat statSync(); |
+ |
+ Stream<FileSystemEvent> watch({int events: FileSystemEvent.ALL_EVENTS, |
+ bool recursive: false}) |
+ => new _FileSystemWatcher(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 +396,204 @@ 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; |
} |
} |
} |
+ |
+ |
+/** |
+ * Base event class emitted by FileSystemWatcher. |
+ */ |
+class FileSystemEvent { |
+ static const int CREATE_EVENT = 1 << 0; |
Søren Gjesse
2013/08/26 07:51:55
How about removing the _EVENT prefix?
Anders Johnsen
2013/09/03 11:36:23
Done.
|
+ static const int MODIFY_EVENT = 1 << 1; |
+ static const int DELETE_EVENT = 1 << 2; |
+ static const int MOVE_EVENT = 1 << 3; |
Søren Gjesse
2013/08/26 07:51:55
Two spaces after =.
Anders Johnsen
2013/09/03 11:36:23
Done.
|
+ static const int ALL_EVENTS = |
+ CREATE_EVENT | MODIFY_EVENT | DELETE_EVENT | MOVE_EVENT; |
+ |
+ static const int _MODIFY_ATTRIBUTES_EVENT = 1 << 4; |
Søren Gjesse
2013/08/26 07:51:55
Add empty line.
Anders Johnsen
2013/09/03 11:36:23
Done.
|
+ /** |
+ * The type of event. See [FileSystemEvent] for a list of events. |
+ */ |
+ final int type; |
+ |
+ /** |
+ * The path that triggered the event. |
Søren Gjesse
2013/08/26 07:51:55
Some doc on whether this is absolute or maybe ŕela
Anders Johnsen
2013/09/03 11:36:23
Done.
|
+ */ |
+ 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_EVENT, 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_EVENT, 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_EVENT, 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_EVENT, path); |
+ |
+ String toString() { |
+ var buffer = new StringBuffer(); |
+ buffer.write("FileSystemMoveEvent('$path'"); |
+ if (destination != null) buffer.write(", '$destination'"); |
+ buffer.write(')'); |
+ return buffer.toString(); |
+ } |
+} |
+ |
+ |
+class _FileSystemWatcher { |
+ final String _path; |
+ final int _events; |
+ |
+ StreamController _controller; |
+ int _id; |
+ _RawSocket _socket; |
+ |
+ _FileSystemWatcher(this._path, this._events, bool recursive) { |
+ |
+ _controller = new StreamController( |
+ onListen: () { |
+ print("in listen"); |
Søren Gjesse
2013/08/26 07:51:55
Debug print.
Anders Johnsen
2013/09/03 11:36:23
Done.
|
+ _id = _watchPath(_path, _events, recursive); |
+ FileSystemEntity._throwIfError(_id, "Failed to watch path", _path); |
+ _listen(); |
+ }, |
+ onCancel: () { |
+ if (_socket != null) { |
+ _socket.close(); |
+ } |
+ }); |
+ } |
+ |
+ void _stop() { |
+ _controller.close(); |
+ _unwatchPath(_id); |
+ } |
+ |
+ void _listen() { |
Søren Gjesse
2013/08/26 07:51:55
I think we should do _NativeSocket setup the same
Anders Johnsen
2013/09/03 11:36:23
I've tried to simplify as much as possible.
|
+ int socketId = _getSocketId(_id); |
+ var native = new _NativeSocket.normal(); |
+ native.isClosedWrite = true; |
+ native.setSocketId(socketId); |
+ _socket = new _RawSocket(native); |
+ print(socketId); |
Søren Gjesse
2013/08/26 07:51:55
Debug print.
Anders Johnsen
2013/09/03 11:36:23
Done.
|
+ _socket.expand((event) { |
+ print(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(_id)) { |
+ print(event); |
Søren Gjesse
2013/08/26 07:51:55
Debug print.
Anders Johnsen
2013/09/03 11:36:23
Done.
|
+ var path = getPath(event); |
+ if ((event[0] & FileSystemEvent.CREATE_EVENT) != 0) { |
+ events.add(new FileSystemCreateEvent._(path)); |
+ } |
+ if ((event[0] & FileSystemEvent.MODIFY_EVENT) != 0) { |
+ events.add(new FileSystemModifyEvent._(path, true)); |
+ } |
+ if ((event[0] & FileSystemEvent._MODIFY_ATTRIBUTES_EVENT) != 0) { |
+ events.add(new FileSystemModifyEvent._(path, false)); |
+ } |
+ if ((event[0] & FileSystemEvent.MOVE_EVENT) != 0) { |
+ int link = event[3]; |
+ 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_EVENT) != 0) { |
+ events.add(new FileSystemDeleteEvent._(path)); |
+ } |
+ } |
+ } |
+ for (var event in pair.values) { |
+ events.add(new FileSystemMoveEvent._(getPath(event), null)); |
+ } |
+ } else if (event == RawSocketEvent.CLOSED) { |
+ _stop(); |
+ } else if (event == RawSocketEvent.READ_CLOSED) { |
+ } else { |
+ assert(false); |
+ } |
+ print(events); |
+ return events; |
+ }).where((event) => (event.type & _events) != 0).listen(_controller.add); |
+ } |
+ |
+ Stream<FileSystemEvent> get stream => _controller.stream; |
+ |
+ external _watchPath(String path, int events, bool recursive); |
+ external void _unwatchPath(int id); |
+ external int _getSocketId(int id); |
+ external List _readEvents(int id); |
+} |