| Index: pkg/barback/lib/src/phase_output.dart
|
| diff --git a/pkg/barback/lib/src/phase_output.dart b/pkg/barback/lib/src/phase_output.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..ae309d9820eba63990cd6e77601aabc457d5a267
|
| --- /dev/null
|
| +++ b/pkg/barback/lib/src/phase_output.dart
|
| @@ -0,0 +1,117 @@
|
| +// Copyright (c) 2013, 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.phase_output;
|
| +
|
| +import 'dart:async';
|
| +import 'dart:collection';
|
| +
|
| +import 'asset_cascade.dart';
|
| +import 'asset_id.dart';
|
| +import 'asset_node.dart';
|
| +import 'errors.dart';
|
| +import 'phase_input.dart';
|
| +import 'stream_pool.dart';
|
| +import 'transformer.dart';
|
| +import 'utils.dart';
|
| +
|
| +/// A class that handles a single output of a phase.
|
| +///
|
| +/// Normally there's only a single [AssetNode] for a phase's output, but it's
|
| +/// possible that multiple transformers in the same phase emit assets with the
|
| +/// same id, causing collisions. This handles those collisions by forwarding the
|
| +/// chronologically first asset.
|
| +class PhaseOutput {
|
| + /// The phase for which this is an output.
|
| + final Phase _phase;
|
| +
|
| + /// The asset node for this output.
|
| + AssetNode get output => _outputController.node;
|
| + final AssetNodeController _outputController;
|
| +
|
| + /// The assets for this output.
|
| + ///
|
| + /// If there's no collision, this will only have one element. Otherwise, it
|
| + /// will be ordered by which asset was added first.
|
| + final _assets = new Queue<AssetNode>();
|
| +
|
| + /// The [AssetCollisionException] for this output, or null if there is no
|
| + /// collision currently.
|
| + AssetCollisionException get collisionException {
|
| + if (_assets.length == 1) return null;
|
| + return new AssetCollisionException(
|
| + _assets.where((asset) => asset.transform != null)
|
| + .map((asset) => asset.transform.info),
|
| + output.id);
|
| + }
|
| +
|
| + PhaseOutput(this._phase, AssetNode output)
|
| + : _outputController = new AssetNodeController.from(output) {
|
| + assert(!output.state.isRemoved);
|
| + add(output);
|
| + }
|
| +
|
| + /// Adds an asset node as an output with this id.
|
| + void add(AssetNode node) {
|
| + assert(node.id == output.id);
|
| + _assets.add(node);
|
| + _watchAsset(node);
|
| + }
|
| +
|
| + /// Watches [node] for state changes and adjusts [_assets] and [output]
|
| + /// appropriately when they occur.
|
| + void _watchAsset(AssetNode node) {
|
| + node.onStateChange.listen((state) {
|
| + if (state.isRemoved) {
|
| + _removeAsset(node);
|
| + return;
|
| + }
|
| + if (_assets.first != node) return;
|
| +
|
| + if (state.isAvailable) {
|
| + _outputController.setAvailable(node.asset);
|
| + } else {
|
| + assert(state.isDirty);
|
| + _outputController.setDirty();
|
| + }
|
| + });
|
| + }
|
| +
|
| + /// Removes [node] as an output.
|
| + void _removeAsset(AssetNode node) {
|
| + if (_assets.length == 1) {
|
| + assert(_assets.single == node);
|
| + _outputController.setRemoved();
|
| + return;
|
| + }
|
| +
|
| + // If there was more than one asset, we're resolving a collision --
|
| + // possibly partially.
|
| + var wasFirst = _assets.first == node;
|
| + _assets.remove(node);
|
| +
|
| + // If this was the first asset, we replace it with the next asset
|
| + // (chronologically).
|
| + if (wasFirst) {
|
| + var newOutput = _assets.first;
|
| + _outputController.setTransform(newOutput.transform);
|
| + if (newOutput.state.isAvailable) {
|
| + if (output.state.isAvailable) _outputController.setDirty();
|
| + _outputController.setAvailable(newOutput.asset);
|
| + } else {
|
| + assert(newOutput.isDirty);
|
| + if (!output.isDirty) _outputController.setDirty();
|
| + }
|
| + }
|
| +
|
| + // If there's still a collision, report it. This lets the user know
|
| + // if they've successfully resolved the collision or not.
|
| + if (_assets.length > 1) {
|
| + // Pump the event queue to ensure that the removal of the input triggers
|
| + // a new build to which we can attach the error.
|
| + // TODO(nweiz): report this through the output asset.
|
| + newFuture(() => _phase.cascade.reportError(collisionException));
|
| + }
|
| + }
|
| +}
|
|
|