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

Side by Side Diff: pkg/barback/lib/src/asset_node.dart

Issue 261823008: Reorganize barback's source files. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: re-add barback/lib/src/internal_asset.dart Created 6 years, 7 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 | « pkg/barback/lib/src/asset_id.dart ('k') | pkg/barback/lib/src/asset_node_set.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 library barback.asset_node;
6
7 import 'dart:async';
8
9 import 'asset.dart';
10 import 'asset_id.dart';
11 import 'errors.dart';
12 import 'transform_node.dart';
13 import 'utils.dart';
14
15 /// Describes the current state of an asset as part of a transformation graph.
16 ///
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.
19 ///
20 /// Asset nodes are controlled using [AssetNodeController]s.
21 class AssetNode {
22 /// The id of the asset that this node represents.
23 final AssetId id;
24
25 /// The [AssetNode] from which [this] is forwarded.
26 ///
27 /// For nodes that aren't forwarded, this will return [this]. Otherwise, it
28 /// will return the first node in the forwarding chain.
29 ///
30 /// This is used to determine whether two nodes are forwarded from the same
31 /// source.
32 AssetNode get origin => _origin == null ? this : _origin;
33 AssetNode _origin;
34
35 /// The transform that created this asset node.
36 ///
37 /// This is `null` for source assets. It can change if the upstream transform
38 /// that created this asset changes; this change will *not* cause an
39 /// [onStateChange] event.
40 TransformNode get transform => _transform;
41 TransformNode _transform;
42
43 /// The current state of the asset node.
44 AssetState get state => _state;
45 AssetState _state;
46
47 /// The concrete asset that this node represents.
48 ///
49 /// This is null unless [state] is [AssetState.AVAILABLE].
50 Asset get asset => _asset;
51 Asset _asset;
52
53 /// The callback to be called to notify this asset node's creator that the
54 /// concrete asset should be generated.
55 ///
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
58 /// lazy.
59 Function _lazyCallback;
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.
67 ///
68 /// 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
70 /// any [Barback.getAssetById] calls emit newly-incorrect values.
71 Stream<AssetState> get onStateChange => _stateChangeController.stream;
72
73 /// 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
75 /// requested.
76 final _stateChangeController =
77 new StreamController<AssetState>.broadcast(sync: true);
78
79 /// Calls [callback] when the node's asset is available.
80 ///
81 /// If the asset is currently available, this calls [callback] synchronously
82 /// to ensure that the asset is still available.
83 ///
84 /// The return value of [callback] is piped to the returned Future. If the
85 /// asset is removed before becoming available, the returned future will throw
86 /// an [AssetNotFoundException].
87 Future whenAvailable(callback(Asset asset)) {
88 return _waitForState((state) => state.isAvailable || state.isRemoved,
89 (state) {
90 if (state.isRemoved) throw new AssetNotFoundException(id);
91 return callback(asset);
92 });
93 }
94
95 /// Calls [callback] when the node's asset is removed.
96 ///
97 /// If the asset is already removed when this is called, it calls [callback]
98 /// synchronously.
99 ///
100 /// The return value of [callback] is piped to the returned Future.
101 Future whenRemoved(callback()) =>
102 _waitForState((state) => state.isRemoved, (_) => callback());
103
104 /// Returns a [Future] that completes when [state] changes from its current
105 /// value to any other value.
106 ///
107 /// The returned [Future] will contain the new state.
108 Future<AssetState> whenStateChanges() {
109 var startState = state;
110 return _waitForState((state) => state != startState, (state) => state);
111 }
112
113 /// Calls [callback] as soon as the node is in a state that matches [test].
114 ///
115 /// [callback] is called synchronously if this is already in such a state.
116 ///
117 /// The return value of [callback] is piped to the returned Future.
118 Future _waitForState(bool test(AssetState state),
119 callback(AssetState state)) {
120 if (test(state)) return syncFuture(() => callback(state));
121 return onStateChange.firstWhere(test).then((_) => callback(state));
122 }
123
124 AssetNode._(this.id, this._transform, this._origin)
125 : _state = AssetState.RUNNING;
126
127 AssetNode._available(Asset asset, this._transform, this._origin)
128 : id = asset.id,
129 _asset = asset,
130 _state = AssetState.AVAILABLE;
131
132 AssetNode._lazy(this.id, this._transform, this._origin, this._lazyCallback)
133 : _state = AssetState.RUNNING;
134
135 /// If [this] is lazy, force it to generate a concrete asset; otherwise, do
136 /// nothing.
137 ///
138 /// See [AssetNodeController.lazy].
139 void force() {
140 if (_origin != null) {
141 _origin.force();
142 } else if (_lazyCallback != null) {
143 _lazyCallback();
144 _lazyCallback = null;
145 }
146 }
147
148 String toString() => "${isLazy ? 'lazy' : state} asset $id";
149 }
150
151 /// The controller for an [AssetNode].
152 ///
153 /// This controls which state the node is in.
154 class AssetNodeController {
155 final AssetNode node;
156
157 /// Creates a controller for a dirty node.
158 AssetNodeController(AssetId id, [TransformNode transform])
159 : node = new AssetNode._(id, transform, null);
160
161 /// Creates a controller for an available node with the given concrete
162 /// [asset].
163 AssetNodeController.available(Asset asset, [TransformNode transform])
164 : node = new AssetNode._available(asset, transform, null);
165
166 /// Creates a controller for a lazy node.
167 ///
168 /// For the most part, this node works like any other dirty node. However, the
169 /// owner of its controller isn't expected to do the work to make it available
170 /// as soon as possible like they would for a non-lazy node. Instead, when its
171 /// value is needed, [callback] will fire to indicate that it should be made
172 /// available as soon as possible.
173 ///
174 /// [callback] is guaranteed to only fire once.
175 AssetNodeController.lazy(AssetId id, void callback(),
176 [TransformNode transform])
177 : node = new AssetNode._lazy(id, transform, null, callback);
178
179 /// Creates a controller for a node whose initial state matches the current
180 /// state of [node].
181 ///
182 /// [AssetNode.origin] of the returned node will automatically be set to
183 /// `node.origin`.
184 ///
185 /// If [node] is lazy, the returned node will also be lazy.
186 AssetNodeController.from(AssetNode node)
187 : node = new AssetNode._(node.id, node.transform, node.origin) {
188 if (node.state.isAvailable) {
189 setAvailable(node.asset);
190 } else if (node.state.isRemoved) {
191 setRemoved();
192 }
193 }
194
195 /// Marks the node as [AssetState.RUNNING].
196 void setDirty() {
197 assert(node._state != AssetState.REMOVED);
198 node._asset = null;
199 node._lazyCallback = null;
200
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 }
208
209 /// Marks the node as [AssetState.REMOVED].
210 ///
211 /// 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.
213 void setRemoved() {
214 assert(node._state != AssetState.REMOVED);
215 node._state = AssetState.REMOVED;
216 node._asset = null;
217 node._lazyCallback = null;
218 node._stateChangeController.add(AssetState.REMOVED);
219 }
220
221 /// Marks the node as [AssetState.AVAILABLE] with the given concrete [asset].
222 ///
223 /// It's an error to mark an already-available node as available. It should be
224 /// marked as dirty first.
225 void setAvailable(Asset asset) {
226 assert(asset.id == node.id);
227 assert(node._state != AssetState.REMOVED);
228 assert(node._state != AssetState.AVAILABLE);
229 node._state = AssetState.AVAILABLE;
230 node._asset = asset;
231 node._lazyCallback = null;
232 node._stateChangeController.add(AssetState.AVAILABLE);
233 }
234
235 /// Marks the node as [AssetState.RUNNING] and lazy.
236 ///
237 /// Lazy nodes aren't expected to have their values generated until needed.
238 /// Once it's necessary, [callback] will be called. [callback] is guaranteed
239 /// to be called only once.
240 ///
241 /// See also [AssetNodeController.lazy].
242 void setLazy(void callback()) {
243 assert(node._state != AssetState.REMOVED);
244 node._state = AssetState.RUNNING;
245 node._asset = null;
246 node._lazyCallback = callback;
247 node._stateChangeController.add(AssetState.RUNNING);
248 }
249
250 String toString() => "controller for $node";
251 }
252
253 // TODO(nweiz): add an error state.
254 /// An enum of states that an [AssetNode] can be in.
255 class AssetState {
256 /// 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
258 /// again from the [AssetState.RUNNING] state.
259 static final AVAILABLE = const AssetState._("available");
260
261 /// The asset is no longer available, possibly for good. A removed asset will
262 /// never enter another state.
263 static final REMOVED = const AssetState._("removed");
264
265 /// The asset will exist in the future (unless it's removed), but the concrete
266 /// asset is not yet available.
267 static final RUNNING = const AssetState._("dirty");
268
269 /// Whether this state is [AssetState.AVAILABLE].
270 bool get isAvailable => this == AssetState.AVAILABLE;
271
272 /// Whether this state is [AssetState.REMOVED].
273 bool get isRemoved => this == AssetState.REMOVED;
274
275 /// Whether this state is [AssetState.RUNNING].
276 bool get isDirty => this == AssetState.RUNNING;
277
278 final String name;
279
280 const AssetState._(this.name);
281
282 String toString() => name;
283 }
OLDNEW
« no previous file with comments | « pkg/barback/lib/src/asset_id.dart ('k') | pkg/barback/lib/src/asset_node_set.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698