OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011, 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 part of dart._collection.dev; | |
6 | |
7 /** | |
8 * Marker interface for [Iterable] subclasses that have an efficient | |
9 * [length] implementation. | |
10 */ | |
11 abstract class EfficientLength { | |
12 /** | |
13 * Returns the number of elements in the iterable. | |
14 * | |
15 * This is an efficient operation that doesn't require iterating through | |
16 * the elements. | |
17 */ | |
18 int get length; | |
19 } | |
20 | |
21 /** | |
22 * An [Iterable] for classes that have efficient [length] and [elementAt]. | |
23 * | |
24 * All other methods are implemented in terms of [length] and [elementAt], | |
25 * including [iterator]. | |
26 */ | |
27 abstract class ListIterable<E> extends IterableBase<E> | |
28 implements EfficientLength { | |
29 int get length; | |
30 E elementAt(int i); | |
31 | |
32 const ListIterable(); | |
33 | |
34 Iterator<E> get iterator => new ListIterator<E>(this); | |
35 | |
36 void forEach(void action(E element)) { | |
37 int length = this.length; | |
38 for (int i = 0; i < length; i++) { | |
39 action(elementAt(i)); | |
40 if (length != this.length) { | |
41 throw new ConcurrentModificationError(this); | |
42 } | |
43 } | |
44 } | |
45 | |
46 bool get isEmpty => length == 0; | |
47 | |
48 E get first { | |
49 if (length == 0) throw new StateError("No elements"); | |
50 return elementAt(0); | |
51 } | |
52 | |
53 E get last { | |
54 if (length == 0) throw new StateError("No elements"); | |
55 return elementAt(length - 1); | |
56 } | |
57 | |
58 E get single { | |
59 if (length == 0) throw new StateError("No elements"); | |
60 if (length > 1) throw new StateError("Too many elements"); | |
61 return elementAt(0); | |
62 } | |
63 | |
64 bool contains(Object element) { | |
65 int length = this.length; | |
66 for (int i = 0; i < length; i++) { | |
67 if (elementAt(i) == element) return true; | |
68 if (length != this.length) { | |
69 throw new ConcurrentModificationError(this); | |
70 } | |
71 } | |
72 return false; | |
73 } | |
74 | |
75 bool every(bool test(E element)) { | |
76 int length = this.length; | |
77 for (int i = 0; i < length; i++) { | |
78 if (!test(elementAt(i))) return false; | |
79 if (length != this.length) { | |
80 throw new ConcurrentModificationError(this); | |
81 } | |
82 } | |
83 return true; | |
84 } | |
85 | |
86 bool any(bool test(E element)) { | |
87 int length = this.length; | |
88 for (int i = 0; i < length; i++) { | |
89 if (test(elementAt(i))) return true; | |
90 if (length != this.length) { | |
91 throw new ConcurrentModificationError(this); | |
92 } | |
93 } | |
94 return false; | |
95 } | |
96 | |
97 dynamic firstWhere(bool test(E element), { Object orElse() }) { | |
98 int length = this.length; | |
99 for (int i = 0; i < length; i++) { | |
100 E element = elementAt(i); | |
101 if (test(element)) return element; | |
102 if (length != this.length) { | |
103 throw new ConcurrentModificationError(this); | |
104 } | |
105 } | |
106 if (orElse != null) return orElse(); | |
107 throw new StateError("No matching element"); | |
108 } | |
109 | |
110 dynamic lastWhere(bool test(E element), { Object orElse() }) { | |
111 int length = this.length; | |
112 for (int i = length - 1; i >= 0; i--) { | |
113 E element = elementAt(i); | |
114 if (test(element)) return element; | |
115 if (length != this.length) { | |
116 throw new ConcurrentModificationError(this); | |
117 } | |
118 } | |
119 if (orElse != null) return orElse(); | |
120 throw new StateError("No matching element"); | |
121 } | |
122 | |
123 E singleWhere(bool test(E element)) { | |
124 int length = this.length; | |
125 E match = null; | |
126 bool matchFound = false; | |
127 for (int i = 0; i < length; i++) { | |
128 E element = elementAt(i); | |
129 if (test(element)) { | |
130 if (matchFound) { | |
131 throw new StateError("More than one matching element"); | |
132 } | |
133 matchFound = true; | |
134 match = element; | |
135 } | |
136 if (length != this.length) { | |
137 throw new ConcurrentModificationError(this); | |
138 } | |
139 } | |
140 if (matchFound) return match; | |
141 throw new StateError("No matching element"); | |
142 } | |
143 | |
144 String join([String separator = ""]) { | |
145 int length = this.length; | |
146 if (!separator.isEmpty) { | |
147 if (length == 0) return ""; | |
148 String first = "${elementAt(0)}"; | |
149 if (length != this.length) { | |
150 throw new ConcurrentModificationError(this); | |
151 } | |
152 StringBuffer buffer = new StringBuffer(first); | |
153 for (int i = 1; i < length; i++) { | |
154 buffer.write(separator); | |
155 buffer.write(elementAt(i)); | |
156 if (length != this.length) { | |
157 throw new ConcurrentModificationError(this); | |
158 } | |
159 } | |
160 return buffer.toString(); | |
161 } else { | |
162 StringBuffer buffer = new StringBuffer(); | |
163 for (int i = 0; i < length; i++) { | |
164 buffer.write(elementAt(i)); | |
165 if (length != this.length) { | |
166 throw new ConcurrentModificationError(this); | |
167 } | |
168 } | |
169 return buffer.toString(); | |
170 } | |
171 } | |
172 | |
173 Iterable<E> where(bool test(E element)) => super.where(test); | |
174 | |
175 Iterable map(f(E element)) => new MappedListIterable(this, f); | |
176 | |
177 E reduce(E combine(var value, E element)) { | |
178 if (length == 0) throw new StateError("No elements"); | |
179 E value = elementAt(0); | |
180 for (int i = 1; i < length; i++) { | |
181 value = combine(value, elementAt(i)); | |
182 } | |
183 return value; | |
184 } | |
185 | |
186 fold(var initialValue, combine(var previousValue, E element)) { | |
187 var value = initialValue; | |
188 int length = this.length; | |
189 for (int i = 0; i < length; i++) { | |
190 value = combine(value, elementAt(i)); | |
191 if (length != this.length) { | |
192 throw new ConcurrentModificationError(this); | |
193 } | |
194 } | |
195 return value; | |
196 } | |
197 | |
198 Iterable<E> skip(int count) => new SubListIterable(this, count, null); | |
199 | |
200 Iterable<E> skipWhile(bool test(E element)) => super.skipWhile(test); | |
201 | |
202 Iterable<E> take(int count) => new SubListIterable(this, 0, count); | |
203 | |
204 Iterable<E> takeWhile(bool test(E element)) => super.takeWhile(test); | |
205 | |
206 List<E> toList({ bool growable: true }) { | |
207 List<E> result; | |
208 if (growable) { | |
209 result = new List<E>()..length = length; | |
210 } else { | |
211 result = new List<E>(length); | |
212 } | |
213 for (int i = 0; i < length; i++) { | |
214 result[i] = elementAt(i); | |
215 } | |
216 return result; | |
217 } | |
218 | |
219 Set<E> toSet() { | |
220 Set<E> result = new Set<E>(); | |
221 for (int i = 0; i < length; i++) { | |
222 result.add(elementAt(i)); | |
223 } | |
224 return result; | |
225 } | |
226 } | |
227 | |
228 class SubListIterable<E> extends ListIterable<E> { | |
229 final Iterable<E> _iterable; | |
230 final int _start; | |
231 /** If null, represents the length of the iterable. */ | |
232 final int _endOrLength; | |
233 | |
234 SubListIterable(this._iterable, this._start, this._endOrLength) { | |
235 if (_start < 0) { | |
236 throw new RangeError.value(_start); | |
237 } | |
238 if (_endOrLength != null) { | |
239 if (_endOrLength < 0) { | |
240 throw new RangeError.value(_endOrLength); | |
241 } | |
242 if (_start > _endOrLength) { | |
243 throw new RangeError.range(_start, 0, _endOrLength); | |
244 } | |
245 } | |
246 } | |
247 | |
248 int get _endIndex { | |
249 int length = _iterable.length; | |
250 if (_endOrLength == null || _endOrLength > length) return length; | |
251 return _endOrLength; | |
252 } | |
253 | |
254 int get _startIndex { | |
255 int length = _iterable.length; | |
256 if (_start > length) return length; | |
257 return _start; | |
258 } | |
259 | |
260 int get length { | |
261 int length = _iterable.length; | |
262 if (_start >= length) return 0; | |
263 if (_endOrLength == null || _endOrLength >= length) { | |
264 return length - _start; | |
265 } | |
266 return _endOrLength - _start; | |
267 } | |
268 | |
269 E elementAt(int index) { | |
270 int realIndex = _startIndex + index; | |
271 if (index < 0 || realIndex >= _endIndex) { | |
272 throw new RangeError.range(index, 0, length); | |
273 } | |
274 return _iterable.elementAt(realIndex); | |
275 } | |
276 | |
277 Iterable<E> skip(int count) { | |
278 if (count < 0) throw new RangeError.value(count); | |
279 return new SubListIterable(_iterable, _start + count, _endOrLength); | |
280 } | |
281 | |
282 Iterable<E> take(int count) { | |
283 if (count < 0) throw new RangeError.value(count); | |
284 if (_endOrLength == null) { | |
285 return new SubListIterable(_iterable, _start, _start + count); | |
286 } else { | |
287 int newEnd = _start + count; | |
288 if (_endOrLength < newEnd) return this; | |
289 return new SubListIterable(_iterable, _start, newEnd); | |
290 } | |
291 } | |
292 } | |
293 | |
294 /** | |
295 * An [Iterator] that iterates a list-like [Iterable]. | |
296 * | |
297 * All iterations is done in terms of [Iterable.length] and | |
298 * [Iterable.elementAt]. These operations are fast for list-like | |
299 * iterables. | |
300 */ | |
301 class ListIterator<E> implements Iterator<E> { | |
302 final Iterable<E> _iterable; | |
303 final int _length; | |
304 int _index; | |
305 E _current; | |
306 | |
307 ListIterator(Iterable<E> iterable) | |
308 : _iterable = iterable, _length = iterable.length, _index = 0; | |
309 | |
310 E get current => _current; | |
311 | |
312 bool moveNext() { | |
313 int length = _iterable.length; | |
314 if (_length != length) { | |
315 throw new ConcurrentModificationError(_iterable); | |
316 } | |
317 if (_index >= length) { | |
318 _current = null; | |
319 return false; | |
320 } | |
321 _current = _iterable.elementAt(_index); | |
322 _index++; | |
323 return true; | |
324 } | |
325 } | |
326 | |
327 typedef T _Transformation<S, T>(S value); | |
328 | |
329 class MappedIterable<S, T> extends IterableBase<T> { | |
330 final Iterable<S> _iterable; | |
331 final _Transformation<S, T> _f; | |
332 | |
333 factory MappedIterable(Iterable iterable, T function(S value)) { | |
334 if (iterable is EfficientLength) { | |
335 return new EfficientLengthMappedIterable<S, T>(iterable, function); | |
336 } | |
337 return new MappedIterable<S, T>._(iterable, function); | |
338 } | |
339 | |
340 MappedIterable._(this._iterable, T this._f(S element)); | |
341 | |
342 Iterator<T> get iterator => new MappedIterator<S, T>(_iterable.iterator, _f); | |
343 | |
344 // Length related functions are independent of the mapping. | |
345 int get length => _iterable.length; | |
346 bool get isEmpty => _iterable.isEmpty; | |
347 | |
348 // Index based lookup can be done before transforming. | |
349 T get first => _f(_iterable.first); | |
350 T get last => _f(_iterable.last); | |
351 T get single => _f(_iterable.single); | |
352 T elementAt(int index) => _f(_iterable.elementAt(index)); | |
353 } | |
354 | |
355 class EfficientLengthMappedIterable<S, T> extends MappedIterable<S, T> | |
356 implements EfficientLength { | |
357 EfficientLengthMappedIterable(Iterable iterable, T function(S value)) | |
358 : super._(iterable, function); | |
359 } | |
360 | |
361 class MappedIterator<S, T> extends Iterator<T> { | |
362 T _current; | |
363 final Iterator<S> _iterator; | |
364 final _Transformation<S, T> _f; | |
365 | |
366 MappedIterator(this._iterator, T this._f(S element)); | |
367 | |
368 bool moveNext() { | |
369 if (_iterator.moveNext()) { | |
370 _current = _f(_iterator.current); | |
371 return true; | |
372 } | |
373 _current = null; | |
374 return false; | |
375 } | |
376 | |
377 T get current => _current; | |
378 } | |
379 | |
380 /** | |
381 * Specialized alternative to [MappedIterable] for mapped [List]s. | |
382 * | |
383 * Expects efficient `length` and `elementAt` on the source iterable. | |
384 */ | |
385 class MappedListIterable<S, T> extends ListIterable<T> | |
386 implements EfficientLength { | |
387 final Iterable<S> _source; | |
388 final _Transformation<S, T> _f; | |
389 | |
390 MappedListIterable(this._source, T this._f(S value)); | |
391 | |
392 int get length => _source.length; | |
393 T elementAt(int index) => _f(_source.elementAt(index)); | |
394 } | |
395 | |
396 | |
397 typedef bool _ElementPredicate<E>(E element); | |
398 | |
399 class WhereIterable<E> extends IterableBase<E> { | |
400 final Iterable<E> _iterable; | |
401 final _ElementPredicate _f; | |
402 | |
403 WhereIterable(this._iterable, bool this._f(E element)); | |
404 | |
405 Iterator<E> get iterator => new WhereIterator<E>(_iterable.iterator, _f); | |
406 } | |
407 | |
408 class WhereIterator<E> extends Iterator<E> { | |
409 final Iterator<E> _iterator; | |
410 final _ElementPredicate _f; | |
411 | |
412 WhereIterator(this._iterator, bool this._f(E element)); | |
413 | |
414 bool moveNext() { | |
415 while (_iterator.moveNext()) { | |
416 if (_f(_iterator.current)) { | |
417 return true; | |
418 } | |
419 } | |
420 return false; | |
421 } | |
422 | |
423 E get current => _iterator.current; | |
424 } | |
425 | |
426 typedef Iterable<T> _ExpandFunction<S, T>(S sourceElement); | |
427 | |
428 class ExpandIterable<S, T> extends IterableBase<T> { | |
429 final Iterable<S> _iterable; | |
430 final _ExpandFunction _f; | |
431 | |
432 ExpandIterable(this._iterable, Iterable<T> this._f(S element)); | |
433 | |
434 Iterator<T> get iterator => new ExpandIterator<S, T>(_iterable.iterator, _f); | |
435 } | |
436 | |
437 class ExpandIterator<S, T> implements Iterator<T> { | |
438 final Iterator<S> _iterator; | |
439 final _ExpandFunction _f; | |
440 // Initialize _currentExpansion to an empty iterable. A null value | |
441 // marks the end of iteration, and we don't want to call _f before | |
442 // the first moveNext call. | |
443 Iterator<T> _currentExpansion = const EmptyIterator(); | |
444 T _current; | |
445 | |
446 ExpandIterator(this._iterator, Iterable<T> this._f(S element)); | |
447 | |
448 void _nextExpansion() { | |
449 } | |
450 | |
451 T get current => _current; | |
452 | |
453 bool moveNext() { | |
454 if (_currentExpansion == null) return false; | |
455 while (!_currentExpansion.moveNext()) { | |
456 _current = null; | |
457 if (_iterator.moveNext()) { | |
458 // If _f throws, this ends iteration. Otherwise _currentExpansion and | |
459 // _current will be set again below. | |
460 _currentExpansion = null; | |
461 _currentExpansion = _f(_iterator.current).iterator; | |
462 } else { | |
463 return false; | |
464 } | |
465 } | |
466 _current = _currentExpansion.current; | |
467 return true; | |
468 } | |
469 } | |
470 | |
471 class TakeIterable<E> extends IterableBase<E> { | |
472 final Iterable<E> _iterable; | |
473 final int _takeCount; | |
474 | |
475 factory TakeIterable(Iterable<E> iterable, int takeCount) { | |
476 if (takeCount is! int || takeCount < 0) { | |
477 throw new ArgumentError(takeCount); | |
478 } | |
479 if (iterable is EfficientLength) { | |
480 return new EfficientLengthTakeIterable<E>(iterable, takeCount); | |
481 } | |
482 return new TakeIterable<E>._(iterable, takeCount); | |
483 } | |
484 | |
485 TakeIterable._(this._iterable, this._takeCount); | |
486 | |
487 Iterator<E> get iterator { | |
488 return new TakeIterator<E>(_iterable.iterator, _takeCount); | |
489 } | |
490 } | |
491 | |
492 class EfficientLengthTakeIterable<E> extends TakeIterable<E> | |
493 implements EfficientLength { | |
494 EfficientLengthTakeIterable(Iterable<E> iterable, int takeCount) | |
495 : super._(iterable, takeCount); | |
496 | |
497 int get length { | |
498 int iterableLength = _iterable.length; | |
499 if (iterableLength > _takeCount) return _takeCount; | |
500 return iterableLength; | |
501 } | |
502 } | |
503 | |
504 | |
505 class TakeIterator<E> extends Iterator<E> { | |
506 final Iterator<E> _iterator; | |
507 int _remaining; | |
508 | |
509 TakeIterator(this._iterator, this._remaining) { | |
510 assert(_remaining is int && _remaining >= 0); | |
511 } | |
512 | |
513 bool moveNext() { | |
514 _remaining--; | |
515 if (_remaining >= 0) { | |
516 return _iterator.moveNext(); | |
517 } | |
518 _remaining = -1; | |
519 return false; | |
520 } | |
521 | |
522 E get current { | |
523 if (_remaining < 0) return null; | |
524 return _iterator.current; | |
525 } | |
526 } | |
527 | |
528 class TakeWhileIterable<E> extends IterableBase<E> { | |
529 final Iterable<E> _iterable; | |
530 final _ElementPredicate _f; | |
531 | |
532 TakeWhileIterable(this._iterable, bool this._f(E element)); | |
533 | |
534 Iterator<E> get iterator { | |
535 return new TakeWhileIterator<E>(_iterable.iterator, _f); | |
536 } | |
537 } | |
538 | |
539 class TakeWhileIterator<E> extends Iterator<E> { | |
540 final Iterator<E> _iterator; | |
541 final _ElementPredicate _f; | |
542 bool _isFinished = false; | |
543 | |
544 TakeWhileIterator(this._iterator, bool this._f(E element)); | |
545 | |
546 bool moveNext() { | |
547 if (_isFinished) return false; | |
548 if (!_iterator.moveNext() || !_f(_iterator.current)) { | |
549 _isFinished = true; | |
550 return false; | |
551 } | |
552 return true; | |
553 } | |
554 | |
555 E get current { | |
556 if (_isFinished) return null; | |
557 return _iterator.current; | |
558 } | |
559 } | |
560 | |
561 class SkipIterable<E> extends IterableBase<E> { | |
562 final Iterable<E> _iterable; | |
563 final int _skipCount; | |
564 | |
565 factory SkipIterable(Iterable<E> iterable, int skipCount) { | |
566 if (iterable is EfficientLength) { | |
567 return new EfficientLengthSkipIterable<E>(iterable, skipCount); | |
568 } | |
569 return new SkipIterable<E>._(iterable, skipCount); | |
570 } | |
571 | |
572 SkipIterable._(this._iterable, this._skipCount) { | |
573 if (_skipCount is! int || _skipCount < 0) { | |
574 throw new RangeError(_skipCount); | |
575 } | |
576 } | |
577 | |
578 Iterable<E> skip(int n) { | |
579 if (n is! int || n < 0) { | |
580 throw new RangeError.value(n); | |
581 } | |
582 return new SkipIterable<E>(_iterable, _skipCount + n); | |
583 } | |
584 | |
585 Iterator<E> get iterator { | |
586 return new SkipIterator<E>(_iterable.iterator, _skipCount); | |
587 } | |
588 } | |
589 | |
590 class EfficientLengthSkipIterable<E> extends SkipIterable<E> | |
591 implements EfficientLength { | |
592 EfficientLengthSkipIterable(Iterable<E> iterable, int skipCount) | |
593 : super._(iterable, skipCount); | |
594 | |
595 int get length { | |
596 int length = _iterable.length - _skipCount; | |
597 if (length >= 0) return length; | |
598 return 0; | |
599 } | |
600 } | |
601 | |
602 class SkipIterator<E> extends Iterator<E> { | |
603 final Iterator<E> _iterator; | |
604 int _skipCount; | |
605 | |
606 SkipIterator(this._iterator, this._skipCount) { | |
607 assert(_skipCount is int && _skipCount >= 0); | |
608 } | |
609 | |
610 bool moveNext() { | |
611 for (int i = 0; i < _skipCount; i++) _iterator.moveNext(); | |
612 _skipCount = 0; | |
613 return _iterator.moveNext(); | |
614 } | |
615 | |
616 E get current => _iterator.current; | |
617 } | |
618 | |
619 class SkipWhileIterable<E> extends IterableBase<E> { | |
620 final Iterable<E> _iterable; | |
621 final _ElementPredicate _f; | |
622 | |
623 SkipWhileIterable(this._iterable, bool this._f(E element)); | |
624 | |
625 Iterator<E> get iterator { | |
626 return new SkipWhileIterator<E>(_iterable.iterator, _f); | |
627 } | |
628 } | |
629 | |
630 class SkipWhileIterator<E> extends Iterator<E> { | |
631 final Iterator<E> _iterator; | |
632 final _ElementPredicate _f; | |
633 bool _hasSkipped = false; | |
634 | |
635 SkipWhileIterator(this._iterator, bool this._f(E element)); | |
636 | |
637 bool moveNext() { | |
638 if (!_hasSkipped) { | |
639 _hasSkipped = true; | |
640 while (_iterator.moveNext()) { | |
641 if (!_f(_iterator.current)) return true; | |
642 } | |
643 } | |
644 return _iterator.moveNext(); | |
645 } | |
646 | |
647 E get current => _iterator.current; | |
648 } | |
649 | |
650 /** | |
651 * The always empty [Iterable]. | |
652 */ | |
653 class EmptyIterable<E> extends IterableBase<E> implements EfficientLength { | |
654 const EmptyIterable(); | |
655 | |
656 Iterator<E> get iterator => const EmptyIterator(); | |
657 | |
658 void forEach(void action(E element)) {} | |
659 | |
660 bool get isEmpty => true; | |
661 | |
662 int get length => 0; | |
663 | |
664 E get first { throw new StateError("No elements"); } | |
665 | |
666 E get last { throw new StateError("No elements"); } | |
667 | |
668 E get single { throw new StateError("No elements"); } | |
669 | |
670 E elementAt(int index) { throw new RangeError.value(index); } | |
671 | |
672 bool contains(Object element) => false; | |
673 | |
674 bool every(bool test(E element)) => true; | |
675 | |
676 bool any(bool test(E element)) => false; | |
677 | |
678 E firstWhere(bool test(E element), { E orElse() }) { | |
679 if (orElse != null) return orElse(); | |
680 throw new StateError("No matching element"); | |
681 } | |
682 | |
683 E lastWhere(bool test(E element), { E orElse() }) { | |
684 if (orElse != null) return orElse(); | |
685 throw new StateError("No matching element"); | |
686 } | |
687 | |
688 E singleWhere(bool test(E element), { E orElse() }) { | |
689 if (orElse != null) return orElse(); | |
690 throw new StateError("No matching element"); | |
691 } | |
692 | |
693 String join([String separator = ""]) => ""; | |
694 | |
695 Iterable<E> where(bool test(E element)) => this; | |
696 | |
697 Iterable map(f(E element)) => const EmptyIterable(); | |
698 | |
699 E reduce(E combine(E value, E element)) { | |
700 throw new StateError("No elements"); | |
701 } | |
702 | |
703 fold(var initialValue, combine(var previousValue, E element)) { | |
704 return initialValue; | |
705 } | |
706 | |
707 Iterable<E> skip(int count) { | |
708 if (count < 0) throw new RangeError.value(count); | |
709 return this; | |
710 } | |
711 | |
712 Iterable<E> skipWhile(bool test(E element)) => this; | |
713 | |
714 Iterable<E> take(int count) { | |
715 if (count < 0) throw new RangeError.value(count); | |
716 this; | |
717 } | |
718 | |
719 Iterable<E> takeWhile(bool test(E element)) => this; | |
720 | |
721 List toList({ bool growable: true }) => growable ? <E>[] : new List<E>(0); | |
722 | |
723 Set toSet() => new Set<E>(); | |
724 } | |
725 | |
726 /** The always empty iterator. */ | |
727 class EmptyIterator<E> implements Iterator<E> { | |
728 const EmptyIterator(); | |
729 bool moveNext() => false; | |
730 E get current => null; | |
731 } | |
732 | |
733 /** An [Iterator] that can move in both directions. */ | |
734 abstract class BidirectionalIterator<T> implements Iterator<T> { | |
735 bool movePrevious(); | |
736 } | |
737 | |
738 /** | |
739 * This class provides default implementations for Iterables (including Lists). | |
740 * | |
741 * The uses of this class will be replaced by mixins. | |
742 */ | |
743 class IterableMixinWorkaround { | |
744 // A list to identify cyclic collections during toString() calls. | |
745 static List _toStringList = new List(); | |
746 | |
747 static bool contains(Iterable iterable, var element) { | |
748 for (final e in iterable) { | |
749 if (e == element) return true; | |
750 } | |
751 return false; | |
752 } | |
753 | |
754 static void forEach(Iterable iterable, void f(o)) { | |
755 for (final e in iterable) { | |
756 f(e); | |
757 } | |
758 } | |
759 | |
760 static bool any(Iterable iterable, bool f(o)) { | |
761 for (final e in iterable) { | |
762 if (f(e)) return true; | |
763 } | |
764 return false; | |
765 } | |
766 | |
767 static bool every(Iterable iterable, bool f(o)) { | |
768 for (final e in iterable) { | |
769 if (!f(e)) return false; | |
770 } | |
771 return true; | |
772 } | |
773 | |
774 static dynamic reduce(Iterable iterable, | |
775 dynamic combine(previousValue, element)) { | |
776 Iterator iterator = iterable.iterator; | |
777 if (!iterator.moveNext()) throw new StateError("No elements"); | |
778 var value = iterator.current; | |
779 while (iterator.moveNext()) { | |
780 value = combine(value, iterator.current); | |
781 } | |
782 return value; | |
783 } | |
784 | |
785 static dynamic fold(Iterable iterable, | |
786 dynamic initialValue, | |
787 dynamic combine(dynamic previousValue, element)) { | |
788 for (final element in iterable) { | |
789 initialValue = combine(initialValue, element); | |
790 } | |
791 return initialValue; | |
792 } | |
793 | |
794 /** | |
795 * Removes elements matching [test] from [list]. | |
796 * | |
797 * This is performed in two steps, to avoid exposing an inconsistent state | |
798 * to the [test] function. First the elements to retain are found, and then | |
799 * the original list is updated to contain those elements. | |
800 */ | |
801 static void removeWhereList(List list, bool test(var element)) { | |
802 List retained = []; | |
803 int length = list.length; | |
804 for (int i = 0; i < length; i++) { | |
805 var element = list[i]; | |
806 if (!test(element)) { | |
807 retained.add(element); | |
808 } | |
809 if (length != list.length) { | |
810 throw new ConcurrentModificationError(list); | |
811 } | |
812 } | |
813 if (retained.length == length) return; | |
814 list.length = retained.length; | |
815 for (int i = 0; i < retained.length; i++) { | |
816 list[i] = retained[i]; | |
817 } | |
818 } | |
819 | |
820 static bool isEmpty(Iterable iterable) { | |
821 return !iterable.iterator.moveNext(); | |
822 } | |
823 | |
824 static dynamic first(Iterable iterable) { | |
825 Iterator it = iterable.iterator; | |
826 if (!it.moveNext()) { | |
827 throw new StateError("No elements"); | |
828 } | |
829 return it.current; | |
830 } | |
831 | |
832 static dynamic last(Iterable iterable) { | |
833 Iterator it = iterable.iterator; | |
834 if (!it.moveNext()) { | |
835 throw new StateError("No elements"); | |
836 } | |
837 dynamic result; | |
838 do { | |
839 result = it.current; | |
840 } while(it.moveNext()); | |
841 return result; | |
842 } | |
843 | |
844 static dynamic single(Iterable iterable) { | |
845 Iterator it = iterable.iterator; | |
846 if (!it.moveNext()) throw new StateError("No elements"); | |
847 dynamic result = it.current; | |
848 if (it.moveNext()) throw new StateError("More than one element"); | |
849 return result; | |
850 } | |
851 | |
852 static dynamic firstWhere(Iterable iterable, | |
853 bool test(dynamic value), | |
854 dynamic orElse()) { | |
855 for (dynamic element in iterable) { | |
856 if (test(element)) return element; | |
857 } | |
858 if (orElse != null) return orElse(); | |
859 throw new StateError("No matching element"); | |
860 } | |
861 | |
862 static dynamic lastWhere(Iterable iterable, | |
863 bool test(dynamic value), | |
864 dynamic orElse()) { | |
865 dynamic result = null; | |
866 bool foundMatching = false; | |
867 for (dynamic element in iterable) { | |
868 if (test(element)) { | |
869 result = element; | |
870 foundMatching = true; | |
871 } | |
872 } | |
873 if (foundMatching) return result; | |
874 if (orElse != null) return orElse(); | |
875 throw new StateError("No matching element"); | |
876 } | |
877 | |
878 static dynamic lastWhereList(List list, | |
879 bool test(dynamic value), | |
880 dynamic orElse()) { | |
881 // TODO(floitsch): check that arguments are of correct type? | |
882 for (int i = list.length - 1; i >= 0; i--) { | |
883 dynamic element = list[i]; | |
884 if (test(element)) return element; | |
885 } | |
886 if (orElse != null) return orElse(); | |
887 throw new StateError("No matching element"); | |
888 } | |
889 | |
890 static dynamic singleWhere(Iterable iterable, bool test(dynamic value)) { | |
891 dynamic result = null; | |
892 bool foundMatching = false; | |
893 for (dynamic element in iterable) { | |
894 if (test(element)) { | |
895 if (foundMatching) { | |
896 throw new StateError("More than one matching element"); | |
897 } | |
898 result = element; | |
899 foundMatching = true; | |
900 } | |
901 } | |
902 if (foundMatching) return result; | |
903 throw new StateError("No matching element"); | |
904 } | |
905 | |
906 static dynamic elementAt(Iterable iterable, int index) { | |
907 if (index is! int || index < 0) throw new RangeError.value(index); | |
908 int remaining = index; | |
909 for (dynamic element in iterable) { | |
910 if (remaining == 0) return element; | |
911 remaining--; | |
912 } | |
913 throw new RangeError.value(index); | |
914 } | |
915 | |
916 static String join(Iterable iterable, [String separator]) { | |
917 StringBuffer buffer = new StringBuffer(); | |
918 buffer.writeAll(iterable, separator); | |
919 return buffer.toString(); | |
920 } | |
921 | |
922 static String joinList(List list, [String separator]) { | |
923 if (list.isEmpty) return ""; | |
924 if (list.length == 1) return "${list[0]}"; | |
925 StringBuffer buffer = new StringBuffer(); | |
926 if (separator.isEmpty) { | |
927 for (int i = 0; i < list.length; i++) { | |
928 buffer.write(list[i]); | |
929 } | |
930 } else { | |
931 buffer.write(list[0]); | |
932 for (int i = 1; i < list.length; i++) { | |
933 buffer.write(separator); | |
934 buffer.write(list[i]); | |
935 } | |
936 } | |
937 return buffer.toString(); | |
938 } | |
939 | |
940 static String toStringIterable(Iterable iterable, String leftDelimiter, | |
941 String rightDelimiter) { | |
942 for (int i = 0; i < _toStringList.length; i++) { | |
943 if (identical(_toStringList[i], iterable)) { | |
944 return '$leftDelimiter...$rightDelimiter'; | |
945 } | |
946 } | |
947 | |
948 StringBuffer result = new StringBuffer(); | |
949 try { | |
950 _toStringList.add(iterable); | |
951 result.write(leftDelimiter); | |
952 result.writeAll(iterable, ', '); | |
953 result.write(rightDelimiter); | |
954 } finally { | |
955 assert(identical(_toStringList.last, iterable)); | |
956 _toStringList.removeLast(); | |
957 } | |
958 return result.toString(); | |
959 } | |
960 | |
961 static Iterable where(Iterable iterable, bool f(var element)) { | |
962 return new WhereIterable(iterable, f); | |
963 } | |
964 | |
965 static Iterable map(Iterable iterable, f(var element)) { | |
966 return new MappedIterable(iterable, f); | |
967 } | |
968 | |
969 static Iterable mapList(List list, f(var element)) { | |
970 return new MappedListIterable(list, f); | |
971 } | |
972 | |
973 static Iterable expand(Iterable iterable, Iterable f(var element)) { | |
974 return new ExpandIterable(iterable, f); | |
975 } | |
976 | |
977 static Iterable takeList(List list, int n) { | |
978 // The generic type is currently lost. It will be fixed with mixins. | |
979 return new SubListIterable(list, 0, n); | |
980 } | |
981 | |
982 static Iterable takeWhile(Iterable iterable, bool test(var value)) { | |
983 // The generic type is currently lost. It will be fixed with mixins. | |
984 return new TakeWhileIterable(iterable, test); | |
985 } | |
986 | |
987 static Iterable skipList(List list, int n) { | |
988 // The generic type is currently lost. It will be fixed with mixins. | |
989 return new SubListIterable(list, n, null); | |
990 } | |
991 | |
992 static Iterable skipWhile(Iterable iterable, bool test(var value)) { | |
993 // The generic type is currently lost. It will be fixed with mixins. | |
994 return new SkipWhileIterable(iterable, test); | |
995 } | |
996 | |
997 static Iterable reversedList(List list) { | |
998 return new ReversedListIterable(list); | |
999 } | |
1000 | |
1001 static void sortList(List list, int compare(a, b)) { | |
1002 if (compare == null) compare = Comparable.compare; | |
1003 Sort.sort(list, compare); | |
1004 } | |
1005 | |
1006 static void shuffleList(List list, Random random) { | |
1007 if (random == null) random = new Random(); | |
1008 int length = list.length; | |
1009 while (length > 1) { | |
1010 int pos = random.nextInt(length); | |
1011 length -= 1; | |
1012 var tmp = list[length]; | |
1013 list[length] = list[pos]; | |
1014 list[pos] = tmp; | |
1015 } | |
1016 } | |
1017 | |
1018 static int indexOfList(List list, var element, int start) { | |
1019 return Lists.indexOf(list, element, start, list.length); | |
1020 } | |
1021 | |
1022 static int lastIndexOfList(List list, var element, int start) { | |
1023 if (start == null) start = list.length - 1; | |
1024 return Lists.lastIndexOf(list, element, start); | |
1025 } | |
1026 | |
1027 static void _rangeCheck(List list, int start, int end) { | |
1028 if (start < 0 || start > list.length) { | |
1029 throw new RangeError.range(start, 0, list.length); | |
1030 } | |
1031 if (end < start || end > list.length) { | |
1032 throw new RangeError.range(end, start, list.length); | |
1033 } | |
1034 } | |
1035 | |
1036 static Iterable getRangeList(List list, int start, int end) { | |
1037 _rangeCheck(list, start, end); | |
1038 // The generic type is currently lost. It will be fixed with mixins. | |
1039 return new SubListIterable(list, start, end); | |
1040 } | |
1041 | |
1042 static void setRangeList(List list, int start, int end, | |
1043 Iterable from, int skipCount) { | |
1044 _rangeCheck(list, start, end); | |
1045 int length = end - start; | |
1046 if (length == 0) return; | |
1047 | |
1048 if (skipCount < 0) throw new ArgumentError(skipCount); | |
1049 | |
1050 // TODO(floitsch): Make this accept more. | |
1051 List otherList; | |
1052 int otherStart; | |
1053 if (from is List) { | |
1054 otherList = from; | |
1055 otherStart = skipCount; | |
1056 } else { | |
1057 otherList = from.skip(skipCount).toList(growable: false); | |
1058 otherStart = 0; | |
1059 } | |
1060 if (otherStart + length > otherList.length) { | |
1061 throw new StateError("Not enough elements"); | |
1062 } | |
1063 Lists.copy(otherList, otherStart, list, start, length); | |
1064 } | |
1065 | |
1066 static void replaceRangeList(List list, int start, int end, | |
1067 Iterable iterable) { | |
1068 _rangeCheck(list, start, end); | |
1069 if (iterable is! EfficientLength) { | |
1070 iterable = iterable.toList(); | |
1071 } | |
1072 int removeLength = end - start; | |
1073 int insertLength = iterable.length; | |
1074 if (removeLength >= insertLength) { | |
1075 int delta = removeLength - insertLength; | |
1076 int insertEnd = start + insertLength; | |
1077 int newEnd = list.length - delta; | |
1078 list.setRange(start, insertEnd, iterable); | |
1079 if (delta != 0) { | |
1080 list.setRange(insertEnd, newEnd, list, end); | |
1081 list.length = newEnd; | |
1082 } | |
1083 } else { | |
1084 int delta = insertLength - removeLength; | |
1085 int newLength = list.length + delta; | |
1086 int insertEnd = start + insertLength; // aka. end + delta. | |
1087 list.length = newLength; | |
1088 list.setRange(insertEnd, newLength, list, end); | |
1089 list.setRange(start, insertEnd, iterable); | |
1090 } | |
1091 } | |
1092 | |
1093 static void fillRangeList(List list, int start, int end, fillValue) { | |
1094 _rangeCheck(list, start, end); | |
1095 for (int i = start; i < end; i++) { | |
1096 list[i] = fillValue; | |
1097 } | |
1098 } | |
1099 | |
1100 static void insertAllList(List list, int index, Iterable iterable) { | |
1101 if (index < 0 || index > list.length) { | |
1102 throw new RangeError.range(index, 0, list.length); | |
1103 } | |
1104 if (iterable is! EfficientLength) { | |
1105 iterable = iterable.toList(growable: false); | |
1106 } | |
1107 int insertionLength = iterable.length; | |
1108 list.length += insertionLength; | |
1109 list.setRange(index + insertionLength, list.length, list, index); | |
1110 for (var element in iterable) { | |
1111 list[index++] = element; | |
1112 } | |
1113 } | |
1114 | |
1115 static void setAllList(List list, int index, Iterable iterable) { | |
1116 if (index < 0 || index > list.length) { | |
1117 throw new RangeError.range(index, 0, list.length); | |
1118 } | |
1119 for (var element in iterable) { | |
1120 list[index++] = element; | |
1121 } | |
1122 } | |
1123 | |
1124 static Map<int, dynamic> asMapList(List l) { | |
1125 return new ListMapView(l); | |
1126 } | |
1127 | |
1128 static bool setContainsAll(Set set, Iterable other) { | |
1129 for (var element in other) { | |
1130 if (!set.contains(element)) return false; | |
1131 } | |
1132 return true; | |
1133 } | |
1134 | |
1135 static Set setIntersection(Set set, Set other, Set result) { | |
1136 Set smaller; | |
1137 Set larger; | |
1138 if (set.length < other.length) { | |
1139 smaller = set; | |
1140 larger = other; | |
1141 } else { | |
1142 smaller = other; | |
1143 larger = set; | |
1144 } | |
1145 for (var element in smaller) { | |
1146 if (larger.contains(element)) { | |
1147 result.add(element); | |
1148 } | |
1149 } | |
1150 return result; | |
1151 } | |
1152 | |
1153 static Set setUnion(Set set, Set other, Set result) { | |
1154 result.addAll(set); | |
1155 result.addAll(other); | |
1156 return result; | |
1157 } | |
1158 | |
1159 static Set setDifference(Set set, Set other, Set result) { | |
1160 for (var element in set) { | |
1161 if (!other.contains(element)) { | |
1162 result.add(element); | |
1163 } | |
1164 } | |
1165 return result; | |
1166 } | |
1167 } | |
OLD | NEW |