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 | |
Søren Gjesse
2013/12/03 14:42:19
Please add a comment here on the lifecycle and how
Anders Johnsen
2013/12/04 10:30:52
Done.
| |
138 void _newWatcher(); | |
139 void _doneWatcher(); | |
140 Stream _pathWatched(); | |
141 void _donePathWatched(); | |
142 | |
143 static _WatcherPath _pathFromPathId(int pathId) { | |
144 return _idMap[pathId]; | |
145 } | |
146 | |
147 static Stream _listenOnSocket(int socketId, int id, int pathId) { | |
78 var socket = new _RawSocket(new _NativeSocket.watch(socketId)); | 148 var socket = new _RawSocket(new _NativeSocket.watch(socketId)); |
79 _subscription = socket.expand((event) { | 149 return socket.expand((event) { |
80 bool stop = false; | 150 var stops = []; |
81 var events = []; | 151 var events = []; |
82 var pair = {}; | 152 var pair = {}; |
83 if (event == RawSocketEvent.READ) { | 153 if (event == RawSocketEvent.READ) { |
84 String getPath(event) { | 154 String getPath(event) { |
85 var path = _path; | 155 var path = _pathFromPathId(event[4]).path; |
86 if (event[2] != null && event[2].isNotEmpty) { | 156 if (event[2] != null && event[2].isNotEmpty) { |
87 path += Platform.pathSeparator; | 157 path += Platform.pathSeparator; |
88 path += event[2]; | 158 path += event[2]; |
89 } | 159 } |
90 return path; | 160 return path; |
91 } | 161 } |
92 bool getIsDir(event) { | 162 bool getIsDir(event) { |
93 if (Platform.isWindows) { | 163 if (Platform.isWindows) { |
94 // Windows does not get 'isDir' as part of the event. | 164 // Windows does not get 'isDir' as part of the event. |
95 return FileSystemEntity.isDirectorySync(getPath(event)); | 165 return FileSystemEntity.isDirectorySync(getPath(event)); |
96 } | 166 } |
97 return (event[0] & FileSystemEvent._IS_DIR) != 0; | 167 return (event[0] & FileSystemEvent._IS_DIR) != 0; |
98 } | 168 } |
99 void add(event) { | 169 void add(id, event) { |
100 if ((event.type & _events) == 0) return; | 170 if ((event.type & _pathFromPathId(id).events) == 0) return; |
101 events.add(event); | 171 events.add([id, event]); |
102 } | 172 } |
103 void rewriteMove(event, isDir) { | 173 void rewriteMove(event, isDir) { |
104 if (event[3]) { | 174 if (event[3]) { |
105 add(new FileSystemCreateEvent._(getPath(event), isDir)); | 175 add(event[4], new FileSystemCreateEvent._(getPath(event), isDir)); |
106 } else { | 176 } else { |
107 add(new FileSystemDeleteEvent._(getPath(event), isDir)); | 177 add(event[4], new FileSystemDeleteEvent._(getPath(event), isDir)); |
108 } | 178 } |
109 } | 179 } |
110 while (socket.available() > 0) { | 180 while (socket.available() > 0) { |
111 for (var event in _readEvents()) { | 181 for (var event in _readEvents(id, pathId)) { |
112 if (event == null) continue; | 182 if (event == null) continue; |
183 int pathId = event[4]; | |
113 bool isDir = getIsDir(event); | 184 bool isDir = getIsDir(event); |
114 var path = getPath(event); | 185 var path = getPath(event); |
115 if ((event[0] & FileSystemEvent.CREATE) != 0) { | 186 if ((event[0] & FileSystemEvent.CREATE) != 0) { |
116 add(new FileSystemCreateEvent._(path, isDir)); | 187 add(event[4], new FileSystemCreateEvent._(path, isDir)); |
117 } | 188 } |
118 if ((event[0] & FileSystemEvent.MODIFY) != 0) { | 189 if ((event[0] & FileSystemEvent.MODIFY) != 0) { |
119 add(new FileSystemModifyEvent._(path, isDir, true)); | 190 add(event[4], new FileSystemModifyEvent._(path, isDir, true)); |
120 } | 191 } |
121 if ((event[0] & FileSystemEvent._MODIFY_ATTRIBUTES) != 0) { | 192 if ((event[0] & FileSystemEvent._MODIFY_ATTRIBUTES) != 0) { |
122 add(new FileSystemModifyEvent._(path, isDir, false)); | 193 add(event[4], new FileSystemModifyEvent._(path, isDir, false)); |
123 } | 194 } |
124 if ((event[0] & FileSystemEvent.MOVE) != 0) { | 195 if ((event[0] & FileSystemEvent.MOVE) != 0) { |
125 int link = event[1]; | 196 int link = event[1]; |
126 if (link > 0) { | 197 if (link > 0) { |
127 if (pair.containsKey(link)) { | 198 pair.putIfAbsent(pathId, () => {}); |
128 events.add(new FileSystemMoveEvent._( | 199 if (pair[pathId].containsKey(link)) { |
129 getPath(pair[link]), isDir, path)); | 200 add(event[4], |
130 pair.remove(link); | 201 new FileSystemMoveEvent._( |
202 getPath(pair[pathId][link]), isDir, path)); | |
203 pair[pathId].remove(link); | |
131 } else { | 204 } else { |
132 pair[link] = event; | 205 pair[pathId][link] = event; |
133 } | 206 } |
134 } else { | 207 } else { |
135 rewriteMove(event, isDir); | 208 rewriteMove(event, isDir); |
136 } | 209 } |
137 } | 210 } |
138 if ((event[0] & FileSystemEvent.DELETE) != 0) { | 211 if ((event[0] & FileSystemEvent.DELETE) != 0) { |
139 add(new FileSystemDeleteEvent._(path, isDir)); | 212 add(event[4], new FileSystemDeleteEvent._(path, isDir)); |
140 } | 213 } |
141 if ((event[0] & FileSystemEvent._DELETE_SELF) != 0) { | 214 if ((event[0] & FileSystemEvent._DELETE_SELF) != 0) { |
142 add(new FileSystemDeleteEvent._(path, isDir)); | 215 add(event[4], new FileSystemDeleteEvent._(path, isDir)); |
143 stop = true; | 216 // Signal done event. |
144 } | 217 stops.add([event[4], null]); |
145 } | 218 } |
146 } | 219 } |
147 for (var event in pair.values) { | 220 } |
148 rewriteMove(event, getIsDir(event)); | 221 for (var map in pair.values) { |
222 for (var event in map.values) { | |
223 rewriteMove(event, getIsDir(event)); | |
224 } | |
149 } | 225 } |
150 } else if (event == RawSocketEvent.CLOSED) { | 226 } else if (event == RawSocketEvent.CLOSED) { |
151 } else if (event == RawSocketEvent.READ_CLOSED) { | 227 } else if (event == RawSocketEvent.READ_CLOSED) { |
152 } else { | 228 } else { |
153 assert(false); | 229 assert(false); |
154 } | 230 } |
155 if (stop) socket.close(); | 231 events.addAll(stops); |
156 return events; | 232 return events; |
157 }) | 233 }); |
158 .listen(_controller.add, onDone: _cancel); | 234 } |
159 } | 235 |
160 | 236 /* patch */ static bool get isSupported |
161 void _cancel() { | 237 native "FileSystemWatcher_IsSupported"; |
162 if (_subscription != null) { | 238 |
163 _unwatchPath(); | 239 static int _initWatcher() native "FileSystemWatcher_InitWatcher"; |
164 _subscription.cancel(); | 240 static void _closeWatcher(int id) native "FileSystemWatcher_CloseWatcher"; |
165 _subscription = null; | 241 |
242 static int _watchPath(int id, String path, int events, bool recursive) | |
243 native "FileSystemWatcher_WatchPath"; | |
244 static void _unwatchPath(int id, int path_id) | |
245 native "FileSystemWatcher_UnwatchPath"; | |
246 static List _readEvents(int id, int path_id) | |
247 native "FileSystemWatcher_ReadEvents"; | |
248 static int _getSocketId(int id, int path_id) | |
249 native "FileSystemWatcher_GetSocketId"; | |
250 } | |
251 | |
252 | |
253 class _InotifyFileSystemWatcher extends _FileSystemWatcher { | |
254 static final Map<int, StreamController> _idMap = {}; | |
255 static StreamSubscription _subscription; | |
256 | |
257 _InotifyFileSystemWatcher(path, events, recursive) | |
258 : super._(path, events, recursive); | |
259 | |
260 void _newWatcher() { | |
261 int id = _FileSystemWatcher._id; | |
262 _subscription = _FileSystemWatcher._listenOnSocket(id, id, 0) | |
263 .listen((event) { | |
264 if (_idMap.containsKey(event[0])) { | |
265 if (event[1] != null) { | |
266 _idMap[event[0]].add(event[1]); | |
267 } else { | |
268 _idMap[event[0]].close(); | |
269 } | |
270 } | |
271 }); | |
272 } | |
273 | |
274 void _doneWatcher() { | |
Søren Gjesse
2013/12/03 14:42:19
Also close the inotify fd here?
Anders Johnsen
2013/12/04 10:30:52
canceling a one-way stream will close the fd.
| |
275 _subscription.cancel(); | |
276 } | |
277 | |
278 Stream _pathWatched() { | |
279 var pathId = _watcherPath.pathId; | |
280 if (!_idMap.containsKey(pathId)) { | |
281 _idMap[pathId] = new StreamController.broadcast(); | |
166 } | 282 } |
283 return _idMap[pathId].stream; | |
284 } | |
285 | |
286 void _pathWatchedEnd() { | |
287 var pathId = _watcherPath.pathId; | |
288 if (!_idMap.containsKey(pathId)) return; | |
289 _idMap[pathId].close(); | |
290 _idMap.remove(pathId); | |
291 } | |
292 } | |
293 | |
294 | |
295 class _Win32FileSystemWatcher extends _FileSystemWatcher { | |
296 StreamSubscription _subscription; | |
297 StreamController _controller; | |
298 | |
299 _Win32FileSystemWatcher(path, events, recursive) | |
300 : super._(path, events, recursive); | |
301 | |
302 void _newWatcher() { | |
Søren Gjesse
2013/12/03 14:42:19
You can move the closing curly one line up if you
Anders Johnsen
2013/12/04 10:30:52
Done.
| |
303 } | |
304 | |
305 void _doneWatcher() { | |
306 } | |
307 | |
308 Stream _pathWatched() { | |
309 var pathId = _watcherPath.pathId; | |
310 _controller = new StreamController(); | |
311 _subscription = _FileSystemWatcher._listenOnSocket(pathId, 0, pathId) | |
312 .listen((event) { | |
313 assert(event[0] == pathId); | |
314 if (event[1] != null) { | |
315 _controller.add(event[1]); | |
316 } else { | |
317 _controller.close(); | |
318 } | |
319 }); | |
320 return _controller.stream; | |
321 } | |
322 | |
323 void _pathWatchedEnd() { | |
324 _subscription.cancel(); | |
167 _controller.close(); | 325 _controller.close(); |
168 } | 326 } |
169 | 327 } |
170 Stream<FileSystemEvent> get stream => _controller.stream; | 328 |
171 | 329 |
172 static bool get isSupported native "FileSystemWatcher_IsSupported"; | 330 class _FSEventStreamFileSystemWatcher extends _FileSystemWatcher { |
173 | 331 StreamSubscription _subscription; |
174 int _watchPath(String path, int events, bool recursive) | 332 StreamController _controller; |
175 native "FileSystemWatcher_WatchPath"; | 333 |
176 void _unwatchPath() native "FileSystemWatcher_UnwatchPath"; | 334 _FSEventStreamFileSystemWatcher(path, events, recursive) |
177 List _readEvents() native "FileSystemWatcher_ReadEvents"; | 335 : super._(path, events, recursive); |
178 } | 336 |
337 void _newWatcher() { | |
338 } | |
339 | |
340 void _doneWatcher() { | |
341 } | |
342 | |
343 Stream _pathWatched() { | |
344 var pathId = _watcherPath.pathId; | |
345 var socketId = _FileSystemWatcher._getSocketId(0, pathId); | |
346 _controller = new StreamController(); | |
347 _subscription = _FileSystemWatcher._listenOnSocket(socketId, 0, pathId) | |
348 .listen((event) { | |
349 if (event[1] != null) { | |
350 _controller.add(event[1]); | |
351 } else { | |
352 _controller.close(); | |
353 } | |
354 }); | |
355 return _controller.stream; | |
356 } | |
357 | |
358 void _pathWatchedEnd() { | |
359 _subscription.cancel(); | |
360 _controller.close(); | |
361 } | |
362 } | |
363 | |
179 | 364 |
180 Uint8List _makeUint8ListView(Uint8List source, int offsetInBytes, int length) { | 365 Uint8List _makeUint8ListView(Uint8List source, int offsetInBytes, int length) { |
181 return new Uint8List.view(source.buffer, offsetInBytes, length); | 366 return new Uint8List.view(source.buffer, offsetInBytes, length); |
182 } | 367 } |
OLD | NEW |