OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 patch class _File { | 5 patch class _File { |
6 /* patch */ static _exists(String path) native "File_Exists"; | 6 /* patch */ static _exists(String path) native "File_Exists"; |
7 /* patch */ static _create(String path) native "File_Create"; | 7 /* patch */ static _create(String path) native "File_Create"; |
8 /* patch */ static _createLink(String path, String target) | 8 /* patch */ static _createLink(String path, String target) |
9 native "File_CreateLink"; | 9 native "File_CreateLink"; |
10 /* patch */ static _linkTarget(String path) native "File_LinkTarget"; | 10 /* patch */ static _linkTarget(String path) native "File_LinkTarget"; |
11 /* patch */ static _deleteNative(String path) native "File_Delete"; | 11 /* patch */ static _deleteNative(String path) native "File_Delete"; |
12 /* patch */ static _deleteLinkNative(String path) native "File_DeleteLink"; | 12 /* patch */ static _deleteLinkNative(String path) native "File_DeleteLink"; |
13 /* patch */ static _rename(String oldPath, String newPath) | 13 /* patch */ static _rename(String oldPath, String newPath) |
14 native "File_Rename"; | 14 native "File_Rename"; |
15 /* patch */ static _renameLink(String oldPath, String newPath) | 15 /* patch */ static _renameLink(String oldPath, String newPath) |
16 native "File_RenameLink"; | 16 native "File_RenameLink"; |
17 /* patch */ static _lengthFromPath(String path) native "File_LengthFromPath"; | 17 /* patch */ static _lengthFromPath(String path) native "File_LengthFromPath"; |
18 /* patch */ static _lastModified(String path) native "File_LastModified"; | 18 /* patch */ static _lastModified(String path) native "File_LastModified"; |
19 /* patch */ static _open(String path, int mode) native "File_Open"; | 19 /* patch */ static _open(String path, int mode) native "File_Open"; |
20 /* patch */ static int _openStdio(int fd) native "File_OpenStdio"; | 20 /* patch */ static int _openStdio(int fd) native "File_OpenStdio"; |
21 } | 21 } |
22 | 22 |
| 23 |
23 patch class _RandomAccessFile { | 24 patch class _RandomAccessFile { |
24 /* patch */ static int _close(int id) native "File_Close"; | 25 /* patch */ static int _close(int id) native "File_Close"; |
25 /* patch */ static _readByte(int id) native "File_ReadByte"; | 26 /* patch */ static _readByte(int id) native "File_ReadByte"; |
26 /* patch */ static _read(int id, int bytes) native "File_Read"; | 27 /* patch */ static _read(int id, int bytes) native "File_Read"; |
27 /* patch */ static _readInto(int id, List<int> buffer, int start, int end) | 28 /* patch */ static _readInto(int id, List<int> buffer, int start, int end) |
28 native "File_ReadInto"; | 29 native "File_ReadInto"; |
29 /* patch */ static _writeByte(int id, int value) native "File_WriteByte"; | 30 /* patch */ static _writeByte(int id, int value) native "File_WriteByte"; |
30 /* patch */ static _writeFrom(int id, List<int> buffer, int start, int end) | 31 /* patch */ static _writeFrom(int id, List<int> buffer, int start, int end) |
31 native "File_WriteFrom"; | 32 native "File_WriteFrom"; |
32 /* patch */ static _position(int id) native "File_Position"; | 33 /* patch */ static _position(int id) native "File_Position"; |
33 /* patch */ static _setPosition(int id, int position) | 34 /* patch */ static _setPosition(int id, int position) |
34 native "File_SetPosition"; | 35 native "File_SetPosition"; |
35 /* patch */ static _truncate(int id, int length) native "File_Truncate"; | 36 /* patch */ static _truncate(int id, int length) native "File_Truncate"; |
36 /* patch */ static _length(int id) native "File_Length"; | 37 /* patch */ static _length(int id) native "File_Length"; |
37 /* patch */ static _flush(int id) native "File_Flush"; | 38 /* patch */ static _flush(int id) native "File_Flush"; |
38 } | 39 } |
39 | 40 |
40 patch class _FileSystemWatcher { | |
41 /* patch */ factory _FileSystemWatcher( | |
42 String path, int events, bool recursive) | |
43 => new _FileSystemWatcherImpl(path, events, recursive); | |
44 | 41 |
45 /* patch */ static bool get isSupported => _FileSystemWatcherImpl.isSupported; | 42 class _WatcherPath { |
| 43 final int pathId; |
| 44 final String path; |
| 45 final int events; |
| 46 int count = 0; |
| 47 _WatcherPath(this.pathId, this.path, this.events); |
46 } | 48 } |
47 | 49 |
48 class _FileSystemWatcherImpl | 50 |
49 extends NativeFieldWrapperClass1 | 51 patch class _FileSystemWatcher { |
50 implements _FileSystemWatcher { | 52 static int _id; |
| 53 static final Map<int, _FileSystemWatcherPath> _idMap = {}; |
| 54 |
51 final String _path; | 55 final String _path; |
52 final int _events; | 56 final int _events; |
53 final bool _recursive; | 57 final bool _recursive; |
54 | 58 |
55 StreamController _controller; | 59 _WatcherPath _watcherPath; |
56 StreamSubscription _subscription; | |
57 | 60 |
58 _FileSystemWatcherImpl(this._path, this._events, this._recursive) { | 61 StreamController _broadcastController; |
| 62 |
| 63 /* patch */ static Stream<FileSystemEvent> watch( |
| 64 String path, int events, bool recursive) { |
| 65 if (Platform.isLinux) { |
| 66 return new _InotifyFileSystemWatcher(path, events, recursive).stream; |
| 67 } |
| 68 if (Platform.isWindows) { |
| 69 return new _Win32FileSystemWatcher(path, events, recursive).stream; |
| 70 } |
| 71 if (Platform.isMacOS) { |
| 72 return new _FSEventStreamFileSystemWatcher( |
| 73 path, events, recursive).stream; |
| 74 } |
| 75 throw new FileSystemException( |
| 76 "File system watching is not supported on this platform"); |
| 77 } |
| 78 |
| 79 _FileSystemWatcher._(this._path, this._events, this._recursive) { |
59 if (!isSupported) { | 80 if (!isSupported) { |
60 throw new FileSystemException( | 81 throw new FileSystemException( |
61 "File system watching is not supported on this system", | 82 "File system watching is not supported on this platform", |
62 _path); | 83 _path); |
63 } | 84 } |
64 _controller = new StreamController.broadcast(onListen: _listen, | 85 _broadcastController = new StreamController.broadcast(onListen: _listen, |
65 onCancel: _cancel); | 86 onCancel: _cancel); |
66 } | 87 } |
67 | 88 |
| 89 Stream get stream => _broadcastController.stream; |
| 90 |
68 void _listen() { | 91 void _listen() { |
69 int socketId; | 92 if (_id == null) { |
| 93 try { |
| 94 _id = _initWatcher(); |
| 95 _newWatcher(); |
| 96 } catch (e) { |
| 97 _broadcastController.addError(new FileSystemException( |
| 98 "Failed to initialize file system entity watcher")); |
| 99 _broadcastController.close(); |
| 100 return; |
| 101 } |
| 102 } |
| 103 var pathId; |
70 try { | 104 try { |
71 socketId = _watchPath(_path, _events, identical(true, _recursive)); | 105 pathId = _watchPath(_id, _path, _events, _recursive); |
72 } catch (e) { | 106 } catch (e) { |
73 _controller.addError(new FileSystemException( | 107 _broadcastController.addError(new FileSystemException( |
74 "Failed to watch path", _path, e)); | 108 "Failed to watch path", _path, e)); |
75 _controller.close(); | 109 _broadcastController.close(); |
76 return; | 110 return; |
77 } | 111 } |
| 112 if (!_idMap.containsKey(pathId)) { |
| 113 _idMap[pathId] = new _WatcherPath(pathId, _path, _events); |
| 114 } |
| 115 _watcherPath = _idMap[pathId]; |
| 116 _watcherPath.count++; |
| 117 _pathWatched().pipe(_broadcastController); |
| 118 } |
| 119 |
| 120 void _cancel() { |
| 121 if (_watcherPath != null) { |
| 122 assert(_watcherPath.count > 0); |
| 123 _watcherPath.count--; |
| 124 if (_watcherPath.count == 0) { |
| 125 _pathWatchedEnd(); |
| 126 _unwatchPath(_id, _watcherPath.pathId); |
| 127 _idMap.remove(_watcherPath.pathId); |
| 128 } |
| 129 _watcherPath = null; |
| 130 } |
| 131 if (_idMap.isEmpty && _id != null) { |
| 132 _closeWatcher(_id); |
| 133 _doneWatcher(); |
| 134 _id = null; |
| 135 } |
| 136 } |
| 137 |
| 138 // Called when (and after) a new watcher instance is created and available. |
| 139 void _newWatcher() {} |
| 140 // Called when a watcher is no longer needed. |
| 141 void _doneWatcher() {} |
| 142 // Called when a new path is being watched. |
| 143 Stream _pathWatched() {} |
| 144 // Called when a path is no longer being watched. |
| 145 void _donePathWatched() {} |
| 146 |
| 147 static _WatcherPath _pathFromPathId(int pathId) { |
| 148 return _idMap[pathId]; |
| 149 } |
| 150 |
| 151 static Stream _listenOnSocket(int socketId, int id, int pathId) { |
78 var socket = new _RawSocket(new _NativeSocket.watch(socketId)); | 152 var socket = new _RawSocket(new _NativeSocket.watch(socketId)); |
79 _subscription = socket.expand((event) { | 153 return socket.expand((event) { |
80 bool stop = false; | 154 var stops = []; |
81 var events = []; | 155 var events = []; |
82 var pair = {}; | 156 var pair = {}; |
83 if (event == RawSocketEvent.READ) { | 157 if (event == RawSocketEvent.READ) { |
84 String getPath(event) { | 158 String getPath(event) { |
85 var path = _path; | 159 var path = _pathFromPathId(event[4]).path; |
86 if (event[2] != null && event[2].isNotEmpty) { | 160 if (event[2] != null && event[2].isNotEmpty) { |
87 path += Platform.pathSeparator; | 161 path += Platform.pathSeparator; |
88 path += event[2]; | 162 path += event[2]; |
89 } | 163 } |
90 return path; | 164 return path; |
91 } | 165 } |
92 bool getIsDir(event) { | 166 bool getIsDir(event) { |
93 if (Platform.isWindows) { | 167 if (Platform.isWindows) { |
94 // Windows does not get 'isDir' as part of the event. | 168 // Windows does not get 'isDir' as part of the event. |
95 return FileSystemEntity.isDirectorySync(getPath(event)); | 169 return FileSystemEntity.isDirectorySync(getPath(event)); |
96 } | 170 } |
97 return (event[0] & FileSystemEvent._IS_DIR) != 0; | 171 return (event[0] & FileSystemEvent._IS_DIR) != 0; |
98 } | 172 } |
99 void add(event) { | 173 void add(id, event) { |
100 if ((event.type & _events) == 0) return; | 174 if ((event.type & _pathFromPathId(id).events) == 0) return; |
101 events.add(event); | 175 events.add([id, event]); |
102 } | 176 } |
103 void rewriteMove(event, isDir) { | 177 void rewriteMove(event, isDir) { |
104 if (event[3]) { | 178 if (event[3]) { |
105 add(new FileSystemCreateEvent._(getPath(event), isDir)); | 179 add(event[4], new FileSystemCreateEvent._(getPath(event), isDir)); |
106 } else { | 180 } else { |
107 add(new FileSystemDeleteEvent._(getPath(event), isDir)); | 181 add(event[4], new FileSystemDeleteEvent._(getPath(event), isDir)); |
108 } | 182 } |
109 } | 183 } |
110 while (socket.available() > 0) { | 184 while (socket.available() > 0) { |
111 for (var event in _readEvents()) { | 185 for (var event in _readEvents(id, pathId)) { |
112 if (event == null) continue; | 186 if (event == null) continue; |
| 187 int pathId = event[4]; |
113 bool isDir = getIsDir(event); | 188 bool isDir = getIsDir(event); |
114 var path = getPath(event); | 189 var path = getPath(event); |
115 if ((event[0] & FileSystemEvent.CREATE) != 0) { | 190 if ((event[0] & FileSystemEvent.CREATE) != 0) { |
116 add(new FileSystemCreateEvent._(path, isDir)); | 191 add(event[4], new FileSystemCreateEvent._(path, isDir)); |
117 } | 192 } |
118 if ((event[0] & FileSystemEvent.MODIFY) != 0) { | 193 if ((event[0] & FileSystemEvent.MODIFY) != 0) { |
119 add(new FileSystemModifyEvent._(path, isDir, true)); | 194 add(event[4], new FileSystemModifyEvent._(path, isDir, true)); |
120 } | 195 } |
121 if ((event[0] & FileSystemEvent._MODIFY_ATTRIBUTES) != 0) { | 196 if ((event[0] & FileSystemEvent._MODIFY_ATTRIBUTES) != 0) { |
122 add(new FileSystemModifyEvent._(path, isDir, false)); | 197 add(event[4], new FileSystemModifyEvent._(path, isDir, false)); |
123 } | 198 } |
124 if ((event[0] & FileSystemEvent.MOVE) != 0) { | 199 if ((event[0] & FileSystemEvent.MOVE) != 0) { |
125 int link = event[1]; | 200 int link = event[1]; |
126 if (link > 0) { | 201 if (link > 0) { |
127 if (pair.containsKey(link)) { | 202 pair.putIfAbsent(pathId, () => {}); |
128 events.add(new FileSystemMoveEvent._( | 203 if (pair[pathId].containsKey(link)) { |
129 getPath(pair[link]), isDir, path)); | 204 add(event[4], |
130 pair.remove(link); | 205 new FileSystemMoveEvent._( |
| 206 getPath(pair[pathId][link]), isDir, path)); |
| 207 pair[pathId].remove(link); |
131 } else { | 208 } else { |
132 pair[link] = event; | 209 pair[pathId][link] = event; |
133 } | 210 } |
134 } else { | 211 } else { |
135 rewriteMove(event, isDir); | 212 rewriteMove(event, isDir); |
136 } | 213 } |
137 } | 214 } |
138 if ((event[0] & FileSystemEvent.DELETE) != 0) { | 215 if ((event[0] & FileSystemEvent.DELETE) != 0) { |
139 add(new FileSystemDeleteEvent._(path, isDir)); | 216 add(event[4], new FileSystemDeleteEvent._(path, isDir)); |
140 } | 217 } |
141 if ((event[0] & FileSystemEvent._DELETE_SELF) != 0) { | 218 if ((event[0] & FileSystemEvent._DELETE_SELF) != 0) { |
142 add(new FileSystemDeleteEvent._(path, isDir)); | 219 add(event[4], new FileSystemDeleteEvent._(path, isDir)); |
143 stop = true; | 220 // Signal done event. |
| 221 stops.add([event[4], null]); |
144 } | 222 } |
145 } | 223 } |
146 } | 224 } |
147 for (var event in pair.values) { | 225 for (var map in pair.values) { |
148 rewriteMove(event, getIsDir(event)); | 226 for (var event in map.values) { |
| 227 rewriteMove(event, getIsDir(event)); |
| 228 } |
149 } | 229 } |
150 } else if (event == RawSocketEvent.CLOSED) { | 230 } else if (event == RawSocketEvent.CLOSED) { |
151 } else if (event == RawSocketEvent.READ_CLOSED) { | 231 } else if (event == RawSocketEvent.READ_CLOSED) { |
152 } else { | 232 } else { |
153 assert(false); | 233 assert(false); |
154 } | 234 } |
155 if (stop) socket.close(); | 235 events.addAll(stops); |
156 return events; | 236 return events; |
157 }) | 237 }); |
158 .listen(_controller.add, onDone: _cancel); | |
159 } | 238 } |
160 | 239 |
161 void _cancel() { | 240 /* patch */ static bool get isSupported |
162 if (_subscription != null) { | 241 native "FileSystemWatcher_IsSupported"; |
163 _unwatchPath(); | 242 |
164 _subscription.cancel(); | 243 static int _initWatcher() native "FileSystemWatcher_InitWatcher"; |
165 _subscription = null; | 244 static void _closeWatcher(int id) native "FileSystemWatcher_CloseWatcher"; |
| 245 |
| 246 static int _watchPath(int id, String path, int events, bool recursive) |
| 247 native "FileSystemWatcher_WatchPath"; |
| 248 static void _unwatchPath(int id, int path_id) |
| 249 native "FileSystemWatcher_UnwatchPath"; |
| 250 static List _readEvents(int id, int path_id) |
| 251 native "FileSystemWatcher_ReadEvents"; |
| 252 static int _getSocketId(int id, int path_id) |
| 253 native "FileSystemWatcher_GetSocketId"; |
| 254 } |
| 255 |
| 256 |
| 257 class _InotifyFileSystemWatcher extends _FileSystemWatcher { |
| 258 static final Map<int, StreamController> _idMap = {}; |
| 259 static StreamSubscription _subscription; |
| 260 |
| 261 _InotifyFileSystemWatcher(path, events, recursive) |
| 262 : super._(path, events, recursive); |
| 263 |
| 264 void _newWatcher() { |
| 265 int id = _FileSystemWatcher._id; |
| 266 _subscription = _FileSystemWatcher._listenOnSocket(id, id, 0) |
| 267 .listen((event) { |
| 268 if (_idMap.containsKey(event[0])) { |
| 269 if (event[1] != null) { |
| 270 _idMap[event[0]].add(event[1]); |
| 271 } else { |
| 272 _idMap[event[0]].close(); |
| 273 } |
| 274 } |
| 275 }); |
| 276 } |
| 277 |
| 278 void _doneWatcher() { |
| 279 _subscription.cancel(); |
| 280 } |
| 281 |
| 282 Stream _pathWatched() { |
| 283 var pathId = _watcherPath.pathId; |
| 284 if (!_idMap.containsKey(pathId)) { |
| 285 _idMap[pathId] = new StreamController.broadcast(); |
166 } | 286 } |
| 287 return _idMap[pathId].stream; |
| 288 } |
| 289 |
| 290 void _pathWatchedEnd() { |
| 291 var pathId = _watcherPath.pathId; |
| 292 if (!_idMap.containsKey(pathId)) return; |
| 293 _idMap[pathId].close(); |
| 294 _idMap.remove(pathId); |
| 295 } |
| 296 } |
| 297 |
| 298 |
| 299 class _Win32FileSystemWatcher extends _FileSystemWatcher { |
| 300 StreamSubscription _subscription; |
| 301 StreamController _controller; |
| 302 |
| 303 _Win32FileSystemWatcher(path, events, recursive) |
| 304 : super._(path, events, recursive); |
| 305 |
| 306 Stream _pathWatched() { |
| 307 var pathId = _watcherPath.pathId; |
| 308 _controller = new StreamController(); |
| 309 _subscription = _FileSystemWatcher._listenOnSocket(pathId, 0, pathId) |
| 310 .listen((event) { |
| 311 assert(event[0] == pathId); |
| 312 if (event[1] != null) { |
| 313 _controller.add(event[1]); |
| 314 } else { |
| 315 _controller.close(); |
| 316 } |
| 317 }); |
| 318 return _controller.stream; |
| 319 } |
| 320 |
| 321 void _pathWatchedEnd() { |
| 322 _subscription.cancel(); |
167 _controller.close(); | 323 _controller.close(); |
168 } | 324 } |
| 325 } |
169 | 326 |
170 Stream<FileSystemEvent> get stream => _controller.stream; | |
171 | 327 |
172 static bool get isSupported native "FileSystemWatcher_IsSupported"; | 328 class _FSEventStreamFileSystemWatcher extends _FileSystemWatcher { |
| 329 StreamSubscription _subscription; |
| 330 StreamController _controller; |
173 | 331 |
174 int _watchPath(String path, int events, bool recursive) | 332 _FSEventStreamFileSystemWatcher(path, events, recursive) |
175 native "FileSystemWatcher_WatchPath"; | 333 : super._(path, events, recursive); |
176 void _unwatchPath() native "FileSystemWatcher_UnwatchPath"; | 334 |
177 List _readEvents() native "FileSystemWatcher_ReadEvents"; | 335 Stream _pathWatched() { |
| 336 var pathId = _watcherPath.pathId; |
| 337 var socketId = _FileSystemWatcher._getSocketId(0, pathId); |
| 338 _controller = new StreamController(); |
| 339 _subscription = _FileSystemWatcher._listenOnSocket(socketId, 0, pathId) |
| 340 .listen((event) { |
| 341 if (event[1] != null) { |
| 342 _controller.add(event[1]); |
| 343 } else { |
| 344 _controller.close(); |
| 345 } |
| 346 }); |
| 347 return _controller.stream; |
| 348 } |
| 349 |
| 350 void _pathWatchedEnd() { |
| 351 _subscription.cancel(); |
| 352 _controller.close(); |
| 353 } |
178 } | 354 } |
179 | 355 |
| 356 |
180 Uint8List _makeUint8ListView(Uint8List source, int offsetInBytes, int length) { | 357 Uint8List _makeUint8ListView(Uint8List source, int offsetInBytes, int length) { |
181 return new Uint8List.view(source.buffer, offsetInBytes, length); | 358 return new Uint8List.view(source.buffer, offsetInBytes, length); |
182 } | 359 } |
OLD | NEW |