OLD | NEW |
| (Empty) |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | |
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. | |
4 | |
5 library analyzer.src.task.inputs; | |
6 | |
7 import 'dart:collection'; | |
8 | |
9 import 'package:analyzer/task/model.dart'; | |
10 | |
11 /** | |
12 * A function that converts an object of the type [B] into a [TaskInput]. | |
13 * This is used, for example, by a [ListToListTaskInput] to create task inputs | |
14 * for each value in a list of values. | |
15 */ | |
16 typedef TaskInput<E> GenerateTaskInputs<B, E>(B object); | |
17 | |
18 /** | |
19 * A function that maps one [value] to another value. | |
20 */ | |
21 typedef R Mapper<P, R>(P value); | |
22 | |
23 /** | |
24 * An input to an [AnalysisTask] that is computed by accessing a single result | |
25 * defined on a single target. | |
26 */ | |
27 class ListTaskInputImpl<E> extends SimpleTaskInput<List<E>> | |
28 with ListTaskInputMixin<E> implements ListTaskInput<E> { | |
29 /** | |
30 * Initialize a newly created task input that computes the input by accessing | |
31 * the given [result] associated with the given [target]. | |
32 */ | |
33 ListTaskInputImpl(AnalysisTarget target, ResultDescriptor<List<E>> result) | |
34 : super(target, result); | |
35 } | |
36 | |
37 /** | |
38 * A mixin-ready implementation of [ListTaskInput]. | |
39 */ | |
40 abstract class ListTaskInputMixin<E> implements ListTaskInput<E> { | |
41 ListTaskInput /*<V>*/ toList(UnaryFunction<E, dynamic /*<V>*/ > mapper) { | |
42 return new ListToListTaskInput<E, dynamic /*V*/ >(this, mapper); | |
43 } | |
44 | |
45 ListTaskInput /*<V>*/ toListOf(ResultDescriptor /*<V>*/ valueResult) { | |
46 return (this as ListTaskInputImpl<AnalysisTarget>).toList(valueResult.of); | |
47 } | |
48 | |
49 MapTaskInput<E, dynamic /*V*/ > toMap( | |
50 UnaryFunction<E, dynamic /*<V>*/ > mapper) { | |
51 return new ListToMapTaskInput<E, dynamic /*V*/ >(this, mapper); | |
52 } | |
53 | |
54 MapTaskInput<AnalysisTarget, dynamic /*V*/ > toMapOf( | |
55 ResultDescriptor /*<V>*/ valueResult) { | |
56 return (this as ListTaskInputImpl<AnalysisTarget>).toMap(valueResult.of); | |
57 } | |
58 } | |
59 | |
60 /** | |
61 * An input to an [AnalysisTask] that is computed by the following steps. First | |
62 * another (base) task input is used to compute a [List]-valued result. An input | |
63 * generator function is then used to map each element of that list to a task | |
64 * input. Finally, each of the task inputs are used to access analysis results, | |
65 * and the list of the analysis results is used as the input to the task. | |
66 */ | |
67 class ListToListTaskInput<B, E> | |
68 extends _ListToCollectionTaskInput<B, E, List<E>> | |
69 with ListTaskInputMixin<E> { | |
70 /** | |
71 * Initialize a result accessor to use the given [baseAccessor] to access a | |
72 * list of values that can be passed to the given [generateTaskInputs] to | |
73 * generate a list of task inputs that can be used to access the elements of | |
74 * the input being accessed. | |
75 */ | |
76 ListToListTaskInput(TaskInput<List<B>> baseAccessor, | |
77 GenerateTaskInputs<B, E> generateTaskInputs) | |
78 : super(baseAccessor, generateTaskInputs); | |
79 | |
80 @override | |
81 TaskInputBuilder<List<E>> createBuilder() => | |
82 new ListToListTaskInputBuilder<B, E>(this); | |
83 } | |
84 | |
85 /** | |
86 * A [TaskInputBuilder] used to build an input based on a [ListToListTaskInput]. | |
87 */ | |
88 class ListToListTaskInputBuilder<B, E> | |
89 extends _ListToCollectionTaskInputBuilder<B, E, List<E>> { | |
90 /** | |
91 * The list of values being built. | |
92 */ | |
93 List<E> _resultValue; | |
94 | |
95 /** | |
96 * Initialize a newly created task input builder that computes the result | |
97 * specified by the given [input]. | |
98 */ | |
99 ListToListTaskInputBuilder(ListToListTaskInput<B, E> input) : super(input); | |
100 | |
101 @override | |
102 void _addResultElement(B baseElement, E resultElement) { | |
103 _resultValue.add(resultElement); | |
104 } | |
105 | |
106 @override | |
107 void _initResultValue() { | |
108 _resultValue = <E>[]; | |
109 } | |
110 } | |
111 | |
112 /** | |
113 * An input to an [AnalysisTask] that is computed by the following steps. First | |
114 * another (base) task input is used to compute a [List]-valued result. An input | |
115 * generator function is then used to map each element of that list to a task | |
116 * input. Finally, each of the task inputs are used to access analysis results, | |
117 * and the map of the base elements to the analysis results is used as the | |
118 * input to the task. | |
119 */ | |
120 class ListToMapTaskInput<B, E> | |
121 extends _ListToCollectionTaskInput<B, E, Map<B, E>> | |
122 with MapTaskInputMixin<B, E> { | |
123 /** | |
124 * Initialize a result accessor to use the given [baseAccessor] to access a | |
125 * list of values that can be passed to the given [generateTaskInputs] to | |
126 * generate a list of task inputs that can be used to access the elements of | |
127 * the input being accessed. | |
128 */ | |
129 ListToMapTaskInput(TaskInput<List<B>> baseAccessor, | |
130 GenerateTaskInputs<B, E> generateTaskInputs) | |
131 : super(baseAccessor, generateTaskInputs); | |
132 | |
133 @override | |
134 TaskInputBuilder<Map<B, E>> createBuilder() => | |
135 new ListToMapTaskInputBuilder<B, E>(this); | |
136 } | |
137 | |
138 /** | |
139 * A [TaskInputBuilder] used to build an input based on a [ListToMapTaskInput]. | |
140 */ | |
141 class ListToMapTaskInputBuilder<B, E> | |
142 extends _ListToCollectionTaskInputBuilder<B, E, Map<B, E>> { | |
143 /** | |
144 * The map being built. | |
145 */ | |
146 Map<B, E> _resultValue; | |
147 | |
148 /** | |
149 * Initialize a newly created task input builder that computes the result | |
150 * specified by the given [input]. | |
151 */ | |
152 ListToMapTaskInputBuilder(ListToMapTaskInput<B, E> input) : super(input); | |
153 | |
154 @override | |
155 void _addResultElement(B baseElement, E resultElement) { | |
156 _resultValue[baseElement] = resultElement; | |
157 } | |
158 | |
159 @override | |
160 void _initResultValue() { | |
161 _resultValue = new HashMap<B, E>(); | |
162 } | |
163 } | |
164 | |
165 /** | |
166 * A mixin-ready implementation of [MapTaskInput]. | |
167 */ | |
168 abstract class MapTaskInputMixin<K, V> implements MapTaskInput<K, V> { | |
169 TaskInput<List /*<E>*/ > toFlattenList( | |
170 BinaryFunction<K, dynamic /*element of V*/, dynamic /*<E>*/ > mapper) { | |
171 return new MapToFlattenListTaskInput<K, dynamic /*element of V*/, dynamic /*
E*/ >( | |
172 this as MapTaskInput<K, List /*<element of V>*/ >, mapper); | |
173 } | |
174 } | |
175 | |
176 /** | |
177 * A [TaskInput] that is computed by the following steps. | |
178 * | |
179 * First the [base] task input is used to compute a [Map]-valued result. | |
180 * The values of the [Map] must be [List]s. | |
181 * | |
182 * The given [mapper] is used to transform each key / value pair of the [Map] | |
183 * into task inputs. | |
184 * | |
185 * Finally, each of the task inputs are used to access analysis results, | |
186 * and the list of the results is used as the input. | |
187 */ | |
188 class MapToFlattenListTaskInput<K, V, E> extends TaskInputImpl<List<E>> { | |
189 final MapTaskInput<K, List<V>> base; | |
190 final BinaryFunction<K, V, E> mapper; | |
191 | |
192 MapToFlattenListTaskInput(this.base, this.mapper); | |
193 | |
194 @override | |
195 TaskInputBuilder<List<E>> createBuilder() { | |
196 return new MapToFlattenListTaskInputBuilder<K, V, E>(base, mapper); | |
197 } | |
198 } | |
199 | |
200 /** | |
201 * The [TaskInputBuilder] for [MapToFlattenListTaskInput]. | |
202 */ | |
203 class MapToFlattenListTaskInputBuilder<K, V, E> | |
204 implements TaskInputBuilder<List<E>> { | |
205 final MapTaskInput<K, List<V>> base; | |
206 final BinaryFunction<K, V, E> mapper; | |
207 | |
208 TaskInputBuilder currentBuilder; | |
209 Map<K, List<V>> baseMap; | |
210 Iterator<K> keyIterator; | |
211 Iterator<V> valueIterator; | |
212 | |
213 final List<E> inputValue = <E>[]; | |
214 | |
215 MapToFlattenListTaskInputBuilder(this.base, this.mapper) { | |
216 currentBuilder = base.createBuilder(); | |
217 } | |
218 | |
219 @override | |
220 ResultDescriptor get currentResult { | |
221 if (currentBuilder == null) { | |
222 return null; | |
223 } | |
224 return currentBuilder.currentResult; | |
225 } | |
226 | |
227 AnalysisTarget get currentTarget { | |
228 if (currentBuilder == null) { | |
229 return null; | |
230 } | |
231 return currentBuilder.currentTarget; | |
232 } | |
233 | |
234 @override | |
235 void set currentValue(Object value) { | |
236 if (currentBuilder == null) { | |
237 throw new StateError( | |
238 'Cannot set the result value when there is no current result'); | |
239 } | |
240 currentBuilder.currentValue = value; | |
241 } | |
242 | |
243 @override | |
244 void currentValueNotAvailable() { | |
245 if (currentBuilder == null) { | |
246 throw new StateError( | |
247 'Cannot set the result value when there is no current result'); | |
248 } | |
249 currentBuilder.currentValueNotAvailable(); | |
250 } | |
251 | |
252 @override | |
253 bool moveNext() { | |
254 // Prepare base Map. | |
255 if (baseMap == null) { | |
256 if (currentBuilder.moveNext()) { | |
257 return true; | |
258 } | |
259 baseMap = currentBuilder.inputValue; | |
260 if (baseMap == null) { | |
261 // No base map could be computed due to a circular dependency. Use an | |
262 // empty map so that no further results will be computed. | |
263 baseMap = {}; | |
264 } | |
265 keyIterator = baseMap.keys.iterator; | |
266 // Done with this builder. | |
267 currentBuilder = null; | |
268 } | |
269 // Prepare the next result value. | |
270 if (currentBuilder != null) { | |
271 if (currentBuilder.moveNext()) { | |
272 return true; | |
273 } | |
274 // Add the result value for the current Map key/value. | |
275 E resultValue = currentBuilder.inputValue; | |
276 if (resultValue != null) { | |
277 inputValue.add(resultValue); | |
278 } | |
279 // Done with this builder. | |
280 currentBuilder = null; | |
281 } | |
282 // Move to the next Map value. | |
283 if (valueIterator != null && valueIterator.moveNext()) { | |
284 K key = keyIterator.current; | |
285 V value = valueIterator.current; | |
286 currentBuilder = mapper(key, value).createBuilder(); | |
287 return moveNext(); | |
288 } | |
289 // Move to the next Map key. | |
290 if (keyIterator.moveNext()) { | |
291 K key = keyIterator.current; | |
292 valueIterator = baseMap[key].iterator; | |
293 return moveNext(); | |
294 } | |
295 // No more Map values/keys to transform. | |
296 return false; | |
297 } | |
298 } | |
299 | |
300 /** | |
301 * An input to an [AnalysisTask] that is computed by mapping the value of | |
302 * another task input to a list of values. | |
303 */ | |
304 class ObjectToListTaskInput<E> extends TaskInputImpl<List<E>> with ListTaskInput
Mixin<E> | |
305 implements ListTaskInput<E> { | |
306 /** | |
307 * The input used to compute the value to be mapped. | |
308 */ | |
309 final TaskInput baseInput; | |
310 | |
311 /** | |
312 * The function used to map the value of the base input to the list of values. | |
313 */ | |
314 final Mapper<Object, List<E>> mapper; | |
315 | |
316 /** | |
317 * Initialize a newly created task input that computes the input by accessing | |
318 * the given [result] associated with the given [target]. | |
319 */ | |
320 ObjectToListTaskInput(this.baseInput, this.mapper); | |
321 | |
322 @override | |
323 TaskInputBuilder<List<E>> createBuilder() => | |
324 new ObjectToListTaskInputBuilder<E>(this); | |
325 | |
326 @override | |
327 ListTaskInput /*<V>*/ toListOf(ResultDescriptor /*<V>*/ valueResult) { | |
328 return new ListToListTaskInput<E, dynamic /*V*/ >( | |
329 this, valueResult.of as dynamic); | |
330 } | |
331 | |
332 @override | |
333 MapTaskInput<AnalysisTarget, dynamic /*V*/ > toMapOf( | |
334 ResultDescriptor /*<V>*/ valueResult) { | |
335 return new ListToMapTaskInput<AnalysisTarget, dynamic /*V*/ >( | |
336 this as dynamic, valueResult.of); | |
337 } | |
338 } | |
339 | |
340 /** | |
341 * A [TaskInputBuilder] used to build an input based on a [SimpleTaskInput]. | |
342 */ | |
343 class ObjectToListTaskInputBuilder<E> implements TaskInputBuilder<List<E>> { | |
344 /** | |
345 * The input being built. | |
346 */ | |
347 final ObjectToListTaskInput<E> input; | |
348 | |
349 /** | |
350 * The builder created by the input. | |
351 */ | |
352 TaskInputBuilder builder; | |
353 | |
354 /** | |
355 * The value of the input being built, or `null` if the value hasn't been set | |
356 * yet or if no result is available ([currentValueNotAvailable] was called). | |
357 */ | |
358 List<E> _inputValue = null; | |
359 | |
360 /** | |
361 * Initialize a newly created task input builder that computes the result | |
362 * specified by the given [input]. | |
363 */ | |
364 ObjectToListTaskInputBuilder(this.input) { | |
365 builder = input.baseInput.createBuilder(); | |
366 } | |
367 | |
368 @override | |
369 ResultDescriptor get currentResult { | |
370 if (builder == null) { | |
371 return null; | |
372 } | |
373 return builder.currentResult; | |
374 } | |
375 | |
376 @override | |
377 AnalysisTarget get currentTarget { | |
378 if (builder == null) { | |
379 return null; | |
380 } | |
381 return builder.currentTarget; | |
382 } | |
383 | |
384 @override | |
385 void set currentValue(Object value) { | |
386 if (builder == null) { | |
387 throw new StateError( | |
388 'Cannot set the result value when there is no current result'); | |
389 } | |
390 builder.currentValue = value; | |
391 } | |
392 | |
393 @override | |
394 List<E> get inputValue { | |
395 if (builder != null) { | |
396 throw new StateError('Result value has not been created'); | |
397 } | |
398 return _inputValue; | |
399 } | |
400 | |
401 @override | |
402 void currentValueNotAvailable() { | |
403 if (builder == null) { | |
404 throw new StateError( | |
405 'Cannot set the result value when there is no current result'); | |
406 } | |
407 builder.currentValueNotAvailable(); | |
408 } | |
409 | |
410 @override | |
411 bool moveNext() { | |
412 if (builder == null) { | |
413 return false; | |
414 } else if (builder.moveNext()) { | |
415 return true; | |
416 } else { | |
417 // This might not be the right semantics. If the value could not be | |
418 // computed then we pass the resulting `null` in to the mapper function. | |
419 // Unfortunately, we cannot tell the difference between a `null` that's | |
420 // there because no value could be computed and a `null` that's there | |
421 // because that's what *was* computed. | |
422 _inputValue = input.mapper(builder.inputValue); | |
423 builder = null; | |
424 return false; | |
425 } | |
426 } | |
427 } | |
428 | |
429 /** | |
430 * An input to an [AnalysisTask] that is computed by accessing a single result | |
431 * defined on a single target. | |
432 */ | |
433 class SimpleTaskInput<V> extends TaskInputImpl<V> { | |
434 /** | |
435 * The target on which the result is defined. | |
436 */ | |
437 final AnalysisTarget target; | |
438 | |
439 /** | |
440 * The result to be accessed. | |
441 */ | |
442 final ResultDescriptor<V> result; | |
443 | |
444 /** | |
445 * Initialize a newly created task input that computes the input by accessing | |
446 * the given [result] associated with the given [target]. | |
447 */ | |
448 SimpleTaskInput(this.target, this.result); | |
449 | |
450 @override | |
451 TaskInputBuilder<V> createBuilder() => new SimpleTaskInputBuilder<V>(this); | |
452 } | |
453 | |
454 /** | |
455 * A [TaskInputBuilder] used to build an input based on a [SimpleTaskInput]. | |
456 */ | |
457 class SimpleTaskInputBuilder<V> implements TaskInputBuilder<V> { | |
458 /** | |
459 * The state value indicating that the builder is positioned before the single | |
460 * result. | |
461 */ | |
462 static const _BEFORE = -1; | |
463 | |
464 /** | |
465 * The state value indicating that the builder is positioned at the single | |
466 * result. | |
467 */ | |
468 static const _AT = 0; | |
469 | |
470 /** | |
471 * The state value indicating that the builder is positioned after the single | |
472 * result. | |
473 */ | |
474 static const _AFTER = 1; | |
475 | |
476 /** | |
477 * The input being built. | |
478 */ | |
479 final SimpleTaskInput<V> input; | |
480 | |
481 /** | |
482 * The value of the input being built. `null` if the value hasn't been set | |
483 * yet, or if no result is available ([currentValueNotAvailable] was called). | |
484 */ | |
485 V _resultValue = null; | |
486 | |
487 /** | |
488 * The state of the builder. | |
489 */ | |
490 int _state = _BEFORE; | |
491 | |
492 /** | |
493 * A flag indicating whether the result value was explicitly set. | |
494 */ | |
495 bool _resultSet = false; | |
496 | |
497 /** | |
498 * Initialize a newly created task input builder that computes the result | |
499 * specified by the given [input]. | |
500 */ | |
501 SimpleTaskInputBuilder(this.input); | |
502 | |
503 @override | |
504 ResultDescriptor get currentResult => _state == _AT ? input.result : null; | |
505 | |
506 @override | |
507 AnalysisTarget get currentTarget => _state == _AT ? input.target : null; | |
508 | |
509 @override | |
510 void set currentValue(Object value) { | |
511 if (_state != _AT) { | |
512 throw new StateError( | |
513 'Cannot set the result value when there is no current result'); | |
514 } | |
515 _resultValue = value as V; | |
516 _resultSet = true; | |
517 } | |
518 | |
519 @override | |
520 V get inputValue { | |
521 if (_state != _AFTER) { | |
522 throw new StateError('Result value has not been created'); | |
523 } | |
524 return _resultValue; | |
525 } | |
526 | |
527 @override | |
528 void currentValueNotAvailable() { | |
529 if (_state != _AT) { | |
530 throw new StateError( | |
531 'Cannot set the result value when there is no current result'); | |
532 } | |
533 _resultValue = null; | |
534 _resultSet = true; | |
535 } | |
536 | |
537 @override | |
538 bool moveNext() { | |
539 if (_state == _BEFORE) { | |
540 _state = _AT; | |
541 return true; | |
542 } else { | |
543 if (!_resultSet) { | |
544 throw new StateError( | |
545 'The value of the current result must be set before moving to the ne
xt result.'); | |
546 } | |
547 _state = _AFTER; | |
548 return false; | |
549 } | |
550 } | |
551 } | |
552 | |
553 abstract class TaskInputImpl<V> implements TaskInput<V> { | |
554 @override | |
555 ListTaskInput /*<E>*/ mappedToList(List /*<E>*/ mapper(V value)) { | |
556 return new ObjectToListTaskInput(this, mapper); | |
557 } | |
558 } | |
559 | |
560 /** | |
561 * A [TaskInputBuilder] used to build an input based on one or more other task | |
562 * inputs. The task inputs to be built are specified by a table mapping the name | |
563 * of the input to the task used to access the input's value. | |
564 */ | |
565 class TopLevelTaskInputBuilder | |
566 implements TaskInputBuilder<Map<String, Object>> { | |
567 /** | |
568 * The descriptors describing the inputs to be built. | |
569 */ | |
570 final Map<String, TaskInput> inputDescriptors; | |
571 | |
572 /** | |
573 * The names of the inputs. There are the keys from the [inputDescriptors] in | |
574 * an indexable form. | |
575 */ | |
576 List<String> inputNames; | |
577 | |
578 /** | |
579 * The index of the input name associated with the current result and target. | |
580 */ | |
581 int nameIndex = -1; | |
582 | |
583 /** | |
584 * The builder used to build the current result. | |
585 */ | |
586 TaskInputBuilder currentBuilder; | |
587 | |
588 /** | |
589 * The inputs that are being or have been built. The map will be incomplete | |
590 * unless the method [moveNext] returns `false`. | |
591 */ | |
592 final Map<String, Object> inputs = new HashMap<String, Object>(); | |
593 | |
594 /** | |
595 * Initialize a newly created task input builder to build the inputs described | |
596 * by the given [inputDescriptors]. | |
597 */ | |
598 TopLevelTaskInputBuilder(this.inputDescriptors) { | |
599 inputNames = inputDescriptors.keys.toList(); | |
600 } | |
601 | |
602 @override | |
603 ResultDescriptor get currentResult { | |
604 if (currentBuilder == null) { | |
605 return null; | |
606 } | |
607 return currentBuilder.currentResult; | |
608 } | |
609 | |
610 @override | |
611 AnalysisTarget get currentTarget { | |
612 if (currentBuilder == null) { | |
613 return null; | |
614 } | |
615 return currentBuilder.currentTarget; | |
616 } | |
617 | |
618 @override | |
619 void set currentValue(Object value) { | |
620 if (currentBuilder == null) { | |
621 throw new StateError( | |
622 'Cannot set the result value when there is no current result'); | |
623 } | |
624 currentBuilder.currentValue = value; | |
625 } | |
626 | |
627 @override | |
628 Map<String, Object> get inputValue { | |
629 if (nameIndex < inputNames.length) { | |
630 throw new StateError('Result value has not been created'); | |
631 } | |
632 return inputs; | |
633 } | |
634 | |
635 /** | |
636 * Assuming that there is a current input, return its name. | |
637 */ | |
638 String get _currentName => inputNames[nameIndex]; | |
639 | |
640 @override | |
641 void currentValueNotAvailable() { | |
642 if (currentBuilder == null) { | |
643 throw new StateError( | |
644 'Cannot set the result value when there is no current result'); | |
645 } | |
646 currentBuilder.currentValueNotAvailable(); | |
647 } | |
648 | |
649 @override | |
650 bool moveNext() { | |
651 if (nameIndex >= inputNames.length) { | |
652 // We have already computed all of the results, so just return false. | |
653 return false; | |
654 } | |
655 if (nameIndex < 0) { | |
656 // This is the first time moveNext has been invoked, so we just determine | |
657 // whether there are any results to be computed. | |
658 nameIndex = 0; | |
659 } else { | |
660 if (currentBuilder.moveNext()) { | |
661 // We are still working on building the value associated with the | |
662 // current name. | |
663 return true; | |
664 } | |
665 if (currentBuilder.inputValue != null) { | |
666 inputs[_currentName] = currentBuilder.inputValue; | |
667 } | |
668 nameIndex++; | |
669 } | |
670 if (nameIndex >= inputNames.length) { | |
671 // There is no next value, so we're done. | |
672 return false; | |
673 } | |
674 currentBuilder = inputDescriptors[_currentName].createBuilder(); | |
675 // NOTE: This assumes that every builder will require at least one result | |
676 // value to be created. If that assumption is every broken, this method will | |
677 // need to be changed to advance until we find a builder that does require | |
678 // a result to be computed (or run out of builders). | |
679 return currentBuilder.moveNext(); | |
680 } | |
681 } | |
682 | |
683 /** | |
684 * An input to an [AnalysisTask] that is computed by the following steps. First | |
685 * another (base) task input is used to compute a [List]-valued result. An input | |
686 * generator function is then used to map each element of that list to a task | |
687 * input. Finally, each of the task inputs are used to access analysis results, | |
688 * and a collection of the analysis results is used as the input to the task. | |
689 */ | |
690 abstract class _ListToCollectionTaskInput<B, E, C> extends TaskInputImpl<C> { | |
691 /** | |
692 * The accessor used to access the list of elements being mapped. | |
693 */ | |
694 final TaskInput<List<B>> baseAccessor; | |
695 | |
696 /** | |
697 * The function used to convert an element in the list returned by the | |
698 * [baseAccessor] to a task input. | |
699 */ | |
700 final GenerateTaskInputs<B, E> generateTaskInputs; | |
701 | |
702 /** | |
703 * Initialize a result accessor to use the given [baseAccessor] to access a | |
704 * list of values that can be passed to the given [generateTaskInputs] to | |
705 * generate a list of task inputs that can be used to access the elements of | |
706 * the input being accessed. | |
707 */ | |
708 _ListToCollectionTaskInput(this.baseAccessor, this.generateTaskInputs); | |
709 } | |
710 | |
711 /** | |
712 * A [TaskInputBuilder] used to build an [_ListToCollectionTaskInput]. | |
713 */ | |
714 abstract class _ListToCollectionTaskInputBuilder<B, E, C> | |
715 implements TaskInputBuilder<C> { | |
716 /** | |
717 * The input being built. | |
718 */ | |
719 final _ListToCollectionTaskInput<B, E, C> input; | |
720 | |
721 /** | |
722 * The builder used to build the current result. | |
723 */ | |
724 TaskInputBuilder currentBuilder; | |
725 | |
726 /** | |
727 * The list of values computed by the [input]'s base accessor. | |
728 */ | |
729 List<B> _baseList = null; | |
730 | |
731 /** | |
732 * The index in the [_baseList] of the value for which a value is currently | |
733 * being built. | |
734 */ | |
735 int _baseListIndex = -1; | |
736 | |
737 /** | |
738 * The element of the [_baseList] for which a value is currently being built. | |
739 */ | |
740 B _baseListElement; | |
741 | |
742 /** | |
743 * Initialize a newly created task input builder that computes the result | |
744 * specified by the given [input]. | |
745 */ | |
746 _ListToCollectionTaskInputBuilder(this.input); | |
747 | |
748 @override | |
749 ResultDescriptor get currentResult { | |
750 if (currentBuilder == null) { | |
751 return null; | |
752 } | |
753 return currentBuilder.currentResult; | |
754 } | |
755 | |
756 @override | |
757 AnalysisTarget get currentTarget { | |
758 if (currentBuilder == null) { | |
759 return null; | |
760 } | |
761 return currentBuilder.currentTarget; | |
762 } | |
763 | |
764 @override | |
765 void set currentValue(Object value) { | |
766 if (currentBuilder == null) { | |
767 throw new StateError( | |
768 'Cannot set the result value when there is no current result'); | |
769 } | |
770 currentBuilder.currentValue = value; | |
771 } | |
772 | |
773 @override | |
774 C get inputValue { | |
775 if (currentBuilder != null || _resultValue == null) { | |
776 throw new StateError('Result value has not been created'); | |
777 } | |
778 return _resultValue; | |
779 } | |
780 | |
781 /** | |
782 * The list of values being built. | |
783 */ | |
784 C get _resultValue; | |
785 | |
786 @override | |
787 void currentValueNotAvailable() { | |
788 if (currentBuilder == null) { | |
789 throw new StateError( | |
790 'Cannot set the result value when there is no current result'); | |
791 } | |
792 currentBuilder.currentValueNotAvailable(); | |
793 } | |
794 | |
795 @override | |
796 bool moveNext() { | |
797 if (currentBuilder == null) { | |
798 if (_resultValue == null) { | |
799 // This is the first time moveNext has been invoked, so start by | |
800 // computing the list of values from which the results will be derived. | |
801 currentBuilder = input.baseAccessor.createBuilder(); | |
802 return currentBuilder.moveNext(); | |
803 } else { | |
804 // We have already computed all of the results, so just return false. | |
805 return false; | |
806 } | |
807 } | |
808 if (currentBuilder.moveNext()) { | |
809 return true; | |
810 } | |
811 if (_resultValue == null) { | |
812 // We have finished computing the list of values from which the results | |
813 // will be derived. | |
814 _baseList = currentBuilder.inputValue; | |
815 if (_baseList == null) { | |
816 // No base list could be computed due to a circular dependency. Use an | |
817 // empty list so that no further results will be computed. | |
818 _baseList = []; | |
819 } | |
820 _baseListIndex = 0; | |
821 _initResultValue(); | |
822 } else { | |
823 // We have finished computing one of the elements in the result list. | |
824 if (currentBuilder.inputValue != null) { | |
825 _addResultElement(_baseListElement, currentBuilder.inputValue); | |
826 } | |
827 _baseListIndex++; | |
828 } | |
829 if (_baseListIndex >= _baseList.length) { | |
830 currentBuilder = null; | |
831 return false; | |
832 } | |
833 _baseListElement = _baseList[_baseListIndex]; | |
834 currentBuilder = input.generateTaskInputs(_baseListElement).createBuilder(); | |
835 return currentBuilder.moveNext(); | |
836 } | |
837 | |
838 void _addResultElement(B baseElement, E resultElement); | |
839 void _initResultValue(); | |
840 } | |
OLD | NEW |