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

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

Issue 196273003: Move isPrimary computation from PhaseInput into TransformNode. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 9 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) 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.transform_node; 5 library barback.transform_node;
6 6
7 import 'dart:async'; 7 import 'dart:async';
8 8
9 import 'asset.dart'; 9 import 'asset.dart';
10 import 'asset_id.dart'; 10 import 'asset_id.dart';
11 import 'asset_node.dart'; 11 import 'asset_node.dart';
12 import 'asset_set.dart';
13 import 'declaring_transform.dart'; 12 import 'declaring_transform.dart';
14 import 'errors.dart'; 13 import 'errors.dart';
15 import 'lazy_transformer.dart'; 14 import 'lazy_transformer.dart';
16 import 'log.dart'; 15 import 'log.dart';
17 import 'phase.dart'; 16 import 'phase.dart';
18 import 'stream_pool.dart'; 17 import 'stream_pool.dart';
19 import 'transform.dart'; 18 import 'transform.dart';
20 import 'transformer.dart'; 19 import 'transformer.dart';
21 import 'utils.dart'; 20 import 'utils.dart';
22 21
(...skipping 11 matching lines...) Expand all
34 33
35 /// The node for the primary asset this transform depends on. 34 /// The node for the primary asset this transform depends on.
36 final AssetNode primary; 35 final AssetNode primary;
37 36
38 /// A string describing the location of [this] in the transformer graph. 37 /// A string describing the location of [this] in the transformer graph.
39 final String _location; 38 final String _location;
40 39
41 /// The subscription to [primary]'s [AssetNode.onStateChange] stream. 40 /// The subscription to [primary]'s [AssetNode.onStateChange] stream.
42 StreamSubscription _primarySubscription; 41 StreamSubscription _primarySubscription;
43 42
44 // TODO(nweiz): Remove this and move isPrimary computation into TransformNode.
45 /// Whether the parent [PhaseInput] is currently computing whether its input
46 /// is primary for [this].
47 bool _pendingIsPrimary = false;
48
49 /// Whether [this] is dirty and still has more processing to do. 43 /// Whether [this] is dirty and still has more processing to do.
50 bool get isDirty => _pendingIsPrimary || _isApplying; 44 bool get isDirty => !_state.isDone;
51
52 /// Whether any input has become dirty since [_apply] last started running.
53 var _hasBecomeDirty = false;
54
55 /// Whether [_apply] is currently running.
56 var _isApplying = false;
57
58 /// Whether the most recent run of this transform has declared that it
59 /// consumes the primary input.
60 ///
61 /// Defaults to `false`.
62 bool get consumePrimary => _consumePrimary;
63 bool _consumePrimary = false;
64 45
65 /// Whether [transformer] is lazy and this transform has yet to be forced. 46 /// Whether [transformer] is lazy and this transform has yet to be forced.
66 bool _isLazy; 47 bool _isLazy;
67 48
68 /// The subscriptions to each input's [AssetNode.onStateChange] stream. 49 /// The subscriptions to each input's [AssetNode.onStateChange] stream.
69 var _inputSubscriptions = new Map<AssetId, StreamSubscription>(); 50 var _inputSubscriptions = new Map<AssetId, StreamSubscription>();
70 51
71 /// The controllers for the asset nodes emitted by this node. 52 /// The controllers for the asset nodes emitted by this node.
72 var _outputControllers = new Map<AssetId, AssetNodeController>(); 53 var _outputControllers = new Map<AssetId, AssetNodeController>();
73 54
74 // TODO(nweiz): It's weird that this is different than the [onDone] stream the 55 /// The controller that's used to pass [primary] through [this] if it's not
75 // other nodes emit. See if we can make that more consistent. 56 /// consumed or overwritten.
76 /// A stream that emits an event whenever [onDirty] changes its value. 57 ///
58 /// This needs an intervening controller to ensure that the output can be
59 /// marked dirty when determining whether [this] will consume or overwrite it,
60 /// and be marked removed if it does. [_passThroughController] will be null
61 /// null if the asset is not being passed through.
Bob Nystrom 2014/03/12 17:42:58 "null null" -> "null"
nweiz 2014/03/12 21:44:47 Done.
62 AssetNodeController _passThroughController;
63
64 /// A stream that emits an event whenever [this] is no longer dirty.
77 /// 65 ///
78 /// This is synchronous in order to guarantee that it will emit an event as 66 /// This is synchronous in order to guarantee that it will emit an event as
79 /// soon as [isDirty] changes. It's possible for this to emit multiple events 67 /// soon as [isDirty] flips from `true` to `false`.
80 /// while [isDirty] is `true`. However, it will only emit a single event each 68 Stream get onDone => _onDoneController.stream;
81 /// time [isDirty] becomes `false`. 69 final _onDoneController = new StreamController.broadcast(sync: true);
82 Stream get onStateChange => _onStateChangeController.stream;
83 final _onStateChangeController = new StreamController.broadcast(sync: true);
84 70
85 /// A stream that emits any new assets emitted by [this]. 71 /// A stream that emits any new assets emitted by [this].
86 /// 72 ///
87 /// Assets are emitted synchronously to ensure that any changes are thoroughly 73 /// Assets are emitted synchronously to ensure that any changes are thoroughly
88 /// propagated as soon as they occur. 74 /// propagated as soon as they occur.
89 Stream<AssetNode> get onAsset => _onAssetController.stream; 75 Stream<AssetNode> get onAsset => _onAssetController.stream;
90 final _onAssetController = new StreamController<AssetNode>(sync: true); 76 final _onAssetController = new StreamController<AssetNode>(sync: true);
91 77
92 /// A stream that emits an event whenever this transform logs an entry. 78 /// A stream that emits an event whenever this transform logs an entry.
93 /// 79 ///
94 /// This is synchronous because error logs can cause the transform to fail, so 80 /// This is synchronous because error logs can cause the transform to fail, so
95 /// we need to ensure that their processing isn't delayed until after the 81 /// we need to ensure that their processing isn't delayed until after the
96 /// transform or build has finished. 82 /// transform or build has finished.
97 Stream<LogEntry> get onLog => _onLogPool.stream; 83 Stream<LogEntry> get onLog => _onLogPool.stream;
98 final _onLogPool = new StreamPool<LogEntry>.broadcast(); 84 final _onLogPool = new StreamPool<LogEntry>.broadcast();
99 85
86 /// The current state of [this].
87 var _state = _TransformNodeState.PROCESSING;
88
89 /// Whether [this] has been marked as removed.
90 bool get _isRemoved => _onAssetController.isClosed;
91
92 /// Whether the most recent run of this transform has declared that it
93 /// consumes the primary input.
94 ///
95 /// Defaults to `false`. This is not meaningful unless [_state] is
96 /// [_TransformNodeState.APPLIED].
97 bool _consumePrimary = false;
98
100 TransformNode(this.phase, Transformer transformer, this.primary, 99 TransformNode(this.phase, Transformer transformer, this.primary,
101 this._location) 100 this._location)
102 : transformer = transformer, 101 : transformer = transformer,
103 _isLazy = transformer is LazyTransformer { 102 _isLazy = transformer is LazyTransformer {
104 _primarySubscription = primary.onStateChange.listen((state) { 103 _primarySubscription = primary.onStateChange.listen((state) {
105 if (state.isRemoved) { 104 if (state.isRemoved) {
106 remove(); 105 remove();
107 } else { 106 } else {
108 if (state.isDirty) _pendingIsPrimary = true; 107 _dirty(primaryChanged: true);
109 _dirty();
110 } 108 }
111 }); 109 });
112 110
113 _apply(); 111 _process();
114 } 112 }
115 113
116 /// The [TransformInfo] describing this node. 114 /// The [TransformInfo] describing this node.
117 /// 115 ///
118 /// [TransformInfo] is the publicly-visible representation of a transform 116 /// [TransformInfo] is the publicly-visible representation of a transform
119 /// node. 117 /// node.
120 TransformInfo get info => new TransformInfo(transformer, primary.id); 118 TransformInfo get info => new TransformInfo(transformer, primary.id);
121 119
122 /// Marks this transform as removed. 120 /// Marks this transform as removed.
123 /// 121 ///
124 /// This causes all of the transform's outputs to be marked as removed as 122 /// This causes all of the transform's outputs to be marked as removed as
125 /// well. Normally this will be automatically done internally based on events 123 /// well. Normally this will be automatically done internally based on events
126 /// from the primary input, but it's possible for a transform to no longer be 124 /// from the primary input, but it's possible for a transform to no longer be
127 /// valid even if its primary input still exists. 125 /// valid even if its primary input still exists.
128 void remove() { 126 void remove() {
129 _hasBecomeDirty = false;
130 _onAssetController.close(); 127 _onAssetController.close();
131 _onStateChangeController.close(); 128 _onDoneController.close();
132 _primarySubscription.cancel(); 129 _primarySubscription.cancel();
133 for (var subscription in _inputSubscriptions.values) { 130 _clearInputSubscriptions();
134 subscription.cancel();
135 }
136 for (var controller in _outputControllers.values) { 131 for (var controller in _outputControllers.values) {
137 controller.setRemoved(); 132 controller.setRemoved();
138 } 133 }
134 if (_passThroughController != null) {
135 _passThroughController.setRemoved();
136 _passThroughController = null;
137 }
139 } 138 }
140 139
141 /// If [transformer] is lazy, ensures that its concrete outputs will be 140 /// If [transformer] is lazy, ensures that its concrete outputs will be
142 /// generated. 141 /// generated.
143 void force() { 142 void force() {
144 // TODO(nweiz): we might want to have a timeout after which, if the 143 // TODO(nweiz): we might want to have a timeout after which, if the
145 // transform's outputs have gone unused, we switch it back to lazy mode. 144 // transform's outputs have gone unused, we switch it back to lazy mode.
146 if (!_isLazy) return; 145 if (!_isLazy) return;
147 _isLazy = false; 146 _isLazy = false;
148 _dirty(); 147 _dirty(primaryChanged: false);
149 }
150
151 // TODO(nweiz): remove this and move isPrimary computation into TransformNode.
152 /// Mark that the parent [PhaseInput] has determined that its input is indeed
153 /// primary for [this].
154 void markPrimary() {
155 if (!_pendingIsPrimary) return;
156 _pendingIsPrimary = false;
157 if (!_isApplying) _apply();
158 } 148 }
159 149
160 /// Marks this transform as dirty. 150 /// Marks this transform as dirty.
161 /// 151 ///
162 /// This causes all of the transform's outputs to be marked as dirty as well. 152 /// This causes all of the transform's outputs to be marked as dirty as well.
163 void _dirty() { 153 /// [primaryChanged] should be true if and only if [this] was set dirty
154 /// because [primary] changed.
155 void _dirty({bool primaryChanged: false}) {
156 if (!primaryChanged && _state.isNotPrimary) return;
157
158 if (_passThroughController != null) _passThroughController.setDirty();
164 for (var controller in _outputControllers.values) { 159 for (var controller in _outputControllers.values) {
165 controller.setDirty(); 160 controller.setDirty();
166 } 161 }
167 162
168 _hasBecomeDirty = true; 163 if (_state.isDone) {
169 _onStateChangeController.add(null); 164 if (primaryChanged) {
170 if (!_isApplying && !_pendingIsPrimary) _apply(); 165 _process();
166 } else {
167 _apply();
168 }
169 } else if (primaryChanged) {
170 _state = _TransformNodeState.NEEDS_IS_PRIMARY;
171 } else if (!_state.needsIsPrimary) {
172 _state = _TransformNodeState.NEEDS_APPLY;
173 }
Bob Nystrom 2014/03/12 17:42:58 It might be worth moving this conditional logic in
nweiz 2014/03/12 21:44:47 I tried this out, but I ended up finding it more c
174 }
175
176 /// Determines whether [primary] is primary for [transformer], and if so runs
177 /// [transformer.apply].
178 void _process() {
179 // Clear all the old input subscriptions. If an input is re-used, we'll
180 // re-subscribe.
181 _clearInputSubscriptions();
182 _state = _TransformNodeState.PROCESSING;
183 primary.whenAvailable((_) {
184 _state = _TransformNodeState.PROCESSING;
185 return transformer.isPrimary(primary.asset);
186 }).catchError((error, stackTrace) {
187 // If the transform became dirty while processing, ignore any errors from
188 // it.
189 if (_state.needsIsPrimary || _isRemoved) return false;
190
191 if (error is! MissingInputException) {
192 error = new TransformerException(info, error, stackTrace);
193 }
194
195 // Catch all transformer errors and pipe them to the results stream. This
196 // is so a broken transformer doesn't take down the whole graph.
197 phase.cascade.reportError(error);
198
199 return false;
200 }).then((isPrimary) {
201 if (_isRemoved) return;
202 if (_state.needsIsPrimary) {
203 _process();
204 } else if (isPrimary) {
205 _apply();
206 } else {
207 _doesNotApply();
208 _state = _TransformNodeState.NOT_PRIMARY;
209 _onDoneController.add(null);
210 }
Bob Nystrom 2014/03/12 17:42:58 This might be another chunk of conditional logic y
211 });
171 } 212 }
172 213
173 /// Applies this transform. 214 /// Applies this transform.
174 void _apply() { 215 void _apply() {
175 assert(!_onAssetController.isClosed); 216 assert(!_onAssetController.isClosed);
176 217
177 // Clear all the old input subscriptions. If an input is re-used, we'll 218 // Clear input subscriptions here as well as in [_process] because [_apply]
178 // re-subscribe. 219 // may be restarted independently if only a secondary input changes.
179 for (var subscription in _inputSubscriptions.values) { 220 _clearInputSubscriptions();
180 subscription.cancel(); 221 _state = _TransformNodeState.PROCESSING;
181 }
182 _inputSubscriptions.clear();
183
184 _isApplying = true;
185 _onStateChangeController.add(null);
186 primary.whenAvailable((_) { 222 primary.whenAvailable((_) {
187 _hasBecomeDirty = false; 223 if (_state.needsApply) _state = _TransformNodeState.PROCESSING;
188
189 // TODO(nweiz): If [transformer] is a [DeclaringTransformer] but not a 224 // TODO(nweiz): If [transformer] is a [DeclaringTransformer] but not a
190 // [LazyTransformer], we can get some mileage out of doing a declarative 225 // [LazyTransformer], we can get some mileage out of doing a declarative
191 // first so we know how to hook up the assets. 226 // first so we know how to hook up the assets.
192 if (_isLazy) return _declareLazy(); 227 if (_isLazy) return _declareLazy();
193 return _applyImmediate(); 228 return _applyImmediate();
194 }).catchError((error, stackTrace) { 229 }).catchError((error, stackTrace) {
195 // If the transform became dirty while processing, ignore any errors from 230 // If the transform became dirty while processing, ignore any errors from
196 // it. 231 // it.
197 if (_hasBecomeDirty || _onAssetController.isClosed) return; 232 if (!_state.isProcessing || _isRemoved) return;
198 233
199 if (error is! MissingInputException) { 234 if (error is! MissingInputException) {
200 error = new TransformerException(info, error, stackTrace); 235 error = new TransformerException(info, error, stackTrace);
201 } 236 }
202 237
203 // Catch all transformer errors and pipe them to the results stream. This 238 // Catch all transformer errors and pipe them to the results stream. This
204 // is so a broken transformer doesn't take down the whole graph. 239 // is so a broken transformer doesn't take down the whole graph.
205 phase.cascade.reportError(error); 240 phase.cascade.reportError(error);
206 241
207 // Remove all the previously-emitted assets. 242 _doesNotApply();
208 for (var controller in _outputControllers.values) {
209 controller.setRemoved();
210 }
211 _outputControllers.clear();
212 }).then((_) { 243 }).then((_) {
213 if (_onAssetController.isClosed) return; 244 if (_isRemoved) return;
214 245
215 _isApplying = false; 246 if (_state.needsIsPrimary) {
216 if (_hasBecomeDirty) { 247 _process();
217 // Re-apply the transform if it became dirty while applying. 248 } else if (_state.needsApply) {
218 if (!_pendingIsPrimary) _apply(); 249 _apply();
219 } else { 250 } else {
220 assert(!isDirty); 251 assert(_state.isProcessing);
221 // Otherwise, notify the parent nodes that it's no longer dirty. 252 _state = _TransformNodeState.APPLIED;
222 _onStateChangeController.add(null); 253 _onDoneController.add(null);
223 } 254 }
Bob Nystrom 2014/03/12 17:42:58 Ditto.
224 }); 255 });
225 } 256 }
226 257
227 /// Gets the asset for an input [id]. 258 /// Gets the asset for an input [id].
228 /// 259 ///
229 /// If an input with that ID cannot be found, throws an 260 /// If an input with that ID cannot be found, throws an
230 /// [AssetNotFoundException]. 261 /// [AssetNotFoundException].
231 Future<Asset> getInput(AssetId id) { 262 Future<Asset> getInput(AssetId id) {
232 return phase.getInput(id).then((node) { 263 return phase.getInput(id).then((node) {
233 // Throw if the input isn't found. This ensures the transformer's apply 264 // Throw if the input isn't found. This ensures the transformer's apply
234 // is exited. We'll then catch this and report it through the proper 265 // is exited. We'll then catch this and report it through the proper
235 // results stream. 266 // results stream.
236 if (node == null) throw new MissingInputException(info, id); 267 if (node == null) throw new MissingInputException(info, id);
237 268
238 _inputSubscriptions.putIfAbsent(node.id, 269 _inputSubscriptions.putIfAbsent(node.id, () {
239 () => node.onStateChange.listen((_) => _dirty())); 270 return node.onStateChange.listen((_) => _dirty(primaryChanged: false));
271 });
240 272
241 return node.asset; 273 return node.asset;
242 }); 274 });
243 } 275 }
244 276
245 /// Applies the transform so that it produces concrete (as opposed to lazy) 277 /// Applies the transform so that it produces concrete (as opposed to lazy)
246 /// outputs. 278 /// outputs.
247 Future _applyImmediate() { 279 Future _applyImmediate() {
248 var transformController = new TransformController(this); 280 var transformController = new TransformController(this);
249 _onLogPool.add(transformController.onLog); 281 _onLogPool.add(transformController.onLog);
250 282
251 return syncFuture(() { 283 return syncFuture(() {
252 return transformer.apply(transformController.transform); 284 return transformer.apply(transformController.transform);
253 }).then((_) { 285 }).then((_) {
254 if (_hasBecomeDirty || _onAssetController.isClosed) return; 286 if (!_state.isProcessing || _onAssetController.isClosed) return;
255 287
256 _consumePrimary = transformController.consumePrimary; 288 _consumePrimary = transformController.consumePrimary;
257 289
258 var newOutputs = transformController.outputs; 290 var newOutputs = transformController.outputs;
259 // Any ids that are for a different package are invalid. 291 // Any ids that are for a different package are invalid.
260 var invalidIds = newOutputs 292 var invalidIds = newOutputs
261 .map((asset) => asset.id) 293 .map((asset) => asset.id)
262 .where((id) => id.package != phase.cascade.package) 294 .where((id) => id.package != phase.cascade.package)
263 .toSet(); 295 .toSet();
264 for (var id in invalidIds) { 296 for (var id in invalidIds) {
265 newOutputs.removeId(id); 297 newOutputs.removeId(id);
266 // TODO(nweiz): report this as a warning rather than a failing error. 298 // TODO(nweiz): report this as a warning rather than a failing error.
267 phase.cascade.reportError(new InvalidOutputException(info, id)); 299 phase.cascade.reportError(new InvalidOutputException(info, id));
268 } 300 }
269 301
270 // Remove outputs that used to exist but don't anymore. 302 // Remove outputs that used to exist but don't anymore.
271 for (var id in _outputControllers.keys.toList()) { 303 for (var id in _outputControllers.keys.toList()) {
272 if (newOutputs.containsId(id)) continue; 304 if (newOutputs.containsId(id)) continue;
273 _outputControllers.remove(id).setRemoved(); 305 _outputControllers.remove(id).setRemoved();
274 } 306 }
275 307
308 // Emit or stop emitting the pass-through asset between removing and
309 // adding outputs to ensure there are no collisions.
310 if (!newOutputs.containsId(primary.id)) {
311 _emitPassThrough();
312 } else {
313 _dontEmitPassThrough();
314 }
315
276 // Store any new outputs or new contents for existing outputs. 316 // Store any new outputs or new contents for existing outputs.
277 for (var asset in newOutputs) { 317 for (var asset in newOutputs) {
278 var controller = _outputControllers[asset.id]; 318 var controller = _outputControllers[asset.id];
279 if (controller != null) { 319 if (controller != null) {
280 controller.setAvailable(asset); 320 controller.setAvailable(asset);
281 } else { 321 } else {
282 var controller = new AssetNodeController.available(asset, this); 322 var controller = new AssetNodeController.available(asset, this);
283 _outputControllers[asset.id] = controller; 323 _outputControllers[asset.id] = controller;
284 _onAssetController.add(controller.node); 324 _onAssetController.add(controller.node);
285 } 325 }
286 } 326 }
287 }); 327 });
288 } 328 }
289 329
290 /// Applies the transform in declarative mode so that it produces lazy 330 /// Applies the transform in declarative mode so that it produces lazy
291 /// outputs. 331 /// outputs.
292 Future _declareLazy() { 332 Future _declareLazy() {
293 var transformController = new DeclaringTransformController(this); 333 var transformController = new DeclaringTransformController(this);
294 334
295 return syncFuture(() { 335 return syncFuture(() {
296 return (transformer as LazyTransformer) 336 return (transformer as LazyTransformer)
297 .declareOutputs(transformController.transform); 337 .declareOutputs(transformController.transform);
298 }).then((_) { 338 }).then((_) {
299 if (_hasBecomeDirty || _onAssetController.isClosed) return; 339 if (!_state.isProcessing || _onAssetController.isClosed) return;
300 340
301 _consumePrimary = transformController.consumePrimary; 341 _consumePrimary = transformController.consumePrimary;
302 342
303 var newIds = transformController.outputIds; 343 var newIds = transformController.outputIds;
304 var invalidIds = 344 var invalidIds =
305 newIds.where((id) => id.package != phase.cascade.package).toSet(); 345 newIds.where((id) => id.package != phase.cascade.package).toSet();
306 for (var id in invalidIds) { 346 for (var id in invalidIds) {
307 newIds.remove(id); 347 newIds.remove(id);
308 // TODO(nweiz): report this as a warning rather than a failing error. 348 // TODO(nweiz): report this as a warning rather than a failing error.
309 phase.cascade.reportError(new InvalidOutputException(info, id)); 349 phase.cascade.reportError(new InvalidOutputException(info, id));
310 } 350 }
311 351
312 // Remove outputs that used to exist but don't anymore. 352 // Remove outputs that used to exist but don't anymore.
313 for (var id in _outputControllers.keys.toList()) { 353 for (var id in _outputControllers.keys.toList()) {
314 if (newIds.contains(id)) continue; 354 if (newIds.contains(id)) continue;
315 _outputControllers.remove(id).setRemoved(); 355 _outputControllers.remove(id).setRemoved();
316 } 356 }
317 357
358 // Emit or stop emitting the pass-through asset between removing and
359 // adding outputs to ensure there are no collisions.
360 if (!newIds.contains(primary.id)) {
361 _emitPassThrough();
362 } else {
363 _dontEmitPassThrough();
364 }
365
318 for (var id in newIds) { 366 for (var id in newIds) {
319 var controller = _outputControllers[id]; 367 var controller = _outputControllers[id];
320 if (controller != null) { 368 if (controller != null) {
321 controller.setLazy(force); 369 controller.setLazy(force);
322 } else { 370 } else {
323 var controller = new AssetNodeController.lazy(id, force, this); 371 var controller = new AssetNodeController.lazy(id, force, this);
324 _outputControllers[id] = controller; 372 _outputControllers[id] = controller;
325 _onAssetController.add(controller.node); 373 _onAssetController.add(controller.node);
326 } 374 }
327 } 375 }
328 }); 376 });
329 } 377 }
330 378
379 /// Cancels all subscriptions to secondary input nodes.
380 void _clearInputSubscriptions() {
381 for (var subscription in _inputSubscriptions.values) {
382 subscription.cancel();
383 }
384 _inputSubscriptions.clear();
385 }
386
387 /// Marks this transformer as not applying to [primary].
388 ///
389 /// This might be because [primary] isn't primary for [transformer], or
390 /// because [transformer] threw an error during [transformer.apply].
391 void _doesNotApply() {
392 // Remove all the previously-emitted assets.
393 for (var controller in _outputControllers.values) {
394 controller.setRemoved();
395 }
396 _outputControllers.clear();
397 _emitPassThrough();
398 }
399
400 /// Emit the pass-through asset if it's not being emitted already.
401 void _emitPassThrough() {
402 assert(!_outputControllers.containsKey(primary.id));
403
404 if (_consumePrimary) return;
405 if (_passThroughController == null) {
406 _passThroughController = new AssetNodeController.from(primary);
407 _onAssetController.add(_passThroughController.node);
408 } else {
409 _passThroughController.setAvailable(primary.asset);
410 }
411 }
412
413 /// Stop emitting the pass-through asset if it's being emitted already.
414 void _dontEmitPassThrough() {
415 if (_passThroughController == null) return;
416 _passThroughController.setRemoved();
417 _passThroughController = null;
418 }
419
331 String toString() => 420 String toString() =>
332 "transform node in $_location for $transformer on $primary"; 421 "transform node in $_location for $transformer on $primary";
333 } 422 }
423
424 /// The enum of states that [TransformNode] can be in.
425 class _TransformNodeState {
426 /// The transform node is running [Transformer.isPrimary] or
427 /// [Transformer.apply] and doesn't need to re-run them.
428 ///
429 /// If there are no external changes by the time the processing finishes, this
430 /// will transition to [APPLIED] or [NOT_PRIMARY] depending on the result of
431 /// [Transformer.isPrimary]. If the primary input changes, this will
432 /// transition to [NEEDS_IS_PRIMARY]. If a secondary input changes, this will
433 /// transition to [NEEDS_APPLY].
434 static final PROCESSING = const _TransformNodeState._("processing");
435
436 /// The transform is running [Transformer.isPrimary] or [Transformer.apply],
437 /// but since it started the primary input changed, so it will need to re-run
438 /// [Transformer.isPrimary].
439 ///
440 /// This will always transition to [Transformer.PROCESSING].
441 static final NEEDS_IS_PRIMARY =
442 const _TransformNodeState._("needs isPrimary");
443
444 /// The transform is running [Transformer.apply], but since it started a
445 /// secondary input changed, so it will need to re-run [Transformer.apply].
446 ///
447 /// If there are no external changes by the time [Transformer.apply] finishes,
448 /// this will transition to [PROCESSING]. If the primary input changes, this
449 /// will transition to [NEEDS_IS_PRIMARY].
450 static final NEEDS_APPLY = const _TransformNodeState._("needs apply");
451
452 /// The transform has finished running [Transformer.apply], whether or not it
453 /// emitted an error.
454 ///
455 /// If the primary input or a secondary input changes, this will transition to
456 /// [PROCESSING].
457 static final APPLIED = const _TransformNodeState._("applied");
458
459 /// The transform has finished running [Transformer.isPrimary], which returned
460 /// `false`.
461 ///
462 /// If the primary input changes, this will transition to [PROCESSING].
463 static final NOT_PRIMARY = const _TransformNodeState._("not primary");
464
465 /// Whether [this] is [PROCESSING].
466 bool get isProcessing => this == _TransformNodeState.PROCESSING;
467
468 /// Whether [this] is [NEEDS_IS_PRIMARY].
469 bool get needsIsPrimary => this == _TransformNodeState.NEEDS_IS_PRIMARY;
470
471 /// Whether [this] is [NEEDS_APPLY].
472 bool get needsApply => this == _TransformNodeState.NEEDS_APPLY;
473
474 /// Whether [this] is [APPLIED].
475 bool get isApplied => this == _TransformNodeState.APPLIED;
476
477 /// Whether [this] is [IS_NOT_PRIMARY].
Bob Nystrom 2014/03/12 17:42:58 IS_NOT_PRIMARY -> NOT_PRIMARY.
nweiz 2014/03/12 21:44:47 Done.
478 bool get isNotPrimary => this == _TransformNodeState.NOT_PRIMARY;
479
480 /// Whether the transform has finished running [Transformer.isPrimary] and
481 /// [Transformer.apply].
482 ///
483 /// Specifically, whether [this] is [APPLIED] or [IS_NOT_PRIMARY].
484 bool get isDone => isApplied || isNotPrimary;
485
486 final String name;
487
488 const _TransformNodeState._(this.name);
489
490 String toString() => name;
491 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698