Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(84)

Side by Side Diff: sdk/lib/async/stream.dart

Issue 11953103: Add public-facing method and class that allows intercepting stream events. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Address review comments Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « sdk/lib/async/merge_stream.dart ('k') | sdk/lib/async/stream_impl.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 part of dart.async; 5 part of dart.async;
6 6
7 // ------------------------------------------------------------------- 7 // -------------------------------------------------------------------
8 // Core Stream types 8 // Core Stream types
9 // ------------------------------------------------------------------- 9 // -------------------------------------------------------------------
10 10
(...skipping 22 matching lines...) Expand all
33 * A single-subscription stream allows only a single listener at a time. 33 * A single-subscription stream allows only a single listener at a time.
34 * It holds back events until it gets a listener, and it may exhaust 34 * It holds back events until it gets a listener, and it may exhaust
35 * itself when the listener is unsubscribed, even if the stream wasn't done. 35 * itself when the listener is unsubscribed, even if the stream wasn't done.
36 * 36 *
37 * Single-subscription streams are generally used for streaming parts of 37 * Single-subscription streams are generally used for streaming parts of
38 * contiguous data like file I/O. 38 * contiguous data like file I/O.
39 * 39 *
40 * A broadcast stream allows any number of listeners, and it fires 40 * A broadcast stream allows any number of listeners, and it fires
41 * its events when they are ready, whether there are listeners or not. 41 * its events when they are ready, whether there are listeners or not.
42 * 42 *
43 * Braodcast streams are used for independent events/observers. 43 * Broadcast streams are used for independent events/observers.
44 * 44 *
45 * The default implementation of [isBroadcast] and 45 * The default implementation of [isBroadcast] returns false.
46 * [asBroadcastStream] are assuming this is a single-subscription stream 46 * A broadcast stream inheriting from [Stream] must override [isBroadcast]
47 * and a broadcast stream inheriting from [Stream] must override these 47 * to return [:true:].
48 * to return [:true:] and [:this:] respectively.
49 */ 48 */
50 abstract class Stream<T> { 49 abstract class Stream<T> {
51 Stream(); 50 Stream();
52 51
53 /** 52 /**
54 * Creates a new single-subscription stream from the future. 53 * Creates a new single-subscription stream from the future.
55 * 54 *
56 * When the future completes, the stream will fire one event, either 55 * When the future completes, the stream will fire one event, either
57 * data or error, and then close with a done-event. 56 * data or error, and then close with a done-event.
58 */ 57 */
(...skipping 27 matching lines...) Expand all
86 * Returns a multi-subscription stream that produces the same events as this. 85 * Returns a multi-subscription stream that produces the same events as this.
87 * 86 *
88 * If this stream is single-subscription, return a new stream that allows 87 * If this stream is single-subscription, return a new stream that allows
89 * multiple subscribers. It will subscribe to this stream when its first 88 * multiple subscribers. It will subscribe to this stream when its first
90 * subscriber is added, and unsubscribe again when the last subscription is 89 * subscriber is added, and unsubscribe again when the last subscription is
91 * cancelled. 90 * cancelled.
92 * 91 *
93 * If this stream is already a broadcast stream, it is returned unmodified. 92 * If this stream is already a broadcast stream, it is returned unmodified.
94 */ 93 */
95 Stream<T> asBroadcastStream() { 94 Stream<T> asBroadcastStream() {
95 if (isBroadcast) return this;
96 return new _SingleStreamMultiplexer<T>(this); 96 return new _SingleStreamMultiplexer<T>(this);
97 } 97 }
98 98
99 /** 99 /**
100 * Stream that outputs events from the [sources] in cyclic order. 100 * Stream that outputs events from the [sources] in cyclic order.
101 * 101 *
102 * The merged streams are paused and resumed in order to ensure the proper 102 * The merged streams are paused and resumed in order to ensure the proper
103 * order of output events. 103 * order of output events.
104 */ 104 */
105 factory Stream.cyclic(Iterable<Stream> sources) { 105 factory Stream.cyclic(Iterable<Stream> sources) {
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
165 * 165 *
166 * If this stream sends an error that matches [test], then it is intercepted 166 * If this stream sends an error that matches [test], then it is intercepted
167 * by the [handle] function. 167 * by the [handle] function.
168 * 168 *
169 * An [AsyncError] [:e:] is matched by a test function if [:test(e):] returns 169 * An [AsyncError] [:e:] is matched by a test function if [:test(e):] returns
170 * true. If [test] is omitted, every error is considered matching. 170 * true. If [test] is omitted, every error is considered matching.
171 * 171 *
172 * If the error is intercepted, the [handle] function can decide what to do 172 * If the error is intercepted, the [handle] function can decide what to do
173 * with it. It can throw if it wants to raise a new (or the same) error, 173 * with it. It can throw if it wants to raise a new (or the same) error,
174 * or simply return to make the stream forget the error. 174 * or simply return to make the stream forget the error.
175 *
176 * If you need to transform an error into a data event, use the more generic
177 * [Stream.transformEvent] to handle the event by writing a data event to
178 * the output sink
175 */ 179 */
176 // TODO(lrn): Say what to do if you want to convert the error to a value.
177 Stream<T> handleError(void handle(AsyncError error), { bool test(error) }) { 180 Stream<T> handleError(void handle(AsyncError error), { bool test(error) }) {
178 return new _HandleErrorStream<T>(this, handle, test); 181 return new _HandleErrorStream<T>(this, handle, test);
179 } 182 }
180 183
181 /** 184 /**
182 * Create a new stream from this stream that converts each element 185 * Create a new stream from this stream that converts each element
183 * into zero or more events. 186 * into zero or more events.
184 * 187 *
185 * Each incoming event is converted to an [Iterable] of new events, 188 * Each incoming event is converted to an [Iterable] of new events,
186 * and each of these new events are then sent by the returned stream 189 * and each of these new events are then sent by the returned stream
(...skipping 12 matching lines...) Expand all
199 202
200 /** 203 /**
201 * Chain this stream as the input of the provided [StreamTransformer]. 204 * Chain this stream as the input of the provided [StreamTransformer].
202 * 205 *
203 * Returns the result of [:streamTransformer.bind:] itself. 206 * Returns the result of [:streamTransformer.bind:] itself.
204 */ 207 */
205 Stream transform(StreamTransformer<T, dynamic> streamTransformer) { 208 Stream transform(StreamTransformer<T, dynamic> streamTransformer) {
206 return streamTransformer.bind(this); 209 return streamTransformer.bind(this);
207 } 210 }
208 211
212 /**
213 * Create a new stream from this by modifying events.
214 *
215 * Subscribing on the returned stream is the same as subscribing on
216 * this stream, except that events are passed through the [transformer]
217 * before being emitted. The transformer may generate any number and
218 * types of events for each incoming event. Pauses on the returned
219 * subscription are pauses on this stream.
220 *
221 * An example that duplicates all data events:
222 *
223 * someStream.transformEvents(new StreamEventTransformer.from(
224 * handleData: (var value, StreamSink sink) {
225 * sink.add(value);
226 * sink.add(value);
227 * }));
228 */
229 Stream transformEvents(StreamEventTransformer<T, dynamic> transformer) {
230 return new EventTransformStream<T, dynamic>(this, transformer);
231 }
209 232
210 /** Reduces a sequence of values by repeatedly applying [combine]. */ 233 /** Reduces a sequence of values by repeatedly applying [combine]. */
211 Future reduce(var initialValue, combine(var previous, T element)) { 234 Future reduce(var initialValue, combine(var previous, T element)) {
212 _FutureImpl result = new _FutureImpl(); 235 _FutureImpl result = new _FutureImpl();
213 var value = initialValue; 236 var value = initialValue;
214 StreamSubscription subscription; 237 StreamSubscription subscription;
215 subscription = this.listen( 238 subscription = this.listen(
216 // TODO(ahe): Restore type when feature is implemented in dart2js 239 // TODO(ahe): Restore type when feature is implemented in dart2js
217 // checked mode. http://dartbug.com/7733 240 // checked mode. http://dartbug.com/7733
218 (/*T*/ element) { 241 (/*T*/ element) {
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
256 * If this stream reports an error, the [Future] will report that error. 279 * If this stream reports an error, the [Future] will report that error.
257 */ 280 */
258 Future<bool> contains(T match) { 281 Future<bool> contains(T match) {
259 _FutureImpl<bool> future = new _FutureImpl<bool>(); 282 _FutureImpl<bool> future = new _FutureImpl<bool>();
260 StreamSubscription subscription; 283 StreamSubscription subscription;
261 subscription = this.listen( 284 subscription = this.listen(
262 // TODO(ahe): Restore type when feature is implemented in dart2js 285 // TODO(ahe): Restore type when feature is implemented in dart2js
263 // checked mode. http://dartbug.com/7733 286 // checked mode. http://dartbug.com/7733
264 (/*T*/ element) { 287 (/*T*/ element) {
265 _runUserCode( 288 _runUserCode(
266 () => match(element), 289 () => (element == match),
267 (bool isMatch) { 290 (bool isMatch) {
268 if (isMatch) { 291 if (isMatch) {
269 subscription.cancel(); 292 subscription.cancel();
270 future._setValue(element); 293 future._setValue(true);
271 } 294 }
272 }, 295 },
273 _cancelAndError(subscription, future) 296 _cancelAndError(subscription, future)
274 ); 297 );
275 }, 298 },
276 onError: future._setError, 299 onError: future._setError,
277 onDone: () { 300 onDone: () {
278 future._setValue(false); 301 future._setValue(false);
279 }, 302 },
280 unsubscribeOnError: true); 303 unsubscribeOnError: true);
(...skipping 646 matching lines...) Expand 10 before | Expand all | Expand 10 after
927 */ 950 */
928 factory StreamTransformer.from({ 951 factory StreamTransformer.from({
929 void onData(S data, StreamSink<T> sink), 952 void onData(S data, StreamSink<T> sink),
930 void onError(AsyncError error, StreamSink<T> sink), 953 void onError(AsyncError error, StreamSink<T> sink),
931 void onDone(StreamSink<T> sink)}) { 954 void onDone(StreamSink<T> sink)}) {
932 return new _StreamTransformerImpl<S, T>(onData, onError, onDone); 955 return new _StreamTransformerImpl<S, T>(onData, onError, onDone);
933 } 956 }
934 957
935 Stream<T> bind(Stream<S> stream); 958 Stream<T> bind(Stream<S> stream);
936 } 959 }
960
961
962 /**
963 * A transformer of stream events.
964 *
965 * A [StreamEventTransformer] transforms incoming Stream
966 * events of one kind into outgoing events of another kind.
967 *
968 * The default implementations of the "handle" methods forward
969 * the events unmodified. In that case the generic type [T] needs to be
970 * assignable to [S].
971 *
972 * You can use a [StreamEventTransformer] to modify a Stream's events using
973 * the [Stream.transformEvents] method.
974 */
975 abstract class StreamEventTransformer<S, T> {
976 const StreamEventTransformer();
977
978 /**
979 * Create a [StreamEventTransformer] that delegates to the provided methods.
980 *
981 * The created transformer acts as if the provided functions were the
982 * methods of the same name.
983 */
984 factory StreamEventTransformer.from({
985 void handleData(S data, StreamSink<T> sink),
986 void handleError(AsyncError error, StreamSink<T> sink),
987 void handleDone(StreamSink<T> sink)
988 }) {
989 return new _StreamEventTransformerImpl<S, T>(handleData,
990 handleError,
991 handleDone);
992 }
993
994
995 /**
996 * Act on incoming data event.
997 *
998 * The method may generate any number of events on the sink, but should
999 * not throw.
1000 */
1001 void handleData(S event, StreamSink<T> sink) {
1002 var data = event;
1003 sink.add(data);
1004 }
1005
1006 /**
1007 * Act on incoming error event.
1008 *
1009 * The method may generate any number of events on the sink, but should
1010 * not throw.
1011 */
1012 void handleError(AsyncError error, StreamSink<T> sink) {
1013 sink.signalError(error);
1014 }
1015
1016 /**
1017 * Act on incoming done event.
1018 *
1019 * The method may generate any number of events on the sink, but should
1020 * not throw.
1021 */
1022 void handleDone(StreamSink<T> sink){
1023 sink.close();
1024 }
1025 }
1026
1027 /**
1028 * Stream that transforms another stream by intercepting and replacing events.
1029 *
1030 * This [Stream] is a transformation of a source stream. Listening on this
1031 * stream is the same as listening on the source stream, except that events
1032 * are intercepted and modified by a [StreamEventTransformer] before becoming
1033 * events on this stream.
1034 */
1035 class EventTransformStream<S, T> extends Stream<T> {
1036 Stream<S> _source;
1037 StreamEventTransformer _transformer;
1038 EventTransformStream(Stream<S> source,
1039 StreamEventTransformer<S, T> transformer)
1040 : _source = source, _transformer = transformer;
1041
1042 StreamSubscription<T> listen(void onData(T data),
1043 { void onError(AsyncError error),
1044 void onDone(),
1045 bool unsubscribeOnError }) {
1046 return new _EventTransformStreamSubscription(_source, _transformer,
1047 onData, onError, onDone,
1048 unsubscribeOnError);
1049 }
1050 }
1051
1052 class _EventTransformStreamSubscription<S, T>
1053 extends _BaseStreamSubscription<T>
1054 implements _StreamOutputSink<T> {
1055 /** The transformer used to transform events. */
1056 final StreamEventTransformer<S, T> _transformer;
1057 /** Whether to unsubscribe when emitting an error. */
1058 final bool _unsubscribeOnError;
1059 /** Source of incoming events. */
1060 StreamSubscription<S> _subscription;
1061 /** Cached StreamSink wrapper for this class. */
1062 StreamSink<T> _sink;
1063
1064 _EventTransformStreamSubscription(Stream<S> source,
1065 this._transformer,
1066 void onData(T data),
1067 void onError(AsyncError error),
1068 void onDone(),
1069 this._unsubscribeOnError)
1070 : super(onData, onError, onDone) {
1071 _sink = new _StreamOutputSinkWrapper<T>(this);
1072 _subscription = source.listen(_handleData,
1073 onError: _handleError,
1074 onDone: _handleDone);
1075 }
1076
1077 void pause([Future pauseSignal]) {
1078 if (_subscription != null) _subscription.pause(pauseSignal);
1079 }
1080
1081 void resume() {
1082 if (_subscription != null) _subscription.resume();
1083 }
1084
1085 void cancel() {
1086 if (_subscription != null) {
1087 _subscription.cancel();
1088 _subscription = null;
1089 }
1090 }
1091
1092 void _handleData(S data) {
1093 _transformer.handleData(data, _sink);
1094 }
1095
1096 void _handleError(AsyncError error) {
1097 _transformer.handleError(error, _sink);
1098 }
1099
1100 void _handleDone() {
1101 _transformer.handleDone(_sink);
1102 }
1103
1104 // StreamOutputSink interface.
1105 void _sendData(T data) {
1106 _onData(data);
1107 }
1108
1109 void _sendError(AsyncError error) {
1110 _onError(error);
1111 if (_unsubscribeOnError) {
1112 cancel();
1113 }
1114 }
1115
1116 void _sendDone() {
1117 // It's ok to cancel even if we have been unsubscribed already.
1118 cancel();
1119 _onDone();
1120 }
1121 }
1122
1123 class _StreamOutputSinkWrapper<T> implements StreamSink<T> {
1124 _StreamOutputSink _sink;
1125 _StreamOutputSinkWrapper(this._sink);
1126
1127 void add(T data) => _sink._sendData(data);
1128 void signalError(AsyncError error) => _sink._sendError(error);
1129 void close() => _sink._sendDone();
1130 }
OLDNEW
« no previous file with comments | « sdk/lib/async/merge_stream.dart ('k') | sdk/lib/async/stream_impl.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698