Line data Source code
1 : // Copyright (c) 2015, 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 : import "dart:async";
6 :
7 : /// A single-subscription [stream] where the contents are provided later.
8 : ///
9 : /// It is generally recommended that you never create a `Future<Stream>`
10 : /// because you can just directly create a stream that doesn't do anything
11 : /// until it's ready to do so.
12 : /// This class can be used to create such a stream.
13 : ///
14 : /// The [stream] is a normal stream that you can listen to immediately,
15 : /// but until either [setSourceStream] or [setEmpty] is called,
16 : /// the stream won't produce any events.
17 : ///
18 : /// The same effect can be achieved by using a [StreamController]
19 : /// and adding the stream using `addStream` when both
20 : /// the controller's stream is listened to and the source stream is ready.
21 : /// This class attempts to shortcut some of the overhead when possible.
22 : /// For example, if the [stream] is only listened to
23 : /// after the source stream has been set,
24 : /// the listen is performed directly on the source stream.
25 : class StreamCompleter<T> {
26 : /// The stream doing the actual work, is returned by [stream].
27 : final _stream = new _CompleterStream<T>();
28 :
29 : /// Convert a `Future<Stream>` to a `Stream`.
30 : ///
31 : /// This creates a stream using a stream completer,
32 : /// and sets the source stream to the result of the future when the
33 : /// future completes.
34 : ///
35 : /// If the future completes with an error, the returned stream will
36 : /// instead contain just that error.
37 : static Stream<T> fromFuture<T>(Future<Stream<T>> streamFuture) {
38 0 : var completer = new StreamCompleter<T>();
39 0 : streamFuture.then(completer.setSourceStream, onError: completer.setError);
40 0 : return completer.stream;
41 : }
42 :
43 : /// The stream of this completer.
44 : ///
45 : /// This stream is always a single-subscription stream.
46 : ///
47 : /// When a source stream is provided, its events will be forwarded to
48 : /// listeners on this stream.
49 : ///
50 : /// The stream can be listened either before or after a source stream
51 : /// is set.
52 0 : Stream<T> get stream => _stream;
53 :
54 : /// Set a stream as the source of events for the [StreamCompleter]'s
55 : /// [stream].
56 : ///
57 : /// The completer's `stream` will act exactly as [sourceStream].
58 : ///
59 : /// If the source stream is set before [stream] is listened to,
60 : /// the listen call on [stream] is forwarded directly to [sourceStream].
61 : ///
62 : /// If [stream] is listened to before setting the source stream,
63 : /// an intermediate subscription is created. It looks like a completely
64 : /// normal subscription, and can be paused or canceled, but it won't
65 : /// produce any events until a source stream is provided.
66 : ///
67 : /// If the `stream` subscription is canceled before a source stream is set,
68 : /// the source stream will be listened to and immediately canceled again.
69 : ///
70 : /// Otherwise, when the source stream is then set,
71 : /// it is immediately listened to, and its events are forwarded to the
72 : /// existing subscription.
73 : ///
74 : /// Any one of [setSourceStream], [setEmpty], and [setError] may be called at
75 : /// most once. Trying to call any of them again will fail.
76 : void setSourceStream(Stream<T> sourceStream) {
77 0 : if (_stream._isSourceStreamSet) {
78 0 : throw new StateError("Source stream already set");
79 : }
80 0 : _stream._setSourceStream(sourceStream);
81 : }
82 :
83 : /// Equivalent to setting an empty stream using [setSourceStream].
84 : ///
85 : /// Any one of [setSourceStream], [setEmpty], and [setError] may be called at
86 : /// most once. Trying to call any of them again will fail.
87 : void setEmpty() {
88 0 : if (_stream._isSourceStreamSet) {
89 0 : throw new StateError("Source stream already set");
90 : }
91 0 : _stream._setEmpty();
92 : }
93 :
94 : /// Completes this to a stream that emits [error] and then closes.
95 : ///
96 : /// This is useful when the process of creating the data for the stream fails.
97 : ///
98 : /// Any one of [setSourceStream], [setEmpty], and [setError] may be called at
99 : /// most once. Trying to call any of them again will fail.
100 : void setError(error, [StackTrace stackTrace]) {
101 0 : setSourceStream(new Stream.fromFuture(new Future.error(error, stackTrace)));
102 : }
103 : }
104 :
105 : /// Stream completed by [StreamCompleter].
106 : class _CompleterStream<T> extends Stream<T> {
107 : /// Controller for an intermediate stream.
108 : ///
109 : /// Created if the user listens on this stream before the source stream
110 : /// is set, or if using [_setEmpty] so there is no source stream.
111 : StreamController<T> _controller;
112 :
113 : /// Source stream for the events provided by this stream.
114 : ///
115 : /// Set when the completer sets the source stream using [_setSourceStream]
116 : /// or [_setEmpty].
117 : Stream<T> _sourceStream;
118 :
119 : StreamSubscription<T> listen(onData(T data),
120 : {Function onError, void onDone(), bool cancelOnError}) {
121 0 : if (_controller == null) {
122 0 : if (_sourceStream != null && !_sourceStream.isBroadcast) {
123 : // If the source stream is itself single subscription,
124 : // just listen to it directly instead of creating a controller.
125 0 : return _sourceStream.listen(onData,
126 : onError: onError, onDone: onDone, cancelOnError: cancelOnError);
127 : }
128 0 : _createController();
129 0 : if (_sourceStream != null) {
130 0 : _linkStreamToController();
131 : }
132 : }
133 0 : return _controller.stream.listen(onData,
134 : onError: onError, onDone: onDone, cancelOnError: cancelOnError);
135 : }
136 :
137 : /// Whether a source stream has been set.
138 : ///
139 : /// Used to throw an error if trying to set a source stream twice.
140 0 : bool get _isSourceStreamSet => _sourceStream != null;
141 :
142 : /// Sets the source stream providing the events for this stream.
143 : ///
144 : /// If set before the user listens, listen calls will be directed directly
145 : /// to the source stream. If the user listenes earlier, and intermediate
146 : /// stream is created using a stream controller, and the source stream is
147 : /// linked into that stream later.
148 : void _setSourceStream(Stream<T> sourceStream) {
149 : assert(_sourceStream == null);
150 0 : _sourceStream = sourceStream;
151 0 : if (_controller != null) {
152 : // User has already listened, so provide the data through controller.
153 0 : _linkStreamToController();
154 : }
155 : }
156 :
157 : /// Links source stream to controller when both are available.
158 : void _linkStreamToController() {
159 : assert(_controller != null);
160 : assert(_sourceStream != null);
161 0 : _controller
162 0 : .addStream(_sourceStream, cancelOnError: false)
163 0 : .whenComplete(_controller.close);
164 : }
165 :
166 : /// Sets an empty source stream.
167 : ///
168 : /// Uses [_controller] for the stream, then closes the controller
169 : /// immediately.
170 : void _setEmpty() {
171 : assert(_sourceStream == null);
172 0 : if (_controller == null) {
173 0 : _createController();
174 : }
175 0 : _sourceStream = _controller.stream; // Mark stream as set.
176 0 : _controller.close();
177 : }
178 :
179 : // Creates the [_controller].
180 : void _createController() {
181 : assert(_controller == null);
182 0 : _controller = new StreamController<T>(sync: true);
183 : }
184 : }
|