Index: mojo/public/dart/third_party/barback/lib/src/graph/transformer_classifier.dart |
diff --git a/mojo/public/dart/third_party/barback/lib/src/graph/transformer_classifier.dart b/mojo/public/dart/third_party/barback/lib/src/graph/transformer_classifier.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..a095e8d0411724938abd471dbf4f89aa6fd6990f |
--- /dev/null |
+++ b/mojo/public/dart/third_party/barback/lib/src/graph/transformer_classifier.dart |
@@ -0,0 +1,147 @@ |
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+library barback.graph.transformer_classifier; |
+ |
+import 'dart:async'; |
+ |
+import '../asset/asset_forwarder.dart'; |
+import '../asset/asset_node.dart'; |
+import '../errors.dart'; |
+import '../log.dart'; |
+import '../transformer/aggregate_transformer.dart'; |
+import '../transformer/wrapping_aggregate_transformer.dart'; |
+import '../utils.dart'; |
+import 'node_status.dart'; |
+import 'node_streams.dart'; |
+import 'phase.dart'; |
+import 'transform_node.dart'; |
+ |
+/// A class for classifying the primary inputs for a transformer according to |
+/// its [AggregateTransformer.classifyPrimary] method. |
+/// |
+/// This is also used for non-aggregate transformers; they're modeled as |
+/// aggregate transformers that return the primary path if `isPrimary` is true |
+/// and `null` if `isPrimary` is `null`. |
+class TransformerClassifier { |
+ /// The containing [Phase]. |
+ final Phase phase; |
+ |
+ /// The [AggregateTransformer] used to classify the inputs. |
+ final AggregateTransformer transformer; |
+ |
+ /// A string describing the location of [this] in the transformer graph. |
+ final String _location; |
+ |
+ /// The individual transforms for each classiciation key. |
+ final _transforms = new Map<Object, TransformNode>(); |
+ |
+ /// Forwarders used to pass through assets that aren't used by [transformer]. |
+ final _passThroughForwarders = new Set<AssetForwarder>(); |
+ |
+ /// The streams exposed by this classifier. |
+ final _streams = new NodeStreams(); |
+ Stream get onStatusChange => _streams.onStatusChange; |
+ Stream<AssetNode> get onAsset => _streams.onAsset; |
+ Stream<LogEntry> get onLog => _streams.onLog; |
+ |
+ /// A broadcast stream that emits an event whenever [this] has finished |
+ /// classifying all available inputs. |
+ Stream get onDoneClassifying => _onDoneClassifyingController.stream; |
+ final _onDoneClassifyingController = |
+ new StreamController.broadcast(sync: true); |
+ |
+ /// The number of currently-active calls to [transformer.classifyPrimary]. |
+ /// |
+ /// This is used to determine whether [this] is dirty. |
+ var _activeClassifications = 0; |
+ |
+ /// Whether this is currently classifying any inputs. |
+ bool get isClassifying => _activeClassifications > 0; |
+ |
+ /// How far along [this] is in processing its assets. |
+ NodeStatus get status { |
+ if (isClassifying) return NodeStatus.RUNNING; |
+ return NodeStatus.dirtiest( |
+ _transforms.values.map((transform) => transform.status)); |
+ } |
+ |
+ TransformerClassifier(this.phase, transformer, this._location) |
+ : transformer = transformer is AggregateTransformer ? |
+ transformer : new WrappingAggregateTransformer(transformer); |
+ |
+ /// Adds a new asset as an input for this transformer. |
+ void addInput(AssetNode input) { |
+ _activeClassifications++; |
+ syncFuture(() => transformer.classifyPrimary(input.id)).catchError( |
+ (error, stackTrace) { |
+ if (input.state.isRemoved) return null; |
+ |
+ // Catch all transformer errors and pipe them to the results stream. This |
+ // is so a broken transformer doesn't take down the whole graph. |
+ var info = new TransformInfo(transformer, input.id); |
+ if (error is! AssetNotFoundException) { |
+ error = new TransformerException(info, error, stackTrace); |
+ } else { |
+ error = new MissingInputException(info, error.id); |
+ } |
+ phase.cascade.reportError(error); |
+ |
+ return null; |
+ }).then((key) { |
+ if (input.state.isRemoved) return; |
+ if (key == null) { |
+ var forwarder = new AssetForwarder(input); |
+ _passThroughForwarders.add(forwarder); |
+ forwarder.node.whenRemoved( |
+ () => _passThroughForwarders.remove(forwarder)); |
+ _streams.onAssetController.add(forwarder.node); |
+ } else if (_transforms.containsKey(key)) { |
+ _transforms[key].addPrimary(input); |
+ } else { |
+ var transform = new TransformNode(this, transformer, key, _location); |
+ _transforms[key] = transform; |
+ |
+ transform.onStatusChange.listen( |
+ (_) => _streams.changeStatus(status), |
+ onDone: () { |
+ _transforms.remove(transform.key); |
+ if (!_streams.isClosed) _streams.changeStatus(status); |
+ }); |
+ |
+ _streams.onAssetPool.add(transform.onAsset); |
+ _streams.onLogPool.add(transform.onLog); |
+ transform.addPrimary(input); |
+ } |
+ }).whenComplete(() { |
+ _activeClassifications--; |
+ if (_streams.isClosed) return; |
+ if (!isClassifying) _onDoneClassifyingController.add(null); |
+ _streams.changeStatus(status); |
+ }); |
+ } |
+ |
+ /// Removes this transformer. |
+ /// |
+ /// This marks all outputs of the transformer as removed. |
+ void remove() { |
+ _streams.close(); |
+ _onDoneClassifyingController.close(); |
+ for (var transform in _transforms.values.toList()) { |
+ transform.remove(); |
+ } |
+ for (var forwarder in _passThroughForwarders.toList()) { |
+ forwarder.close(); |
+ } |
+ } |
+ |
+ /// Force all deferred transforms to begin producing concrete assets. |
+ void forceAllTransforms() { |
+ for (var transform in _transforms.values) { |
+ transform.force(); |
+ } |
+ } |
+ |
+ String toString() => "classifier in $_location for $transformer"; |
+} |