OLD | NEW |
---|---|
(Empty) | |
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 | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 #library("input_stream"); | |
6 | |
7 #import("entry.dart"); | |
8 #import("read_request.dart"); | |
9 #import("utils.dart"); | |
10 | |
11 // TODO(nweiz): ensure that the C struct associated with an input stream gets | |
12 // freed if the stream gets garbage collected before it's closed. | |
13 /** | |
14 * A stream of [ArchiveEntry]s being read from an archive. | |
15 * | |
16 * This is accessible via [ArchiveReader]. | |
17 */ | |
18 class ArchiveInputStream { | |
19 /** | |
20 * The id of the underlying archive. | |
21 * | |
22 * This will be set to null once the input stream has finished reading from | |
23 * the archive. | |
24 */ | |
25 int _id; | |
26 | |
27 /** A [Completer] that will fire once the [_onEntry] callback is set. */ | |
28 final Completer<Function> _onEntryCompleter; | |
29 | |
30 /** The callback to call when the input stream is closed. */ | |
31 Function _onClosed; | |
32 | |
33 /** The callback to call when an error occurs. */ | |
34 Function _onError; | |
35 | |
36 /** The entry that is currently eligible to read data from the archive. */ | |
37 ArchiveEntry _currentEntry; | |
38 | |
39 ArchiveInputStream(this._id) : _onEntryCompleter = new Completer<Function>() { | |
40 var future = _consumeHeaders(); | |
41 future.handleException((e) { | |
42 if (_onError != null) { | |
43 _onError(e, future.stackTrace); | |
44 return true; | |
45 } else { | |
46 throw e; | |
47 } | |
48 }); | |
49 | |
50 future.then((_) { | |
51 close(); | |
52 if (_onClosed != null) _onClosed(); | |
53 }); | |
54 } | |
55 | |
56 /** Whether this stream has finished reading entries. */ | |
57 bool get closed() => _id == null; | |
Bob Nystrom
2012/07/31 21:56:48
"isClosed".
nweiz
2012/07/31 23:38:25
That's contrary to InputStream.closed. Not that da
| |
58 | |
59 /** | |
60 * Sets a callback to call when a new entry is read from the archive. | |
61 * | |
62 * The [ArchiveEntry] that's read from an archvie initially only contains | |
Bob Nystrom
2012/07/31 21:56:48
"archive"
nweiz
2012/07/31 23:38:25
Done.
| |
63 * header information such as the filename and permissions. To get the actual | |
64 * data contained in the entry, use [ArchiveEntry.openInputStream]. | |
65 * | |
66 * Since the entries are read in sequence from the archive, the data stream | |
67 * for one entry must be opened before the next entry is read from the | |
68 * archive. The next entry will not be read until the return value of | |
69 * [callback] has completed. | |
70 * | |
71 * If [callback] calls [ArchiveEntry.openInputStream] before it returns, or if | |
72 * it doesn't want to read the contents of [entry], it can return null. | |
73 */ | |
74 void set onEntry(Future callback(ArchiveEntry entry)) { | |
75 _onEntryCompleter.complete(callback); | |
76 } | |
77 | |
78 /** | |
79 * Sets a callback to call when the input stream is done emitting entries. | |
80 */ | |
81 void set onClosed(void callback()) { | |
82 _onClosed = callback; | |
83 } | |
84 | |
85 /** | |
86 * Sets a callback to call if an error occurs while extracting the archive. | |
87 * | |
88 * [e] is the error that occured and [stack] is the stack trace of the error. | |
89 */ | |
90 void set onError(void callback(e, stack)) { | |
91 _onError = callback; | |
92 } | |
93 | |
94 /** | |
95 * Closes the input stream. No more entries will be emitted. | |
96 */ | |
97 void close() { | |
98 if (closed) return; | |
99 call(FREE, _id).then((_) {}); | |
100 _id = null; | |
101 if (_currentEntry != null) _currentEntry.close(); | |
102 if (!_onEntryCompleter.future.isComplete) _onEntryCompleter.complete(null); | |
103 } | |
104 | |
105 /** | |
106 * Consumes and emits all [ArchiveEntries] in this archive. | |
Bob Nystrom
2012/07/31 21:56:48
Is this comment correct?
nweiz
2012/07/31 23:38:25
Yes, since it calls itself recursively.
| |
107 */ | |
108 Future _consumeHeaders() { | |
109 if (closed) return new Future.immediate(null); | |
110 var data; | |
111 return call(NEXT_HEADER, _id).chain((_data) { | |
112 data = _data; | |
113 if (data == null) return new Future.immediate(null); | |
114 return _emit(new ArchiveEntry(_id, data)). | |
115 chain((_) => _consumeHeaders()); | |
116 }); | |
117 } | |
118 | |
119 /** | |
120 * Emits [entry] to the [onEntry] callback. Returns a [Future] that will | |
121 * complete once the callback's return value completes and the entry's data | |
122 * has been fully consumed. | |
123 */ | |
124 Future _emit(ArchiveEntry entry) { | |
125 _currentEntry = entry; | |
126 var future = _onEntryCompleter.future.chain((onEntry) { | |
127 if (closed) return new Future.immediate(null); | |
128 var result = onEntry(entry); | |
129 if (result is Future) return result; | |
130 return new Future.immediate(null); | |
131 }).chain((_) { | |
132 if (entry.inputOpened) return entry.inputComplete; | |
133 return new Future.immediate(null); | |
134 }); | |
135 future.onComplete((_) { | |
136 _currentEntry = null; | |
137 entry.close(); | |
138 }); | |
139 return future; | |
140 } | |
141 } | |
OLD | NEW |