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

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: Added more documentation Created 7 years, 11 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
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] is assuming this is a
floitsch 2013/01/28 14:45:09 The default implementation of [isBroadcast] return
Lasse Reichstein Nielsen 2013/01/29 08:42:54 Done.
46 * [asBroadcastStream] are assuming this is a single-subscription stream 46 * single-subscription stream.
47 * and a broadcast stream inheriting from [Stream] must override these 47 * A broadcast stream inheriting from [Stream] must override [isBroadcast]
48 * to return [:true:] and [:this:] respectively. 48 * to return [:true:].
49 */ 49 */
50 abstract class Stream<T> { 50 abstract class Stream<T> {
51 Stream(); 51 Stream();
52 52
53 /** 53 /**
54 * Creates a new single-subscription stream from the future. 54 * Creates a new single-subscription stream from the future.
55 * 55 *
56 * When the future completes, the stream will fire one event, either 56 * When the future completes, the stream will fire one event, either
57 * data or error, and then close with a done-event. 57 * data or error, and then close with a done-event.
58 */ 58 */
(...skipping 27 matching lines...) Expand all
86 * Returns a multi-subscription stream that produces the same events as this. 86 * Returns a multi-subscription stream that produces the same events as this.
87 * 87 *
88 * If this stream is single-subscription, return a new stream that allows 88 * If this stream is single-subscription, return a new stream that allows
89 * multiple subscribers. It will subscribe to this stream when its first 89 * multiple subscribers. It will subscribe to this stream when its first
90 * subscriber is added, and unsubscribe again when the last subscription is 90 * subscriber is added, and unsubscribe again when the last subscription is
91 * cancelled. 91 * cancelled.
92 * 92 *
93 * If this stream is already a broadcast stream, it is returned unmodified. 93 * If this stream is already a broadcast stream, it is returned unmodified.
94 */ 94 */
95 Stream<T> asBroadcastStream() { 95 Stream<T> asBroadcastStream() {
96 if (isBroadcast) return this;
96 return new _SingleStreamMultiplexer<T>(this); 97 return new _SingleStreamMultiplexer<T>(this);
97 } 98 }
98 99
99 /** 100 /**
100 * Stream that outputs events from the [sources] in cyclic order. 101 * Stream that outputs events from the [sources] in cyclic order.
101 * 102 *
102 * The merged streams are paused and resumed in order to ensure the proper 103 * The merged streams are paused and resumed in order to ensure the proper
103 * order of output events. 104 * order of output events.
104 */ 105 */
105 factory Stream.cyclic(Iterable<Stream> sources) { 106 factory Stream.cyclic(Iterable<Stream> sources) {
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
165 * 166 *
166 * If this stream sends an error that matches [test], then it is intercepted 167 * If this stream sends an error that matches [test], then it is intercepted
167 * by the [handle] function. 168 * by the [handle] function.
168 * 169 *
169 * An [AsyncError] [:e:] is matched by a test function if [:test(e):] returns 170 * An [AsyncError] [:e:] is matched by a test function if [:test(e):] returns
170 * true. If [test] is omitted, every error is considered matching. 171 * true. If [test] is omitted, every error is considered matching.
171 * 172 *
172 * If the error is intercepted, the [handle] function can decide what to do 173 * 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, 174 * 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. 175 * or simply return to make the stream forget the error.
176 *
177 * If you need to transform an error into a data event, use the more generic
178 * [Stream.transformEvent] to handle the event by writing a data event to
179 * the output sink
175 */ 180 */
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) }) { 181 Stream<T> handleError(void handle(AsyncError error), { bool test(error) }) {
178 return new HandleErrorStream<T>(this, handle, test); 182 return new HandleErrorStream<T>(this, handle, test);
179 } 183 }
180 184
181 /** 185 /**
182 * Create a new stream from this stream that converts each element 186 * Create a new stream from this stream that converts each element
183 * into zero or more events. 187 * into zero or more events.
184 * 188 *
185 * Each incoming event is converted to an [Iterable] of new events, 189 * 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 190 * and each of these new events are then sent by the returned stream
(...skipping 12 matching lines...) Expand all
199 203
200 /** 204 /**
201 * Chain this stream as the input of the provided [StreamTransformer]. 205 * Chain this stream as the input of the provided [StreamTransformer].
202 * 206 *
203 * Returns the result of [:streamTransformer.bind:] itself. 207 * Returns the result of [:streamTransformer.bind:] itself.
204 */ 208 */
205 Stream transform(StreamTransformer<T, dynamic> streamTransformer) { 209 Stream transform(StreamTransformer<T, dynamic> streamTransformer) {
206 return streamTransformer.bind(this); 210 return streamTransformer.bind(this);
207 } 211 }
208 212
213 /**
214 * Create a new stream from this by modifying events.
215 *
216 * Subscribing on the returned stream is the same as subscribing on
217 * this stream, except that events are passed through the [transformer]
218 * before being emitted. The transformer may generate any number and
219 * types of events for each incoming event.
floitsch 2013/01/28 14:45:09 Mention how pauses are handled.
Lasse Reichstein Nielsen 2013/01/29 08:42:54 Done.
220 *
221 * An example that adds one to each of a stream of integers:
floitsch 2013/01/28 14:45:09 Example is bad, since it could be implemented with
Lasse Reichstein Nielsen 2013/01/29 08:42:54 Done.
222 * intStream.transformEvents(new StreamEventTransformer.from(
floitsch 2013/01/28 14:45:09 new line before the code.
Lasse Reichstein Nielsen 2013/01/29 08:42:54 Done.
223 * handleData: (int value, StreamSink sink) {
224 * sink.add(value + 1);
225 * }));
226 */
227 Stream transformEvents(StreamEventTransformer<T, dynamic> transformer) {
floitsch 2013/01/28 14:45:09 As discussed. We should consider making this the d
Lasse Reichstein Nielsen 2013/01/29 08:42:54 Done.
228 return new EventTransformStream<T, dynamic>(this, transformer);
229 }
209 230
210 /** Reduces a sequence of values by repeatedly applying [combine]. */ 231 /** Reduces a sequence of values by repeatedly applying [combine]. */
211 Future reduce(var initialValue, combine(var previous, T element)) { 232 Future reduce(var initialValue, combine(var previous, T element)) {
212 _FutureImpl result = new _FutureImpl(); 233 _FutureImpl result = new _FutureImpl();
213 var value = initialValue; 234 var value = initialValue;
214 StreamSubscription subscription; 235 StreamSubscription subscription;
215 subscription = this.listen( 236 subscription = this.listen(
216 (T element) { 237 (T element) {
217 _runUserCode( 238 _runUserCode(
218 () => combine(value, element), 239 () => combine(value, element),
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
252 * 273 *
253 * Completes the [Future] when the answer is known. 274 * Completes the [Future] when the answer is known.
254 * If this stream reports an error, the [Future] will report that error. 275 * If this stream reports an error, the [Future] will report that error.
255 */ 276 */
256 Future<bool> contains(T match) { 277 Future<bool> contains(T match) {
257 _FutureImpl<bool> future = new _FutureImpl<bool>(); 278 _FutureImpl<bool> future = new _FutureImpl<bool>();
258 StreamSubscription subscription; 279 StreamSubscription subscription;
259 subscription = this.listen( 280 subscription = this.listen(
260 (T element) { 281 (T element) {
261 _runUserCode( 282 _runUserCode(
262 () => match(element), 283 () => (element == match),
263 (bool isMatch) { 284 (bool isMatch) {
264 if (isMatch) { 285 if (isMatch) {
265 subscription.cancel(); 286 subscription.cancel();
266 future._setValue(element); 287 future._setValue(true);
floitsch 2013/01/28 14:45:09 please add tests.
267 } 288 }
268 }, 289 },
269 _cancelAndError(subscription, future) 290 _cancelAndError(subscription, future)
270 ); 291 );
271 }, 292 },
272 onError: future._setError, 293 onError: future._setError,
273 onDone: () { 294 onDone: () {
274 future._setValue(false); 295 future._setValue(false);
275 }, 296 },
276 unsubscribeOnError: true); 297 unsubscribeOnError: true);
(...skipping 620 matching lines...) Expand 10 before | Expand all | Expand 10 after
897 */ 918 */
898 factory StreamTransformer.from({ 919 factory StreamTransformer.from({
899 void onData(S data, StreamSink<T> sink), 920 void onData(S data, StreamSink<T> sink),
900 void onError(AsyncError error, StreamSink<T> sink), 921 void onError(AsyncError error, StreamSink<T> sink),
901 void onDone(StreamSink<T> sink)}) { 922 void onDone(StreamSink<T> sink)}) {
902 return new _StreamTransformerImpl<S, T>(onData, onError, onDone); 923 return new _StreamTransformerImpl<S, T>(onData, onError, onDone);
903 } 924 }
904 925
905 Stream<T> bind(Stream<S> stream); 926 Stream<T> bind(Stream<S> stream);
906 } 927 }
928
929
930 /**
931 * A transformer of stream events.
932 *
933 * A [StreamEventTransformer] transforms incoming Stream
934 * events of one kind into outgoing events of another kind.
935 *
936 * The default implementations of the "handle" methods forward
937 * the events unmodified. This will not work for data events if the types
floitsch 2013/01/28 14:45:09 ... unmodified. In that case the generic type T ne
Lasse Reichstein Nielsen 2013/01/29 08:42:54 Done.
938 * are different.
939 *
940 * You can use a [StreamEventTransformer] to modify a Stream's events using
941 * the [Stream.transformEvents] method.
942 */
943 abstract class StreamEventTransformer<S, T> {
944 const StreamEventTransformer();
945
946 /**
947 * Create a [StreamEventTransformer] that delegates to the provided methods.
948 *
949 * The created transformer acts as if the provided functions were the
950 * methods of the same name.
951 */
952 factory StreamEventTransformer.from({
floitsch 2013/01/28 14:45:09 remove the ".from".
Lasse Reichstein Nielsen 2013/01/29 08:42:54 Not possible. That would conflict with the constru
953 void handleData(S data, StreamSink<T> sink),
954 void handleError(AsyncError error, StreamSink<T> sink),
955 void handleDone(StreamSink<T> sink)
956 }) => new _StreamEventTransformerImpl<S, T>(handleData,
floitsch 2013/01/28 14:45:09 too weird. make it a "return".
Lasse Reichstein Nielsen 2013/01/29 08:42:54 Done. It's still weird :)
957 handleError,
958 handleDone);
959
960 /**
961 * Act on incoming data event.
962 *
963 * The method may generate any number of events on the sink, but should
964 * not throw.
floitsch 2013/01/28 14:45:09 what if it throws? If we catch it, and propagate i
Lasse Reichstein Nielsen 2013/01/29 08:42:54 We don't catch it. It'll be an uncaught error in t
965 */
966 void handleData(S event, StreamSink<T> sink) {
967 var data = event;
968 sink.add(data);
969 }
970
971 /**
972 * Act on incoming error event.
973 *
974 * The method may generate any number of events on the sink, but should
975 * not throw.
976 */
977 void handleError(AsyncError error, StreamSink<T> sink) {
978 sink.signalError(error);
979 }
980
981 /**
982 * Act on incoming done event.
983 *
984 * The method may generate any number of events on the sink, but should
985 * not throw.
986 */
987 void handleDone(StreamSink<T> sink){
988 sink.close();
989 }
990 }
991
992 /**
993 * Stream that transforms another stream by intercepting and replacing events.
994 *
995 * This [Stream] is a transformation of a source stream. Listening on this
996 * stream is the same as listening on the source stream, except that events
997 * are intercepted and modified by a [StreamEventTransformer] before becoming
998 * events on this stream.
999 */
1000 class EventTransformStream<S, T> extends Stream<T> {
1001 Stream<S> _source;
1002 StreamEventTransformer _transformer;
1003 EventTransformStream(Stream<S> source,
1004 StreamEventTransformer<S, T> transformer)
1005 : _source = source, _transformer = transformer;
1006
1007 StreamSubscription<T> listen(void onData(T data),
1008 { void onError(AsyncError error),
1009 void onDone(),
1010 bool unsubscribeOnError }) {
1011 return new _EventTransformStreamSubscription(_source, _transformer,
1012 onData, onError, onDone,
1013 unsubscribeOnError);
1014 }
1015 }
1016
1017 class _EventTransformStreamSubscription<S, T>
1018 extends _BaseStreamSubscription<T>
1019 implements _StreamOutputSink<T> {
1020 /** The transformer used to transform events. */
1021 final StreamEventTransformer<S, T> _transformer;
1022 /** Whether to unsubscribe when emitting an error. */
1023 final bool _unsubscribeOnError;
1024 /** Source of incoming events. */
1025 StreamSubscription<S> _subscription;
1026 /** Cached StreamSink wrapper for this class. */
1027 StreamSink<T> _sink;
1028
1029 _EventTransformStreamSubscription(Stream<S> source,
1030 this._transformer,
1031 void onData(T data),
1032 void onError(AsyncError error),
1033 void onDone(),
1034 this._unsubscribeOnError)
1035 : super(onData, onError, onDone) {
1036 _sink = new _StreamOutputSinkWrapper<T>(this);
1037 _subscription = source.listen(_handleData,
1038 onError: _handleError,
1039 onDone: _handleDone);
1040 }
1041
1042 void pause([Future pauseSignal]) {
1043 if (_subscription != null) _subscription.pause(pauseSignal);
1044 }
1045
1046 void resume() {
1047 if (_subscription != null) _subscription.resume();
1048 }
1049
1050 void cancel() {
1051 if (_subscription != null) {
1052 _subscription.cancel();
1053 _subscription = null;
1054 }
1055 }
1056
1057 void _handleData(S data) {
1058 _transformer.handleData(data, _sink);
1059 }
1060
1061 void _handleError(AsyncError error) {
1062 _transformer.handleError(error, _sink);
1063 }
1064
1065 void _handleDone() {
1066 _transformer.handleDone(_sink);
1067 }
1068
1069 // StreamOutputSink interface.
1070 void _sendData(T data) {
1071 _onData(data);
1072 }
1073
1074 void _sendError(AsyncError error) {
1075 _onError(error);
1076 if (_unsubscribeOnError) {
1077 cancel();
1078 }
1079 }
1080
1081 void _sendDone() {
1082 // It's ok to cancel even if we have been unsubscribed already.
1083 cancel();
1084 _onDone();
1085 }
1086 }
1087
1088 class _StreamOutputSinkWrapper<T> implements StreamSink<T> {
1089 _StreamOutputSink _sink;
1090 _StreamOutputSinkWrapper(this._sink);
1091
1092 void add(T data) => _sink._sendData(data);
1093 void signalError(AsyncError error) => _sink._sendError(error);
1094 void close() => _sink._sendDone();
1095 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698