OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 library barback.asset.asset_node; | 5 library barback.asset_node; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 | 8 |
9 import '../errors.dart'; | |
10 import '../graph/transform_node.dart'; | |
11 import '../utils.dart'; | |
12 import 'asset.dart'; | 9 import 'asset.dart'; |
13 import 'asset_id.dart'; | 10 import 'asset_id.dart'; |
| 11 import 'errors.dart'; |
| 12 import 'transform_node.dart'; |
| 13 import 'utils.dart'; |
14 | 14 |
15 /// Describes the current state of an asset as part of a transformation graph. | 15 /// Describes the current state of an asset as part of a transformation graph. |
16 /// | 16 /// |
17 /// An asset node can be in one of three states (see [AssetState]). It provides | 17 /// An asset node can be in one of three states (see [AssetState]). It provides |
18 /// an [onStateChange] stream that emits an event whenever it changes state. | 18 /// an [onStateChange] stream that emits an event whenever it changes state. |
19 /// | 19 /// |
20 /// Asset nodes are controlled using [AssetNodeController]s. | 20 /// Asset nodes are controlled using [AssetNodeController]s. |
21 class AssetNode { | 21 class AssetNode { |
22 /// The id of the asset that this node represents. | 22 /// The id of the asset that this node represents. |
23 final AssetId id; | 23 final AssetId id; |
(...skipping 27 matching lines...) Expand all Loading... |
51 Asset _asset; | 51 Asset _asset; |
52 | 52 |
53 /// The callback to be called to notify this asset node's creator that the | 53 /// The callback to be called to notify this asset node's creator that the |
54 /// concrete asset should be generated. | 54 /// concrete asset should be generated. |
55 /// | 55 /// |
56 /// This is null for non-lazy asset nodes (see [AssetNodeController.lazy]). | 56 /// This is null for non-lazy asset nodes (see [AssetNodeController.lazy]). |
57 /// Once this is called, it's set to null and [this] is no longer considered | 57 /// Once this is called, it's set to null and [this] is no longer considered |
58 /// lazy. | 58 /// lazy. |
59 Function _lazyCallback; | 59 Function _lazyCallback; |
60 | 60 |
61 /// Whether this node is lazy, meaning that [force] must be called to | |
62 /// guarantee that it will eventually become available. | |
63 bool get isLazy => _lazyCallback != null || | |
64 (_origin != null && _origin.isLazy); | |
65 | |
66 /// A broadcast stream that emits an event whenever the node changes state. | 61 /// A broadcast stream that emits an event whenever the node changes state. |
67 /// | 62 /// |
68 /// This stream is synchronous to ensure that when a source asset is modified | 63 /// This stream is synchronous to ensure that when a source asset is modified |
69 /// or removed, the appropriate portion of the asset graph is dirtied before | 64 /// or removed, the appropriate portion of the asset graph is dirtied before |
70 /// any [Barback.getAssetById] calls emit newly-incorrect values. | 65 /// any [Barback.getAssetById] calls emit newly-incorrect values. |
71 Stream<AssetState> get onStateChange => _stateChangeController.stream; | 66 Stream<AssetState> get onStateChange => _stateChangeController.stream; |
72 | 67 |
73 /// This is synchronous so that a source being updated will always be | 68 /// This is synchronous so that a source being updated will always be |
74 /// propagated through the build graph before anything that depends on it is | 69 /// propagated through the build graph before anything that depends on it is |
75 /// requested. | 70 /// requested. |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
115 /// [callback] is called synchronously if this is already in such a state. | 110 /// [callback] is called synchronously if this is already in such a state. |
116 /// | 111 /// |
117 /// The return value of [callback] is piped to the returned Future. | 112 /// The return value of [callback] is piped to the returned Future. |
118 Future _waitForState(bool test(AssetState state), | 113 Future _waitForState(bool test(AssetState state), |
119 callback(AssetState state)) { | 114 callback(AssetState state)) { |
120 if (test(state)) return syncFuture(() => callback(state)); | 115 if (test(state)) return syncFuture(() => callback(state)); |
121 return onStateChange.firstWhere(test).then((_) => callback(state)); | 116 return onStateChange.firstWhere(test).then((_) => callback(state)); |
122 } | 117 } |
123 | 118 |
124 AssetNode._(this.id, this._transform, this._origin) | 119 AssetNode._(this.id, this._transform, this._origin) |
125 : _state = AssetState.RUNNING; | 120 : _state = AssetState.DIRTY; |
126 | 121 |
127 AssetNode._available(Asset asset, this._transform, this._origin) | 122 AssetNode._available(Asset asset, this._transform, this._origin) |
128 : id = asset.id, | 123 : id = asset.id, |
129 _asset = asset, | 124 _asset = asset, |
130 _state = AssetState.AVAILABLE; | 125 _state = AssetState.AVAILABLE; |
131 | 126 |
132 AssetNode._lazy(this.id, this._transform, this._origin, this._lazyCallback) | 127 AssetNode._lazy(this.id, this._transform, this._origin, this._lazyCallback) |
133 : _state = AssetState.RUNNING; | 128 : _state = AssetState.DIRTY; |
134 | 129 |
135 /// If [this] is lazy, force it to generate a concrete asset; otherwise, do | 130 /// If [this] is lazy, force it to generate a concrete asset; otherwise, do |
136 /// nothing. | 131 /// nothing. |
137 /// | 132 /// |
138 /// See [AssetNodeController.lazy]. | 133 /// See [AssetNodeController.lazy]. |
139 void force() { | 134 void force() { |
140 if (_origin != null) { | 135 if (_origin != null) { |
141 _origin.force(); | 136 _origin.force(); |
142 } else if (_lazyCallback != null) { | 137 } else if (_lazyCallback != null) { |
143 _lazyCallback(); | 138 _lazyCallback(); |
144 _lazyCallback = null; | 139 _lazyCallback = null; |
145 } | 140 } |
146 } | 141 } |
147 | 142 |
148 String toString() => "${isLazy ? 'lazy' : state} asset $id"; | 143 String toString() => |
| 144 "$state${_lazyCallback == null ? '' : ' lazy'} asset $id"; |
149 } | 145 } |
150 | 146 |
151 /// The controller for an [AssetNode]. | 147 /// The controller for an [AssetNode]. |
152 /// | 148 /// |
153 /// This controls which state the node is in. | 149 /// This controls which state the node is in. |
154 class AssetNodeController { | 150 class AssetNodeController { |
155 final AssetNode node; | 151 final AssetNode node; |
156 | 152 |
157 /// Creates a controller for a dirty node. | 153 /// Creates a controller for a dirty node. |
158 AssetNodeController(AssetId id, [TransformNode transform]) | 154 AssetNodeController(AssetId id, [TransformNode transform]) |
(...skipping 26 matching lines...) Expand all Loading... |
185 /// If [node] is lazy, the returned node will also be lazy. | 181 /// If [node] is lazy, the returned node will also be lazy. |
186 AssetNodeController.from(AssetNode node) | 182 AssetNodeController.from(AssetNode node) |
187 : node = new AssetNode._(node.id, node.transform, node.origin) { | 183 : node = new AssetNode._(node.id, node.transform, node.origin) { |
188 if (node.state.isAvailable) { | 184 if (node.state.isAvailable) { |
189 setAvailable(node.asset); | 185 setAvailable(node.asset); |
190 } else if (node.state.isRemoved) { | 186 } else if (node.state.isRemoved) { |
191 setRemoved(); | 187 setRemoved(); |
192 } | 188 } |
193 } | 189 } |
194 | 190 |
195 /// Marks the node as [AssetState.RUNNING]. | 191 /// Marks the node as [AssetState.DIRTY]. |
196 void setDirty() { | 192 void setDirty() { |
197 assert(node._state != AssetState.REMOVED); | 193 assert(node._state != AssetState.REMOVED); |
| 194 node._state = AssetState.DIRTY; |
198 node._asset = null; | 195 node._asset = null; |
199 node._lazyCallback = null; | 196 node._lazyCallback = null; |
200 | 197 node._stateChangeController.add(AssetState.DIRTY); |
201 // Don't re-emit a dirty event to avoid cases where we try to dispatch an | |
202 // event while handling another event (e.g. an output is marked lazy, which | |
203 // causes it to be forced, which causes it to be marked dirty). | |
204 if (node._state.isDirty) return; | |
205 node._state = AssetState.RUNNING; | |
206 node._stateChangeController.add(AssetState.RUNNING); | |
207 } | 198 } |
208 | 199 |
209 /// Marks the node as [AssetState.REMOVED]. | 200 /// Marks the node as [AssetState.REMOVED]. |
210 /// | 201 /// |
211 /// Once a node is marked as removed, it can't be marked as any other state. | 202 /// Once a node is marked as removed, it can't be marked as any other state. |
212 /// If a new asset is created with the same id, it will get a new node. | 203 /// If a new asset is created with the same id, it will get a new node. |
213 void setRemoved() { | 204 void setRemoved() { |
214 assert(node._state != AssetState.REMOVED); | 205 assert(node._state != AssetState.REMOVED); |
215 node._state = AssetState.REMOVED; | 206 node._state = AssetState.REMOVED; |
216 node._asset = null; | 207 node._asset = null; |
217 node._lazyCallback = null; | 208 node._lazyCallback = null; |
218 node._stateChangeController.add(AssetState.REMOVED); | 209 node._stateChangeController.add(AssetState.REMOVED); |
219 } | 210 } |
220 | 211 |
221 /// Marks the node as [AssetState.AVAILABLE] with the given concrete [asset]. | 212 /// Marks the node as [AssetState.AVAILABLE] with the given concrete [asset]. |
222 /// | 213 /// |
223 /// It's an error to mark an already-available node as available. It should be | 214 /// It's an error to mark an already-available node as available. It should be |
224 /// marked as dirty first. | 215 /// marked as dirty first. |
225 void setAvailable(Asset asset) { | 216 void setAvailable(Asset asset) { |
226 assert(asset.id == node.id); | 217 assert(asset.id == node.id); |
227 assert(node._state != AssetState.REMOVED); | 218 assert(node._state != AssetState.REMOVED); |
228 assert(node._state != AssetState.AVAILABLE); | 219 assert(node._state != AssetState.AVAILABLE); |
229 node._state = AssetState.AVAILABLE; | 220 node._state = AssetState.AVAILABLE; |
230 node._asset = asset; | 221 node._asset = asset; |
231 node._lazyCallback = null; | 222 node._lazyCallback = null; |
232 node._stateChangeController.add(AssetState.AVAILABLE); | 223 node._stateChangeController.add(AssetState.AVAILABLE); |
233 } | 224 } |
234 | 225 |
235 /// Marks the node as [AssetState.RUNNING] and lazy. | 226 /// Marks the node as [AssetState.DIRTY] and lazy. |
236 /// | 227 /// |
237 /// Lazy nodes aren't expected to have their values generated until needed. | 228 /// Lazy nodes aren't expected to have their values generated until needed. |
238 /// Once it's necessary, [callback] will be called. [callback] is guaranteed | 229 /// Once it's necessary, [callback] will be called. [callback] is guaranteed |
239 /// to be called only once. | 230 /// to be called only once. |
240 /// | 231 /// |
241 /// See also [AssetNodeController.lazy]. | 232 /// See also [AssetNodeController.lazy]. |
242 void setLazy(void callback()) { | 233 void setLazy(void callback()) { |
243 assert(node._state != AssetState.REMOVED); | 234 assert(node._state != AssetState.REMOVED); |
244 node._state = AssetState.RUNNING; | 235 node._state = AssetState.DIRTY; |
245 node._asset = null; | 236 node._asset = null; |
246 node._lazyCallback = callback; | 237 node._lazyCallback = callback; |
247 node._stateChangeController.add(AssetState.RUNNING); | 238 node._stateChangeController.add(AssetState.DIRTY); |
248 } | 239 } |
249 | 240 |
250 String toString() => "controller for $node"; | 241 String toString() => "controller for $node"; |
251 } | 242 } |
252 | 243 |
253 // TODO(nweiz): add an error state. | 244 // TODO(nweiz): add an error state. |
254 /// An enum of states that an [AssetNode] can be in. | 245 /// An enum of states that an [AssetNode] can be in. |
255 class AssetState { | 246 class AssetState { |
256 /// The node has a concrete asset loaded, available, and up-to-date. The asset | 247 /// The node has a concrete asset loaded, available, and up-to-date. The asset |
257 /// is accessible via [AssetNode.asset]. An asset can only be marked available | 248 /// is accessible via [AssetNode.asset]. An asset can only be marked available |
258 /// again from the [AssetState.RUNNING] state. | 249 /// again from the [AssetState.DIRTY] state. |
259 static final AVAILABLE = const AssetState._("available"); | 250 static final AVAILABLE = const AssetState._("available"); |
260 | 251 |
261 /// The asset is no longer available, possibly for good. A removed asset will | 252 /// The asset is no longer available, possibly for good. A removed asset will |
262 /// never enter another state. | 253 /// never enter another state. |
263 static final REMOVED = const AssetState._("removed"); | 254 static final REMOVED = const AssetState._("removed"); |
264 | 255 |
265 /// The asset will exist in the future (unless it's removed), but the concrete | 256 /// The asset will exist in the future (unless it's removed), but the concrete |
266 /// asset is not yet available. | 257 /// asset is not yet available. |
267 static final RUNNING = const AssetState._("dirty"); | 258 static final DIRTY = const AssetState._("dirty"); |
268 | 259 |
269 /// Whether this state is [AssetState.AVAILABLE]. | 260 /// Whether this state is [AssetState.AVAILABLE]. |
270 bool get isAvailable => this == AssetState.AVAILABLE; | 261 bool get isAvailable => this == AssetState.AVAILABLE; |
271 | 262 |
272 /// Whether this state is [AssetState.REMOVED]. | 263 /// Whether this state is [AssetState.REMOVED]. |
273 bool get isRemoved => this == AssetState.REMOVED; | 264 bool get isRemoved => this == AssetState.REMOVED; |
274 | 265 |
275 /// Whether this state is [AssetState.RUNNING]. | 266 /// Whether this state is [AssetState.DIRTY]. |
276 bool get isDirty => this == AssetState.RUNNING; | 267 bool get isDirty => this == AssetState.DIRTY; |
277 | 268 |
278 final String name; | 269 final String name; |
279 | 270 |
280 const AssetState._(this.name); | 271 const AssetState._(this.name); |
281 | 272 |
282 String toString() => name; | 273 String toString() => name; |
283 } | 274 } |
OLD | NEW |