Chromium Code Reviews| 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); |
| +} |