1003 lines
28 KiB
Dart
1003 lines
28 KiB
Dart
// Copyright (c) 2015, 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 analyzer.src.task.inputs;
|
|
|
|
import 'dart:collection';
|
|
|
|
import 'package:analyzer/task/model.dart';
|
|
|
|
/**
|
|
* A function that converts an object of the type [B] into a [TaskInput].
|
|
* This is used, for example, by a [ListToListTaskInput] to create task inputs
|
|
* for each value in a list of values.
|
|
*/
|
|
typedef TaskInput<E> GenerateTaskInputs<B, E>(B object);
|
|
|
|
/**
|
|
* A function that maps one [value] to another value.
|
|
*/
|
|
typedef R Mapper<P, R>(P value);
|
|
|
|
/**
|
|
* An input to an [AnalysisTask] that is computed by accessing a single result
|
|
* defined on a single target.
|
|
*/
|
|
class ConstantTaskInput<V> extends TaskInputImpl<V> {
|
|
final V value;
|
|
|
|
ConstantTaskInput(this.value);
|
|
|
|
@override
|
|
TaskInputBuilder<V> createBuilder() {
|
|
return new ConstantTaskInputBuilder<V>(this);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A [TaskInputBuilder] used to build an input based on a [ConstantTaskInput].
|
|
*/
|
|
class ConstantTaskInputBuilder<V> implements TaskInputBuilder<V> {
|
|
final ConstantTaskInput<V> input;
|
|
|
|
ConstantTaskInputBuilder(this.input);
|
|
|
|
@override
|
|
ResultDescriptor get currentResult => null;
|
|
|
|
@override
|
|
AnalysisTarget get currentTarget => null;
|
|
|
|
@override
|
|
void set currentValue(Object value) {
|
|
throw new StateError('Only supported after moveNext() returns true');
|
|
}
|
|
|
|
@override
|
|
bool get flushOnAccess => false;
|
|
|
|
@override
|
|
V get inputValue => input.value;
|
|
|
|
@override
|
|
void currentValueNotAvailable() {
|
|
throw new StateError('Only supported after moveNext() returns true');
|
|
}
|
|
|
|
@override
|
|
bool moveNext() => false;
|
|
}
|
|
|
|
/**
|
|
* An input to an [AnalysisTask] that is computed by accessing a single result
|
|
* defined on a single target.
|
|
*/
|
|
class ListTaskInputImpl<E> extends SimpleTaskInput<List<E>>
|
|
with ListTaskInputMixin<E>
|
|
implements ListTaskInput<E> {
|
|
/**
|
|
* Initialize a newly created task input that computes the input by accessing
|
|
* the given [result] associated with the given [target].
|
|
*/
|
|
ListTaskInputImpl(AnalysisTarget target, ResultDescriptor<List<E>> result)
|
|
: super._unflushable(target, result);
|
|
}
|
|
|
|
/**
|
|
* A mixin-ready implementation of [ListTaskInput].
|
|
*/
|
|
abstract class ListTaskInputMixin<E> implements ListTaskInput<E> {
|
|
@override
|
|
ListTaskInput/*<V>*/ toFlattenListOf/*<V>*/(
|
|
ListResultDescriptor/*<V>*/ subListResult) {
|
|
return new ListToFlattenListTaskInput<E, dynamic/*=V*/ >(
|
|
this,
|
|
(E element) =>
|
|
subListResult.of(element as AnalysisTarget) as TaskInput/*<V>*/);
|
|
}
|
|
|
|
ListTaskInput/*<V>*/ toList/*<V>*/(UnaryFunction<E, dynamic/*=V*/ > mapper) {
|
|
return new ListToListTaskInput<E, dynamic/*=V*/ >(this, mapper);
|
|
}
|
|
|
|
ListTaskInput/*<V>*/ toListOf/*<V>*/(ResultDescriptor/*<V>*/ valueResult) {
|
|
return (this as ListTaskInput<AnalysisTarget>).toList(valueResult.of);
|
|
}
|
|
|
|
MapTaskInput<E, dynamic/*=V*/ > toMap/*<V>*/(
|
|
UnaryFunction<E, dynamic/*=V*/ > mapper) {
|
|
return new ListToMapTaskInput<E, dynamic/*=V*/ >(this, mapper);
|
|
}
|
|
|
|
MapTaskInput<AnalysisTarget, dynamic/*=V*/ > toMapOf/*<V>*/(
|
|
ResultDescriptor/*<V>*/ valueResult) {
|
|
return (this as ListTaskInputImpl<AnalysisTarget>).toMap(valueResult.of);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* An input to an [AnalysisTask] that is computed by the following steps. First
|
|
* another (base) task input is used to compute a [List]-valued result. An input
|
|
* generator function is then used to map each element of that list to a task
|
|
* input. Finally, each of the task inputs are used to access analysis results,
|
|
* and the list of the analysis results is used as the input to the task.
|
|
*/
|
|
class ListToFlattenListTaskInput<B, E>
|
|
extends _ListToCollectionTaskInput<B, E, List<E>>
|
|
with ListTaskInputMixin<E>
|
|
implements ListTaskInput<E> {
|
|
/**
|
|
* Initialize a result accessor to use the given [baseAccessor] to access a
|
|
* list of values that can be passed to the given [generateTaskInputs] to
|
|
* generate a list of task inputs that can be used to access the elements of
|
|
* the input being accessed.
|
|
*/
|
|
ListToFlattenListTaskInput(TaskInput<List<B>> baseAccessor,
|
|
GenerateTaskInputs<B, E> generateTaskInputs)
|
|
: super(baseAccessor, generateTaskInputs);
|
|
|
|
@override
|
|
TaskInputBuilder<List<E>> createBuilder() =>
|
|
new ListToFlattenListTaskInputBuilder<B, E>(this);
|
|
}
|
|
|
|
/**
|
|
* A [TaskInputBuilder] used to build an input based on a [ListToFlattenListTaskInput].
|
|
*/
|
|
class ListToFlattenListTaskInputBuilder<B, E>
|
|
extends _ListToCollectionTaskInputBuilder<B, E, List<E>> {
|
|
/**
|
|
* The list of values being built.
|
|
*/
|
|
List<E> _resultValue;
|
|
|
|
/**
|
|
* Initialize a newly created task input builder that computes the result
|
|
* specified by the given [input].
|
|
*/
|
|
ListToFlattenListTaskInputBuilder(ListToFlattenListTaskInput<B, E> input)
|
|
: super(input);
|
|
|
|
@override
|
|
void _addResultElement(B baseElement, E resultElement) {
|
|
_resultValue.addAll(resultElement as Iterable<E>);
|
|
}
|
|
|
|
@override
|
|
void _initResultValue() {
|
|
_resultValue = <E>[];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* An input to an [AnalysisTask] that is computed by the following steps. First
|
|
* another (base) task input is used to compute a [List]-valued result. An input
|
|
* generator function is then used to map each element of that list to a task
|
|
* input. Finally, each of the task inputs are used to access analysis results,
|
|
* and the list of the analysis results is used as the input to the task.
|
|
*/
|
|
class ListToListTaskInput<B, E>
|
|
extends _ListToCollectionTaskInput<B, E, List<E>>
|
|
with ListTaskInputMixin<E> {
|
|
/**
|
|
* Initialize a result accessor to use the given [baseAccessor] to access a
|
|
* list of values that can be passed to the given [generateTaskInputs] to
|
|
* generate a list of task inputs that can be used to access the elements of
|
|
* the input being accessed.
|
|
*/
|
|
ListToListTaskInput(TaskInput<List<B>> baseAccessor,
|
|
GenerateTaskInputs<B, E> generateTaskInputs)
|
|
: super(baseAccessor, generateTaskInputs);
|
|
|
|
@override
|
|
TaskInputBuilder<List<E>> createBuilder() =>
|
|
new ListToListTaskInputBuilder<B, E>(this);
|
|
}
|
|
|
|
/**
|
|
* A [TaskInputBuilder] used to build an input based on a [ListToListTaskInput].
|
|
*/
|
|
class ListToListTaskInputBuilder<B, E>
|
|
extends _ListToCollectionTaskInputBuilder<B, E, List<E>> {
|
|
/**
|
|
* The list of values being built.
|
|
*/
|
|
List<E> _resultValue;
|
|
|
|
/**
|
|
* Initialize a newly created task input builder that computes the result
|
|
* specified by the given [input].
|
|
*/
|
|
ListToListTaskInputBuilder(ListToListTaskInput<B, E> input) : super(input);
|
|
|
|
@override
|
|
void _addResultElement(B baseElement, E resultElement) {
|
|
_resultValue.add(resultElement);
|
|
}
|
|
|
|
@override
|
|
void _initResultValue() {
|
|
_resultValue = <E>[];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* An input to an [AnalysisTask] that is computed by the following steps. First
|
|
* another (base) task input is used to compute a [List]-valued result. An input
|
|
* generator function is then used to map each element of that list to a task
|
|
* input. Finally, each of the task inputs are used to access analysis results,
|
|
* and the map of the base elements to the analysis results is used as the
|
|
* input to the task.
|
|
*/
|
|
class ListToMapTaskInput<B, E>
|
|
extends _ListToCollectionTaskInput<B, E, Map<B, E>>
|
|
with MapTaskInputMixin<B, E> {
|
|
/**
|
|
* Initialize a result accessor to use the given [baseAccessor] to access a
|
|
* list of values that can be passed to the given [generateTaskInputs] to
|
|
* generate a list of task inputs that can be used to access the elements of
|
|
* the input being accessed.
|
|
*/
|
|
ListToMapTaskInput(TaskInput<List<B>> baseAccessor,
|
|
GenerateTaskInputs<B, E> generateTaskInputs)
|
|
: super(baseAccessor, generateTaskInputs);
|
|
|
|
@override
|
|
TaskInputBuilder<Map<B, E>> createBuilder() =>
|
|
new ListToMapTaskInputBuilder<B, E>(this);
|
|
}
|
|
|
|
/**
|
|
* A [TaskInputBuilder] used to build an input based on a [ListToMapTaskInput].
|
|
*/
|
|
class ListToMapTaskInputBuilder<B, E>
|
|
extends _ListToCollectionTaskInputBuilder<B, E, Map<B, E>> {
|
|
/**
|
|
* The map being built.
|
|
*/
|
|
Map<B, E> _resultValue;
|
|
|
|
/**
|
|
* Initialize a newly created task input builder that computes the result
|
|
* specified by the given [input].
|
|
*/
|
|
ListToMapTaskInputBuilder(ListToMapTaskInput<B, E> input) : super(input);
|
|
|
|
@override
|
|
void _addResultElement(B baseElement, E resultElement) {
|
|
_resultValue[baseElement] = resultElement;
|
|
}
|
|
|
|
@override
|
|
void _initResultValue() {
|
|
_resultValue = new HashMap<B, E>();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A mixin-ready implementation of [MapTaskInput].
|
|
*/
|
|
abstract class MapTaskInputMixin<K, V> implements MapTaskInput<K, V> {
|
|
TaskInput<List/*<E>*/ > toFlattenList/*<E>*/(
|
|
BinaryFunction<K, dynamic /*element of V*/, dynamic/*=E*/ > mapper) {
|
|
return new MapToFlattenListTaskInput<K, dynamic /*element of V*/,
|
|
dynamic/*=E*/ >(this as MapTaskInput<K, List /*element of V*/ >, mapper);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A [TaskInput] that is computed by the following steps.
|
|
*
|
|
* First the [base] task input is used to compute a [Map]-valued result.
|
|
* The values of the [Map] must be [List]s.
|
|
*
|
|
* The given [mapper] is used to transform each key / value pair of the [Map]
|
|
* into task inputs.
|
|
*
|
|
* Finally, each of the task inputs are used to access analysis results,
|
|
* and the list of the results is used as the input.
|
|
*/
|
|
class MapToFlattenListTaskInput<K, V, E> extends TaskInputImpl<List<E>> {
|
|
final MapTaskInput<K, List<V>> base;
|
|
final BinaryFunction<K, V, E> mapper;
|
|
|
|
MapToFlattenListTaskInput(this.base, this.mapper);
|
|
|
|
@override
|
|
TaskInputBuilder<List<E>> createBuilder() {
|
|
return new MapToFlattenListTaskInputBuilder<K, V, E>(base, mapper);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The [TaskInputBuilder] for [MapToFlattenListTaskInput].
|
|
*/
|
|
class MapToFlattenListTaskInputBuilder<K, V, E>
|
|
implements TaskInputBuilder<List<E>> {
|
|
final MapTaskInput<K, List<V>> base;
|
|
final BinaryFunction<K, V, E> mapper;
|
|
|
|
TaskInputBuilder currentBuilder;
|
|
Map<K, List<V>> baseMap;
|
|
Iterator<K> keyIterator;
|
|
Iterator<V> valueIterator;
|
|
|
|
final List<E> inputValue = <E>[];
|
|
|
|
MapToFlattenListTaskInputBuilder(this.base, this.mapper) {
|
|
currentBuilder = base.createBuilder();
|
|
}
|
|
|
|
@override
|
|
ResultDescriptor get currentResult {
|
|
if (currentBuilder == null) {
|
|
return null;
|
|
}
|
|
return currentBuilder.currentResult;
|
|
}
|
|
|
|
AnalysisTarget get currentTarget {
|
|
if (currentBuilder == null) {
|
|
return null;
|
|
}
|
|
return currentBuilder.currentTarget;
|
|
}
|
|
|
|
@override
|
|
void set currentValue(Object value) {
|
|
if (currentBuilder == null) {
|
|
throw new StateError(
|
|
'Cannot set the result value when there is no current result');
|
|
}
|
|
currentBuilder.currentValue = value;
|
|
}
|
|
|
|
@override
|
|
bool get flushOnAccess => currentBuilder.flushOnAccess;
|
|
|
|
@override
|
|
void currentValueNotAvailable() {
|
|
if (currentBuilder == null) {
|
|
throw new StateError(
|
|
'Cannot set the result value when there is no current result');
|
|
}
|
|
currentBuilder.currentValueNotAvailable();
|
|
}
|
|
|
|
@override
|
|
bool moveNext() {
|
|
// Prepare base Map.
|
|
if (baseMap == null) {
|
|
if (currentBuilder.moveNext()) {
|
|
return true;
|
|
}
|
|
baseMap = currentBuilder.inputValue as dynamic/*=Map<K, List<V>>*/;
|
|
if (baseMap == null) {
|
|
// No base map could be computed due to a circular dependency. Use an
|
|
// empty map so that no further results will be computed.
|
|
baseMap = {};
|
|
}
|
|
keyIterator = baseMap.keys.iterator;
|
|
// Done with this builder.
|
|
currentBuilder = null;
|
|
}
|
|
// Prepare the next result value.
|
|
if (currentBuilder != null) {
|
|
if (currentBuilder.moveNext()) {
|
|
return true;
|
|
}
|
|
// Add the result value for the current Map key/value.
|
|
E resultValue = currentBuilder.inputValue as dynamic/*=E*/;
|
|
if (resultValue != null) {
|
|
inputValue.add(resultValue);
|
|
}
|
|
// Done with this builder.
|
|
currentBuilder = null;
|
|
}
|
|
// Move to the next Map value.
|
|
if (valueIterator != null && valueIterator.moveNext()) {
|
|
K key = keyIterator.current;
|
|
V value = valueIterator.current;
|
|
currentBuilder = mapper(key, value).createBuilder();
|
|
return moveNext();
|
|
}
|
|
// Move to the next Map key.
|
|
if (keyIterator.moveNext()) {
|
|
K key = keyIterator.current;
|
|
valueIterator = baseMap[key].iterator;
|
|
return moveNext();
|
|
}
|
|
// No more Map values/keys to transform.
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* An input to an [AnalysisTask] that is computed by mapping the value of
|
|
* another task input to a list of values.
|
|
*/
|
|
class ObjectToListTaskInput<E> extends TaskInputImpl<List<E>>
|
|
with ListTaskInputMixin<E>
|
|
implements ListTaskInput<E> {
|
|
// TODO(brianwilkerson) Add another type parameter to this class that can be
|
|
// used as the type of the keys of [mapper].
|
|
/**
|
|
* The input used to compute the value to be mapped.
|
|
*/
|
|
final TaskInput baseInput;
|
|
|
|
/**
|
|
* The function used to map the value of the base input to the list of values.
|
|
*/
|
|
final Mapper<Object, List<E>> mapper;
|
|
|
|
/**
|
|
* Initialize a newly created task input that computes the input by accessing
|
|
* the given [baseInput] associated with the given [mapper].
|
|
*/
|
|
ObjectToListTaskInput(this.baseInput, this.mapper);
|
|
|
|
@override
|
|
TaskInputBuilder<List<E>> createBuilder() =>
|
|
new ObjectToListTaskInputBuilder<E>(this);
|
|
|
|
@override
|
|
ListTaskInput/*<V>*/ toFlattenListOf/*<V>*/(
|
|
ListResultDescriptor/*<V>*/ subListResult) {
|
|
return new ListToFlattenListTaskInput<E, dynamic/*=V*/ >(
|
|
this,
|
|
(E element) =>
|
|
subListResult.of(element as AnalysisTarget) as TaskInput/*<V>*/);
|
|
}
|
|
|
|
@override
|
|
ListTaskInput/*<V>*/ toListOf/*<V>*/(ResultDescriptor/*<V>*/ valueResult) {
|
|
return new ListToListTaskInput<E, dynamic/*=V*/ >(
|
|
this, (E element) => valueResult.of(element as AnalysisTarget));
|
|
}
|
|
|
|
@override
|
|
MapTaskInput<AnalysisTarget, dynamic/*=V*/ > toMapOf/*<V>*/(
|
|
ResultDescriptor/*<V>*/ valueResult) {
|
|
return new ListToMapTaskInput<AnalysisTarget, dynamic/*=V*/ >(
|
|
this as dynamic/*=TaskInput<List<AnalysisTarget>>*/, valueResult.of);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A [TaskInputBuilder] used to build an input based on a [SimpleTaskInput].
|
|
*/
|
|
class ObjectToListTaskInputBuilder<E> implements TaskInputBuilder<List<E>> {
|
|
/**
|
|
* The input being built.
|
|
*/
|
|
final ObjectToListTaskInput<E> input;
|
|
|
|
/**
|
|
* The builder created by the input.
|
|
*/
|
|
TaskInputBuilder builder;
|
|
|
|
/**
|
|
* The value of the input being built, or `null` if the value hasn't been set
|
|
* yet or if no result is available ([currentValueNotAvailable] was called).
|
|
*/
|
|
List<E> _inputValue = null;
|
|
|
|
/**
|
|
* Initialize a newly created task input builder that computes the result
|
|
* specified by the given [input].
|
|
*/
|
|
ObjectToListTaskInputBuilder(this.input) {
|
|
builder = input.baseInput.createBuilder();
|
|
}
|
|
|
|
@override
|
|
ResultDescriptor get currentResult {
|
|
if (builder == null) {
|
|
return null;
|
|
}
|
|
return builder.currentResult;
|
|
}
|
|
|
|
@override
|
|
AnalysisTarget get currentTarget {
|
|
if (builder == null) {
|
|
return null;
|
|
}
|
|
return builder.currentTarget;
|
|
}
|
|
|
|
@override
|
|
void set currentValue(Object value) {
|
|
if (builder == null) {
|
|
throw new StateError(
|
|
'Cannot set the result value when there is no current result');
|
|
}
|
|
builder.currentValue = value;
|
|
}
|
|
|
|
@override
|
|
bool get flushOnAccess => builder.flushOnAccess;
|
|
|
|
@override
|
|
List<E> get inputValue {
|
|
if (builder != null) {
|
|
throw new StateError('Result value has not been created');
|
|
}
|
|
return _inputValue;
|
|
}
|
|
|
|
@override
|
|
void currentValueNotAvailable() {
|
|
if (builder == null) {
|
|
throw new StateError(
|
|
'Cannot set the result value when there is no current result');
|
|
}
|
|
builder.currentValueNotAvailable();
|
|
}
|
|
|
|
@override
|
|
bool moveNext() {
|
|
if (builder == null) {
|
|
return false;
|
|
} else if (builder.moveNext()) {
|
|
return true;
|
|
} else {
|
|
// This might not be the right semantics. If the value could not be
|
|
// computed then we pass the resulting `null` in to the mapper function.
|
|
// Unfortunately, we cannot tell the difference between a `null` that's
|
|
// there because no value could be computed and a `null` that's there
|
|
// because that's what *was* computed.
|
|
_inputValue = input.mapper(builder.inputValue);
|
|
builder = null;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* An input to an [AnalysisTask] that is computed by accessing a single result
|
|
* defined on a single target.
|
|
*/
|
|
class SimpleTaskInput<V> extends TaskInputImpl<V> {
|
|
/**
|
|
* The target on which the result is defined.
|
|
*/
|
|
final AnalysisTarget target;
|
|
|
|
/**
|
|
* The result to be accessed.
|
|
*/
|
|
final ResultDescriptor<V> result;
|
|
|
|
/**
|
|
* Return `true` if the value accessed by this input builder should be flushed
|
|
* from the cache at the time it is retrieved.
|
|
*/
|
|
final bool flushOnAccess;
|
|
|
|
/**
|
|
* Initialize a newly created task input that computes the input by accessing
|
|
* the given [result] associated with the given [target].
|
|
*/
|
|
SimpleTaskInput(this.target, this.result, {this.flushOnAccess: false});
|
|
|
|
/**
|
|
* Initialize a newly created task input that computes the input by accessing
|
|
* the given [result] associated with the given [target].
|
|
*/
|
|
SimpleTaskInput._unflushable(this.target, this.result)
|
|
: flushOnAccess = false;
|
|
|
|
@override
|
|
TaskInputBuilder<V> createBuilder() => new SimpleTaskInputBuilder<V>(this);
|
|
}
|
|
|
|
/**
|
|
* A [TaskInputBuilder] used to build an input based on a [SimpleTaskInput].
|
|
*/
|
|
class SimpleTaskInputBuilder<V> implements TaskInputBuilder<V> {
|
|
/**
|
|
* The state value indicating that the builder is positioned before the single
|
|
* result.
|
|
*/
|
|
static const _BEFORE = -1;
|
|
|
|
/**
|
|
* The state value indicating that the builder is positioned at the single
|
|
* result.
|
|
*/
|
|
static const _AT = 0;
|
|
|
|
/**
|
|
* The state value indicating that the builder is positioned after the single
|
|
* result.
|
|
*/
|
|
static const _AFTER = 1;
|
|
|
|
/**
|
|
* The input being built.
|
|
*/
|
|
final SimpleTaskInput<V> input;
|
|
|
|
/**
|
|
* The value of the input being built. `null` if the value hasn't been set
|
|
* yet, or if no result is available ([currentValueNotAvailable] was called).
|
|
*/
|
|
V _resultValue = null;
|
|
|
|
/**
|
|
* The state of the builder.
|
|
*/
|
|
int _state = _BEFORE;
|
|
|
|
/**
|
|
* A flag indicating whether the result value was explicitly set.
|
|
*/
|
|
bool _resultSet = false;
|
|
|
|
/**
|
|
* Initialize a newly created task input builder that computes the result
|
|
* specified by the given [input].
|
|
*/
|
|
SimpleTaskInputBuilder(this.input);
|
|
|
|
@override
|
|
ResultDescriptor get currentResult => _state == _AT ? input.result : null;
|
|
|
|
@override
|
|
AnalysisTarget get currentTarget => _state == _AT ? input.target : null;
|
|
|
|
@override
|
|
void set currentValue(Object value) {
|
|
if (_state != _AT) {
|
|
throw new StateError(
|
|
'Cannot set the result value when there is no current result');
|
|
}
|
|
_resultValue = value as V;
|
|
_resultSet = true;
|
|
}
|
|
|
|
@override
|
|
bool get flushOnAccess => input.flushOnAccess;
|
|
|
|
@override
|
|
V get inputValue {
|
|
if (_state != _AFTER) {
|
|
throw new StateError('Result value has not been created');
|
|
}
|
|
return _resultValue;
|
|
}
|
|
|
|
@override
|
|
void currentValueNotAvailable() {
|
|
if (_state != _AT) {
|
|
throw new StateError(
|
|
'Cannot set the result value when there is no current result');
|
|
}
|
|
_resultValue = null;
|
|
_resultSet = true;
|
|
}
|
|
|
|
@override
|
|
bool moveNext() {
|
|
if (_state == _BEFORE) {
|
|
_state = _AT;
|
|
return true;
|
|
} else {
|
|
if (!_resultSet) {
|
|
throw new StateError(
|
|
'The value of the current result must be set before moving to the next result.');
|
|
}
|
|
_state = _AFTER;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
abstract class TaskInputImpl<V> implements TaskInput<V> {
|
|
@override
|
|
ListTaskInput/*<E>*/ mappedToList/*<E>*/(List/*<E>*/ mapper(V value)) {
|
|
return new ObjectToListTaskInput(
|
|
this, (Object element) => mapper(element as V));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A [TaskInputBuilder] used to build an input based on one or more other task
|
|
* inputs. The task inputs to be built are specified by a table mapping the name
|
|
* of the input to the task used to access the input's value.
|
|
*/
|
|
class TopLevelTaskInputBuilder
|
|
implements TaskInputBuilder<Map<String, Object>> {
|
|
/**
|
|
* The descriptors describing the inputs to be built.
|
|
*/
|
|
final Map<String, TaskInput> inputDescriptors;
|
|
|
|
/**
|
|
* The names of the inputs. There are the keys from the [inputDescriptors] in
|
|
* an indexable form.
|
|
*/
|
|
List<String> inputNames;
|
|
|
|
/**
|
|
* The index of the input name associated with the current result and target.
|
|
*/
|
|
int nameIndex = -1;
|
|
|
|
/**
|
|
* The builder used to build the current result.
|
|
*/
|
|
TaskInputBuilder currentBuilder;
|
|
|
|
/**
|
|
* The inputs that are being or have been built. The map will be incomplete
|
|
* unless the method [moveNext] returns `false`.
|
|
*/
|
|
final Map<String, Object> inputs = new HashMap<String, Object>();
|
|
|
|
/**
|
|
* Initialize a newly created task input builder to build the inputs described
|
|
* by the given [inputDescriptors].
|
|
*/
|
|
TopLevelTaskInputBuilder(this.inputDescriptors) {
|
|
inputNames = inputDescriptors.keys.toList();
|
|
}
|
|
|
|
@override
|
|
ResultDescriptor get currentResult {
|
|
if (currentBuilder == null) {
|
|
return null;
|
|
}
|
|
return currentBuilder.currentResult;
|
|
}
|
|
|
|
@override
|
|
AnalysisTarget get currentTarget {
|
|
if (currentBuilder == null) {
|
|
return null;
|
|
}
|
|
return currentBuilder.currentTarget;
|
|
}
|
|
|
|
@override
|
|
void set currentValue(Object value) {
|
|
if (currentBuilder == null) {
|
|
throw new StateError(
|
|
'Cannot set the result value when there is no current result');
|
|
}
|
|
currentBuilder.currentValue = value;
|
|
}
|
|
|
|
@override
|
|
bool get flushOnAccess => currentBuilder.flushOnAccess;
|
|
|
|
@override
|
|
Map<String, Object> get inputValue {
|
|
if (nameIndex < inputNames.length) {
|
|
throw new StateError('Result value has not been created');
|
|
}
|
|
return inputs;
|
|
}
|
|
|
|
/**
|
|
* Assuming that there is a current input, return its name.
|
|
*/
|
|
String get _currentName => inputNames[nameIndex];
|
|
|
|
@override
|
|
void currentValueNotAvailable() {
|
|
if (currentBuilder == null) {
|
|
throw new StateError(
|
|
'Cannot set the result value when there is no current result');
|
|
}
|
|
currentBuilder.currentValueNotAvailable();
|
|
}
|
|
|
|
@override
|
|
bool moveNext() {
|
|
if (nameIndex >= inputNames.length) {
|
|
// We have already computed all of the results, so just return false.
|
|
return false;
|
|
}
|
|
if (nameIndex < 0) {
|
|
// This is the first time moveNext has been invoked, so we just determine
|
|
// whether there are any results to be computed.
|
|
nameIndex = 0;
|
|
} else {
|
|
if (currentBuilder.moveNext()) {
|
|
// We are still working on building the value associated with the
|
|
// current name.
|
|
return true;
|
|
}
|
|
if (currentBuilder.inputValue != null) {
|
|
inputs[_currentName] = currentBuilder.inputValue;
|
|
}
|
|
nameIndex++;
|
|
}
|
|
if (nameIndex >= inputNames.length) {
|
|
// There is no next value, so we're done.
|
|
return false;
|
|
}
|
|
currentBuilder = inputDescriptors[_currentName].createBuilder();
|
|
while (!currentBuilder.moveNext()) {
|
|
if (currentBuilder.inputValue != null) {
|
|
inputs[_currentName] = currentBuilder.inputValue;
|
|
}
|
|
nameIndex++;
|
|
if (nameIndex >= inputNames.length) {
|
|
// There is no next value, so we're done.
|
|
return false;
|
|
}
|
|
currentBuilder = inputDescriptors[_currentName].createBuilder();
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* An input to an [AnalysisTask] that is computed by the following steps. First
|
|
* another (base) task input is used to compute a [List]-valued result. An input
|
|
* generator function is then used to map each element of that list to a task
|
|
* input. Finally, each of the task inputs are used to access analysis results,
|
|
* and a collection of the analysis results is used as the input to the task.
|
|
*/
|
|
abstract class _ListToCollectionTaskInput<B, E, C> extends TaskInputImpl<C> {
|
|
/**
|
|
* The accessor used to access the list of elements being mapped.
|
|
*/
|
|
final TaskInput<List<B>> baseAccessor;
|
|
|
|
/**
|
|
* The function used to convert an element in the list returned by the
|
|
* [baseAccessor] to a task input.
|
|
*/
|
|
final GenerateTaskInputs<B, E> generateTaskInputs;
|
|
|
|
/**
|
|
* Initialize a result accessor to use the given [baseAccessor] to access a
|
|
* list of values that can be passed to the given [generateTaskInputs] to
|
|
* generate a list of task inputs that can be used to access the elements of
|
|
* the input being accessed.
|
|
*/
|
|
_ListToCollectionTaskInput(this.baseAccessor, this.generateTaskInputs);
|
|
}
|
|
|
|
/**
|
|
* A [TaskInputBuilder] used to build an [_ListToCollectionTaskInput].
|
|
*/
|
|
abstract class _ListToCollectionTaskInputBuilder<B, E, C>
|
|
implements TaskInputBuilder<C> {
|
|
/**
|
|
* The input being built.
|
|
*/
|
|
final _ListToCollectionTaskInput<B, E, C> input;
|
|
|
|
/**
|
|
* The builder used to build the current result.
|
|
*/
|
|
TaskInputBuilder currentBuilder;
|
|
|
|
/**
|
|
* The list of values computed by the [input]'s base accessor.
|
|
*/
|
|
List<B> _baseList = null;
|
|
|
|
/**
|
|
* The index in the [_baseList] of the value for which a value is currently
|
|
* being built.
|
|
*/
|
|
int _baseListIndex = -1;
|
|
|
|
/**
|
|
* The element of the [_baseList] for which a value is currently being built.
|
|
*/
|
|
B _baseListElement;
|
|
|
|
/**
|
|
* Initialize a newly created task input builder that computes the result
|
|
* specified by the given [input].
|
|
*/
|
|
_ListToCollectionTaskInputBuilder(this.input);
|
|
|
|
@override
|
|
ResultDescriptor get currentResult {
|
|
if (currentBuilder == null) {
|
|
return null;
|
|
}
|
|
return currentBuilder.currentResult;
|
|
}
|
|
|
|
@override
|
|
AnalysisTarget get currentTarget {
|
|
if (currentBuilder == null) {
|
|
return null;
|
|
}
|
|
return currentBuilder.currentTarget;
|
|
}
|
|
|
|
@override
|
|
void set currentValue(Object value) {
|
|
if (currentBuilder == null) {
|
|
throw new StateError(
|
|
'Cannot set the result value when there is no current result');
|
|
}
|
|
currentBuilder.currentValue = value;
|
|
}
|
|
|
|
@override
|
|
bool get flushOnAccess => currentBuilder.flushOnAccess;
|
|
|
|
@override
|
|
C get inputValue {
|
|
if (currentBuilder != null || _resultValue == null) {
|
|
throw new StateError('Result value has not been created');
|
|
}
|
|
return _resultValue;
|
|
}
|
|
|
|
/**
|
|
* The list of values being built.
|
|
*/
|
|
C get _resultValue;
|
|
|
|
@override
|
|
void currentValueNotAvailable() {
|
|
if (currentBuilder == null) {
|
|
throw new StateError(
|
|
'Cannot set the result value when there is no current result');
|
|
}
|
|
currentBuilder.currentValueNotAvailable();
|
|
}
|
|
|
|
@override
|
|
bool moveNext() {
|
|
if (currentBuilder == null) {
|
|
if (_resultValue == null) {
|
|
// This is the first time moveNext has been invoked, so start by
|
|
// computing the list of values from which the results will be derived.
|
|
currentBuilder = input.baseAccessor.createBuilder();
|
|
return currentBuilder.moveNext();
|
|
} else {
|
|
// We have already computed all of the results, so just return false.
|
|
return false;
|
|
}
|
|
}
|
|
if (currentBuilder.moveNext()) {
|
|
return true;
|
|
}
|
|
if (_resultValue == null) {
|
|
// We have finished computing the list of values from which the results
|
|
// will be derived.
|
|
_baseList = currentBuilder.inputValue as dynamic/*=List<B>*/;
|
|
if (_baseList == null) {
|
|
// No base list could be computed due to a circular dependency. Use an
|
|
// empty list so that no further results will be computed.
|
|
_baseList = [];
|
|
}
|
|
_baseListIndex = 0;
|
|
_initResultValue();
|
|
} else {
|
|
// We have finished computing one of the elements in the result list.
|
|
if (currentBuilder.inputValue != null) {
|
|
_addResultElement(
|
|
_baseListElement, currentBuilder.inputValue as dynamic/*=E*/);
|
|
}
|
|
_baseListIndex++;
|
|
}
|
|
if (_baseListIndex >= _baseList.length) {
|
|
currentBuilder = null;
|
|
return false;
|
|
}
|
|
_baseListElement = _baseList[_baseListIndex];
|
|
currentBuilder = input.generateTaskInputs(_baseListElement).createBuilder();
|
|
return currentBuilder.moveNext();
|
|
}
|
|
|
|
void _addResultElement(B baseElement, E resultElement);
|
|
void _initResultValue();
|
|
}
|