| Index: third_party/pkg/barback-0.13.0/lib/src/phase.dart
 | 
| diff --git a/pkg/barback/lib/src/graph/phase.dart b/third_party/pkg/barback-0.13.0/lib/src/phase.dart
 | 
| similarity index 66%
 | 
| copy from pkg/barback/lib/src/graph/phase.dart
 | 
| copy to third_party/pkg/barback-0.13.0/lib/src/phase.dart
 | 
| index a4724ab20e22f0ef3c0c2f4aff84f96842fc272a..aa7dc78ebd01acad469e83de0af134724c6b74ca 100644
 | 
| --- a/pkg/barback/lib/src/graph/phase.dart
 | 
| +++ b/third_party/pkg/barback-0.13.0/lib/src/phase.dart
 | 
| @@ -2,26 +2,24 @@
 | 
|  // 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.phase;
 | 
| +library barback.phase;
 | 
|  
 | 
|  import 'dart:async';
 | 
|  
 | 
| -import '../asset/asset_id.dart';
 | 
| -import '../asset/asset_node.dart';
 | 
| -import '../asset/asset_node_set.dart';
 | 
| -import '../errors.dart';
 | 
| -import '../log.dart';
 | 
| -import '../transformer/transformer.dart';
 | 
| -import '../transformer/transformer_group.dart';
 | 
| -import '../utils.dart';
 | 
| -import '../utils/multiset.dart';
 | 
|  import 'asset_cascade.dart';
 | 
| +import 'asset_id.dart';
 | 
| +import 'asset_node.dart';
 | 
| +import 'errors.dart';
 | 
|  import 'group_runner.dart';
 | 
| -import 'node_status.dart';
 | 
| -import 'node_streams.dart';
 | 
| +import 'log.dart';
 | 
| +import 'multiset.dart';
 | 
|  import 'phase_forwarder.dart';
 | 
| +import 'phase_input.dart';
 | 
|  import 'phase_output.dart';
 | 
| -import 'transformer_classifier.dart';
 | 
| +import 'stream_pool.dart';
 | 
| +import 'transformer.dart';
 | 
| +import 'transformer_group.dart';
 | 
| +import 'utils.dart';
 | 
|  
 | 
|  /// One phase in the ordered series of transformations in an [AssetCascade].
 | 
|  ///
 | 
| @@ -45,6 +43,11 @@ class Phase {
 | 
|    /// The index of [this] in its parent cascade or group.
 | 
|    final int _index;
 | 
|  
 | 
| +  /// The transformers that can access [inputs].
 | 
| +  ///
 | 
| +  /// Their outputs will be available to the next phase.
 | 
| +  final _transformers = new Set<Transformer>();
 | 
| +
 | 
|    /// The groups for this phase.
 | 
|    final _groups = new Map<TransformerGroup, GroupRunner>();
 | 
|  
 | 
| @@ -52,10 +55,7 @@ class Phase {
 | 
|    ///
 | 
|    /// For the first phase, these will be the source assets. For all other
 | 
|    /// phases, they will be the outputs from the previous phase.
 | 
| -  final _inputs = new AssetNodeSet();
 | 
| -
 | 
| -  /// The transformer classifiers for this phase.
 | 
| -  final _classifiers = new Map<Transformer, TransformerClassifier>();
 | 
| +  final _inputs = new Map<AssetId, PhaseInput>();
 | 
|  
 | 
|    /// The forwarders for this phase.
 | 
|    final _forwarders = new Map<AssetId, PhaseForwarder>();
 | 
| @@ -67,45 +67,52 @@ class Phase {
 | 
|    /// phase.
 | 
|    ///
 | 
|    /// This is used to determine which assets have been passed unmodified through
 | 
| -  /// [_classifiers] or [_groups]. It's possible that a given asset was consumed
 | 
| -  /// by a group and not an individual transformer, and so shouldn't be
 | 
| -  /// forwarded through the phase as a whole.
 | 
| +  /// [_inputs] or [_groups]. Each input asset has a PhaseInput in [_inputs]. If
 | 
| +  /// that input isn't consumed by any transformers, it will be forwarded
 | 
| +  /// through the PhaseInput. However, it's possible that it was consumed by a
 | 
| +  /// group, and so shouldn't be forwarded through the phase as a whole.
 | 
|    ///
 | 
|    /// In order to detect whether an output has been forwarded through a group or
 | 
| -  /// a classifier, we must be able to distinguish it from other outputs with
 | 
| +  /// a PhaseInput, we must be able to distinguish it from other outputs with
 | 
|    /// the same id. To do so, we check if its origin is in [_inputOrigins]. If
 | 
|    /// so, it's been forwarded unmodified.
 | 
|    final _inputOrigins = new Multiset<AssetNode>();
 | 
|  
 | 
| -  /// The streams exposed by this phase.
 | 
| -  final _streams = new NodeStreams();
 | 
| -  Stream<NodeStatus> get onStatusChange => _streams.onStatusChange;
 | 
| -  Stream<AssetNode> get onAsset => _streams.onAsset;
 | 
| -  Stream<LogEntry> get onLog => _streams.onLog;
 | 
| -
 | 
| -  /// How far along [this] is in processing its assets.
 | 
| -  NodeStatus get status {
 | 
| -    // Before any transformers are added, the phase should be dirty if and only
 | 
| -    // if any input is dirty.
 | 
| -    if (_classifiers.isEmpty && _groups.isEmpty && previous == null) {
 | 
| -      return _inputs.any((input) => input.state.isDirty) ?
 | 
| -          NodeStatus.RUNNING : NodeStatus.IDLE;
 | 
| -    }
 | 
| +  /// A stream that emits an event whenever [this] is no longer dirty.
 | 
| +  ///
 | 
| +  /// This is synchronous in order to guarantee that it will emit an event as
 | 
| +  /// soon as [isDirty] flips from `true` to `false`.
 | 
| +  Stream get onDone => _onDoneController.stream;
 | 
| +  final _onDoneController = new StreamController.broadcast(sync: true);
 | 
|  
 | 
| -    var classifierStatus = NodeStatus.dirtiest(
 | 
| -        _classifiers.values.map((classifier) => classifier.status));
 | 
| -    var groupStatus = NodeStatus.dirtiest(
 | 
| -        _groups.values.map((group) => group.status));
 | 
| -    return (previous == null ? NodeStatus.IDLE : previous.status)
 | 
| -        .dirtier(classifierStatus)
 | 
| -        .dirtier(groupStatus);
 | 
| -  }
 | 
| +  /// A stream that emits any new assets emitted by [this].
 | 
| +  ///
 | 
| +  /// Assets are emitted synchronously to ensure that any changes are thoroughly
 | 
| +  /// propagated as soon as they occur. Only a phase with no [next] phase will
 | 
| +  /// emit assets.
 | 
| +  Stream<AssetNode> get onAsset => _onAssetController.stream;
 | 
| +  final _onAssetController =
 | 
| +      new StreamController<AssetNode>.broadcast(sync: true);
 | 
| +
 | 
| +  /// Whether [this] is dirty and still has more processing to do.
 | 
| +  ///
 | 
| +  /// A phase is considered dirty if any of the previous phases in the same
 | 
| +  /// cascade are dirty, since those phases could emit an asset that this phase
 | 
| +  /// will then need to process.
 | 
| +  bool get isDirty => (previous != null && previous.isDirty) ||
 | 
| +      _inputs.values.any((input) => input.isDirty) ||
 | 
| +      _groups.values.any((group) => group.isDirty);
 | 
| +
 | 
| +  /// A stream that emits an event whenever any transforms in this phase logs
 | 
| +  /// an entry.
 | 
| +  Stream<LogEntry> get onLog => _onLogPool.stream;
 | 
| +  final _onLogPool = new StreamPool<LogEntry>.broadcast();
 | 
|  
 | 
|    /// The previous phase in the cascade, or null if this is the first phase.
 | 
|    final Phase previous;
 | 
|  
 | 
| -  /// The subscription to [previous]'s [onStatusChange] stream.
 | 
| -  StreamSubscription _previousStatusSubscription;
 | 
| +  /// The subscription to [previous]'s [onDone] stream.
 | 
| +  StreamSubscription _previousOnDoneSubscription;
 | 
|  
 | 
|    /// The subscription to [previous]'s [onAsset] stream.
 | 
|    StreamSubscription<AssetNode> _previousOnAssetSubscription;
 | 
| @@ -134,16 +141,15 @@ class Phase {
 | 
|    Phase._(this.cascade, this._location, this._index, [this.previous]) {
 | 
|      if (previous != null) {
 | 
|        _previousOnAssetSubscription = previous.onAsset.listen(addInput);
 | 
| -      _previousStatusSubscription = previous.onStatusChange
 | 
| -          .listen((_) => _streams.changeStatus(status));
 | 
| +      _previousOnDoneSubscription = previous.onDone.listen((_) {
 | 
| +        if (!isDirty) _onDoneController.add(null);
 | 
| +      });
 | 
|      }
 | 
|  
 | 
| -    onStatusChange.listen((status) {
 | 
| -      if (status == NodeStatus.RUNNING) return;
 | 
| -
 | 
| -      // All the previous phases have finished declaring or producing their
 | 
| -      // outputs. If anyone's still waiting for outputs, cut off the wait; we
 | 
| -      // won't be generating them, at least until a source asset changes.
 | 
| +    onDone.listen((_) {
 | 
| +      // All the previous phases have finished building. If anyone's still
 | 
| +      // waiting for outputs, cut off the wait; we won't be generating them,
 | 
| +      // at least until a source asset changes.
 | 
|        for (var completer in _pendingOutputRequests.values) {
 | 
|          completer.complete(null);
 | 
|        }
 | 
| @@ -162,10 +168,14 @@ class Phase {
 | 
|    /// removed and re-created. The phase will automatically handle updated assets
 | 
|    /// using the [AssetNode.onStateChange] stream.
 | 
|    void addInput(AssetNode node) {
 | 
| +    if (_inputs.containsKey(node.id)) _inputs[node.id].remove();
 | 
| +
 | 
| +    node.force();
 | 
| +
 | 
|      // Each group is one channel along which an asset may be forwarded, as is
 | 
|      // each transformer.
 | 
|      var forwarder = new PhaseForwarder(
 | 
| -        node, _classifiers.length, _groups.length);
 | 
| +        node, _transformers.length, _groups.length);
 | 
|      _forwarders[node.id] = forwarder;
 | 
|      forwarder.onAsset.listen(_handleOutputWithoutForwarder);
 | 
|      if (forwarder.output != null) {
 | 
| @@ -173,17 +183,24 @@ class Phase {
 | 
|      }
 | 
|  
 | 
|      _inputOrigins.add(node.origin);
 | 
| -    _inputs.add(node);
 | 
| -    node.onStateChange.listen((state) {
 | 
| -      if (state.isRemoved) {
 | 
| -        _inputOrigins.remove(node.origin);
 | 
| -        _forwarders.remove(node.id).remove();
 | 
| -      }
 | 
| -      _streams.changeStatus(status);
 | 
| +    var input = new PhaseInput(this, node, "$_location.$_index");
 | 
| +    _inputs[node.id] = input;
 | 
| +    input.input.whenRemoved(() {
 | 
| +      _inputOrigins.remove(node.origin);
 | 
| +      _inputs.remove(node.id);
 | 
| +      _forwarders.remove(node.id).remove();
 | 
| +      if (!isDirty) _onDoneController.add(null);
 | 
| +    });
 | 
| +    input.onAsset.listen(_handleOutput);
 | 
| +    _onLogPool.add(input.onLog);
 | 
| +    input.onDone.listen((_) {
 | 
| +      if (!isDirty) _onDoneController.add(null);
 | 
|      });
 | 
|  
 | 
| -    for (var classifier in _classifiers.values) {
 | 
| -      classifier.addInput(node);
 | 
| +    input.updateTransformers(_transformers);
 | 
| +
 | 
| +    for (var group in _groups.values) {
 | 
| +      group.addInput(node);
 | 
|      }
 | 
|    }
 | 
|  
 | 
| @@ -219,9 +236,9 @@ class Phase {
 | 
|          });
 | 
|        }
 | 
|  
 | 
| -      // If this phase and the previous phases are fully declared or done, the
 | 
| -      // requested output won't be generated and we can safely return null.
 | 
| -      if (status != NodeStatus.RUNNING) return null;
 | 
| +      // If neither this phase nor the previous phases are dirty, the requested
 | 
| +      // output won't be generated and we can safely return null.
 | 
| +      if (!isDirty) return null;
 | 
|  
 | 
|        // Otherwise, store a completer for the asset node. If it's generated in
 | 
|        // the future, we'll complete this completer.
 | 
| @@ -233,23 +250,11 @@ class Phase {
 | 
|  
 | 
|    /// Set this phase's transformers to [transformers].
 | 
|    void updateTransformers(Iterable transformers) {
 | 
| -    var newTransformers = transformers.where((op) => op is Transformer)
 | 
| -        .toSet();
 | 
| -    var oldTransformers = _classifiers.keys.toSet();
 | 
| -    for (var removed in oldTransformers.difference(newTransformers)) {
 | 
| -      _classifiers.remove(removed).remove();
 | 
| -    }
 | 
| -
 | 
| -    for (var transformer in newTransformers.difference(oldTransformers)) {
 | 
| -      var classifier = new TransformerClassifier(
 | 
| -          this, transformer, "$_location.$_index");
 | 
| -      _classifiers[transformer] = classifier;
 | 
| -      classifier.onAsset.listen(_handleOutput);
 | 
| -      _streams.onLogPool.add(classifier.onLog);
 | 
| -      classifier.onStatusChange.listen((_) => _streams.changeStatus(status));
 | 
| -      for (var input in _inputs) {
 | 
| -        classifier.addInput(input);
 | 
| -      }
 | 
| +    var actualTransformers = transformers.where((op) => op is Transformer);
 | 
| +    _transformers.clear();
 | 
| +    _transformers.addAll(actualTransformers);
 | 
| +    for (var input in _inputs.values) {
 | 
| +      input.updateTransformers(actualTransformers);
 | 
|      }
 | 
|  
 | 
|      var newGroups = transformers.where((op) => op is TransformerGroup)
 | 
| @@ -260,46 +265,40 @@ class Phase {
 | 
|      }
 | 
|  
 | 
|      for (var added in newGroups.difference(oldGroups)) {
 | 
| -      var runner = new GroupRunner(previous, added, "$_location.$_index");
 | 
| +      var runner = new GroupRunner(cascade, added, "$_location.$_index");
 | 
|        _groups[added] = runner;
 | 
|        runner.onAsset.listen(_handleOutput);
 | 
| -      _streams.onLogPool.add(runner.onLog);
 | 
| -      runner.onStatusChange.listen((_) => _streams.changeStatus(status));
 | 
| +      _onLogPool.add(runner.onLog);
 | 
| +      runner.onDone.listen((_) {
 | 
| +        if (!isDirty) _onDoneController.add(null);
 | 
| +      });
 | 
| +      for (var input in _inputs.values) {
 | 
| +        runner.addInput(input.input);
 | 
| +      }
 | 
|      }
 | 
|  
 | 
|      for (var forwarder in _forwarders.values) {
 | 
| -      forwarder.updateTransformers(_classifiers.length, _groups.length);
 | 
| +      forwarder.updateTransformers(_transformers.length, _groups.length);
 | 
|      }
 | 
| -
 | 
| -    _streams.changeStatus(status);
 | 
|    }
 | 
|  
 | 
|    /// Force all [LazyTransformer]s' transforms in this phase to begin producing
 | 
|    /// concrete assets.
 | 
|    void forceAllTransforms() {
 | 
| -    for (var classifier in _classifiers.values) {
 | 
| -      classifier.forceAllTransforms();
 | 
| -    }
 | 
| -
 | 
|      for (var group in _groups.values) {
 | 
|        group.forceAllTransforms();
 | 
|      }
 | 
| +
 | 
| +    for (var input in _inputs.values) {
 | 
| +      input.forceAllTransforms();
 | 
| +    }
 | 
|    }
 | 
|  
 | 
|    /// Add a new phase after this one.
 | 
|    ///
 | 
| -  /// The new phase will have a location annotation describing its place in the
 | 
| -  /// package graph. By default, this annotation will describe it as being
 | 
| -  /// directly after [this]. If [location] is passed, though, it's described as
 | 
| -  /// being the first phase in that location.
 | 
| -  Phase addPhase([String location]) {
 | 
| -    var index = 0;
 | 
| -    if (location == null) {
 | 
| -      location = _location;
 | 
| -      index = _index + 1;
 | 
| -    }
 | 
| -
 | 
| -    var next = new Phase._(cascade, location, index, this);
 | 
| +  /// This may only be called on a phase with no phase following it.
 | 
| +  Phase addPhase() {
 | 
| +    var next = new Phase._(cascade, _location, _index + 1, this);
 | 
|      for (var output in _outputs.values.toList()) {
 | 
|        // Remove [output]'s listeners because now they should get the asset from
 | 
|        // [next], rather than this phase. Any transforms consuming [output] will
 | 
| @@ -313,15 +312,16 @@ class Phase {
 | 
|    ///
 | 
|    /// This will remove all the phase's outputs.
 | 
|    void remove() {
 | 
| -    for (var classifier in _classifiers.values.toList()) {
 | 
| -      classifier.remove();
 | 
| +    for (var input in _inputs.values.toList()) {
 | 
| +      input.remove();
 | 
|      }
 | 
|      for (var group in _groups.values) {
 | 
|        group.remove();
 | 
|      }
 | 
| -    _streams.close();
 | 
| -    if (_previousStatusSubscription != null) {
 | 
| -      _previousStatusSubscription.cancel();
 | 
| +    _onAssetController.close();
 | 
| +    _onLogPool.close();
 | 
| +    if (_previousOnDoneSubscription != null) {
 | 
| +      _previousOnDoneSubscription.cancel();
 | 
|      }
 | 
|      if (_previousOnAssetSubscription != null) {
 | 
|        _previousOnAssetSubscription.cancel();
 | 
| @@ -358,7 +358,7 @@ class Phase {
 | 
|    /// This should be called after [_handleOutput], so that collisions are
 | 
|    /// resolved.
 | 
|    void _emit(AssetNode asset) {
 | 
| -    _streams.onAssetController.add(asset);
 | 
| +    _onAssetController.add(asset);
 | 
|      _providePendingAsset(asset);
 | 
|    }
 | 
|  
 | 
| 
 |