// Copyright (c) 2011, 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. part of dart._internal; /** * Marker interface for [Iterable] subclasses that have an efficient * [length] implementation. */ abstract class EfficientLength { /** * Returns the number of elements in the iterable. * * This is an efficient operation that doesn't require iterating through * the elements. */ int get length; } /** * An [Iterable] for classes that have efficient [length] and [elementAt]. * * All other methods are implemented in terms of [length] and [elementAt], * including [iterator]. */ abstract class ListIterable extends Iterable implements EfficientLength { int get length; E elementAt(int i); const ListIterable(); Iterator get iterator => new ListIterator(this); void forEach(void action(E element)) { int length = this.length; for (int i = 0; i < length; i++) { action(elementAt(i)); if (length != this.length) { throw new ConcurrentModificationError(this); } } } bool get isEmpty => length == 0; E get first { if (length == 0) throw IterableElementError.noElement(); return elementAt(0); } E get last { if (length == 0) throw IterableElementError.noElement(); return elementAt(length - 1); } E get single { if (length == 0) throw IterableElementError.noElement(); if (length > 1) throw IterableElementError.tooMany(); return elementAt(0); } bool contains(Object element) { int length = this.length; for (int i = 0; i < length; i++) { if (elementAt(i) == element) return true; if (length != this.length) { throw new ConcurrentModificationError(this); } } return false; } bool every(bool test(E element)) { int length = this.length; for (int i = 0; i < length; i++) { if (!test(elementAt(i))) return false; if (length != this.length) { throw new ConcurrentModificationError(this); } } return true; } bool any(bool test(E element)) { int length = this.length; for (int i = 0; i < length; i++) { if (test(elementAt(i))) return true; if (length != this.length) { throw new ConcurrentModificationError(this); } } return false; } E firstWhere(bool test(E element), { E orElse() }) { int length = this.length; for (int i = 0; i < length; i++) { E element = elementAt(i); if (test(element)) return element; if (length != this.length) { throw new ConcurrentModificationError(this); } } if (orElse != null) return orElse(); throw IterableElementError.noElement(); } E lastWhere(bool test(E element), { E orElse() }) { int length = this.length; for (int i = length - 1; i >= 0; i--) { E element = elementAt(i); if (test(element)) return element; if (length != this.length) { throw new ConcurrentModificationError(this); } } if (orElse != null) return orElse(); throw IterableElementError.noElement(); } E singleWhere(bool test(E element)) { int length = this.length; E match = null; bool matchFound = false; for (int i = 0; i < length; i++) { E element = elementAt(i); if (test(element)) { if (matchFound) { throw IterableElementError.tooMany(); } matchFound = true; match = element; } if (length != this.length) { throw new ConcurrentModificationError(this); } } if (matchFound) return match; throw IterableElementError.noElement(); } String join([String separator = ""]) { int length = this.length; if (!separator.isEmpty) { if (length == 0) return ""; String first = "${elementAt(0)}"; if (length != this.length) { throw new ConcurrentModificationError(this); } StringBuffer buffer = new StringBuffer(first); for (int i = 1; i < length; i++) { buffer.write(separator); buffer.write(elementAt(i)); if (length != this.length) { throw new ConcurrentModificationError(this); } } return buffer.toString(); } else { StringBuffer buffer = new StringBuffer(); for (int i = 0; i < length; i++) { buffer.write(elementAt(i)); if (length != this.length) { throw new ConcurrentModificationError(this); } } return buffer.toString(); } } Iterable where(bool test(E element)) => super.where(test); Iterable map(f(E element)) => new MappedListIterable(this, f); E reduce(E combine(var value, E element)) { int length = this.length; if (length == 0) throw IterableElementError.noElement(); E value = elementAt(0); for (int i = 1; i < length; i++) { value = combine(value, elementAt(i)); if (length != this.length) { throw new ConcurrentModificationError(this); } } return value; } fold(var initialValue, combine(var previousValue, E element)) { var value = initialValue; int length = this.length; for (int i = 0; i < length; i++) { value = combine(value, elementAt(i)); if (length != this.length) { throw new ConcurrentModificationError(this); } } return value; } Iterable skip(int count) => new SubListIterable(this, count, null); Iterable skipWhile(bool test(E element)) => super.skipWhile(test); Iterable take(int count) => new SubListIterable(this, 0, count); Iterable takeWhile(bool test(E element)) => super.takeWhile(test); List toList({ bool growable: true }) { List result; if (growable) { result = new List()..length = length; } else { result = new List(length); } for (int i = 0; i < length; i++) { result[i] = elementAt(i); } return result; } Set toSet() { Set result = new Set(); for (int i = 0; i < length; i++) { result.add(elementAt(i)); } return result; } } class SubListIterable extends ListIterable { final Iterable _iterable; // Has efficient length and elementAt. final int _start; /** If null, represents the length of the iterable. */ final int _endOrLength; SubListIterable(this._iterable, this._start, this._endOrLength) { RangeError.checkNotNegative(_start, "start"); if (_endOrLength != null) { RangeError.checkNotNegative(_endOrLength, "end"); if (_start > _endOrLength) { throw new RangeError.range(_start, 0, _endOrLength, "start"); } } } int get _endIndex { int length = _iterable.length; if (_endOrLength == null || _endOrLength > length) return length; return _endOrLength; } int get _startIndex { int length = _iterable.length; if (_start > length) return length; return _start; } int get length { int length = _iterable.length; if (_start >= length) return 0; if (_endOrLength == null || _endOrLength >= length) { return length - _start; } return _endOrLength - _start; } E elementAt(int index) { int realIndex = _startIndex + index; if (index < 0 || realIndex >= _endIndex) { throw new RangeError.index(index, this, "index"); } return _iterable.elementAt(realIndex); } Iterable skip(int count) { RangeError.checkNotNegative(count, "count"); int newStart = _start + count; if (_endOrLength != null && newStart >= _endOrLength) { return new EmptyIterable(); } return new SubListIterable(_iterable, newStart, _endOrLength); } Iterable take(int count) { RangeError.checkNotNegative(count, "count"); if (_endOrLength == null) { return new SubListIterable(_iterable, _start, _start + count); } else { int newEnd = _start + count; if (_endOrLength < newEnd) return this; return new SubListIterable(_iterable, _start, newEnd); } } List toList({bool growable: true}) { int start = _start; int end = _iterable.length; if (_endOrLength != null && _endOrLength < end) end = _endOrLength; int length = end - start; if (length < 0) length = 0; List result = growable ? (new List()..length = length) : new List(length); for (int i = 0; i < length; i++) { result[i] = _iterable.elementAt(start + i); if (_iterable.length < end) throw new ConcurrentModificationError(this); } return result; } } /** * An [Iterator] that iterates a list-like [Iterable]. * * All iterations is done in terms of [Iterable.length] and * [Iterable.elementAt]. These operations are fast for list-like * iterables. */ class ListIterator implements Iterator { final Iterable _iterable; final int _length; int _index; E _current; ListIterator(Iterable iterable) : _iterable = iterable, _length = iterable.length, _index = 0; E get current => _current; bool moveNext() { int length = _iterable.length; if (_length != length) { throw new ConcurrentModificationError(_iterable); } if (_index >= length) { _current = null; return false; } _current = _iterable.elementAt(_index); _index++; return true; } } typedef T _Transformation(S value); class MappedIterable extends Iterable { final Iterable _iterable; final _Transformation _f; factory MappedIterable(Iterable iterable, T function(S value)) { if (iterable is EfficientLength) { return new EfficientLengthMappedIterable(iterable, function); } return new MappedIterable._(iterable, function); } MappedIterable._(this._iterable, T this._f(S element)); Iterator get iterator => new MappedIterator(_iterable.iterator, _f); // Length related functions are independent of the mapping. int get length => _iterable.length; bool get isEmpty => _iterable.isEmpty; // Index based lookup can be done before transforming. T get first => _f(_iterable.first); T get last => _f(_iterable.last); T get single => _f(_iterable.single); T elementAt(int index) => _f(_iterable.elementAt(index)); } class EfficientLengthMappedIterable extends MappedIterable implements EfficientLength { EfficientLengthMappedIterable(Iterable iterable, T function(S value)) : super._(iterable, function); } class MappedIterator extends Iterator { T _current; final Iterator _iterator; final _Transformation _f; MappedIterator(this._iterator, T this._f(S element)); bool moveNext() { if (_iterator.moveNext()) { _current = _f(_iterator.current); return true; } _current = null; return false; } T get current => _current; } /** * Specialized alternative to [MappedIterable] for mapped [List]s. * * Expects efficient `length` and `elementAt` on the source iterable. */ class MappedListIterable extends ListIterable implements EfficientLength { final Iterable _source; final _Transformation _f; MappedListIterable(this._source, T this._f(S value)); int get length => _source.length; T elementAt(int index) => _f(_source.elementAt(index)); } typedef bool _ElementPredicate(E element); class WhereIterable extends Iterable { final Iterable _iterable; final _ElementPredicate _f; WhereIterable(this._iterable, bool this._f(E element)); Iterator get iterator => new WhereIterator(_iterable.iterator, _f); } class WhereIterator extends Iterator { final Iterator _iterator; final _ElementPredicate _f; WhereIterator(this._iterator, bool this._f(E element)); bool moveNext() { while (_iterator.moveNext()) { if (_f(_iterator.current)) { return true; } } return false; } E get current => _iterator.current; } typedef Iterable _ExpandFunction(S sourceElement); class ExpandIterable extends Iterable { final Iterable _iterable; final _ExpandFunction _f; ExpandIterable(this._iterable, Iterable this._f(S element)); Iterator get iterator => new ExpandIterator(_iterable.iterator, _f); } class ExpandIterator implements Iterator { final Iterator _iterator; final _ExpandFunction _f; // Initialize _currentExpansion to an empty iterable. A null value // marks the end of iteration, and we don't want to call _f before // the first moveNext call. Iterator _currentExpansion = const EmptyIterator(); T _current; ExpandIterator(this._iterator, Iterable this._f(S element)); void _nextExpansion() { } T get current => _current; bool moveNext() { if (_currentExpansion == null) return false; while (!_currentExpansion.moveNext()) { _current = null; if (_iterator.moveNext()) { // If _f throws, this ends iteration. Otherwise _currentExpansion and // _current will be set again below. _currentExpansion = null; _currentExpansion = _f(_iterator.current).iterator; } else { return false; } } _current = _currentExpansion.current; return true; } } class TakeIterable extends Iterable { final Iterable _iterable; final int _takeCount; factory TakeIterable(Iterable iterable, int takeCount) { if (takeCount is! int || takeCount < 0) { throw new ArgumentError(takeCount); } if (iterable is EfficientLength) { return new EfficientLengthTakeIterable(iterable, takeCount); } return new TakeIterable._(iterable, takeCount); } TakeIterable._(this._iterable, this._takeCount); Iterator get iterator { return new TakeIterator(_iterable.iterator, _takeCount); } } class EfficientLengthTakeIterable extends TakeIterable implements EfficientLength { EfficientLengthTakeIterable(Iterable iterable, int takeCount) : super._(iterable, takeCount); int get length { int iterableLength = _iterable.length; if (iterableLength > _takeCount) return _takeCount; return iterableLength; } } class TakeIterator extends Iterator { final Iterator _iterator; int _remaining; TakeIterator(this._iterator, this._remaining) { assert(_remaining is int && _remaining >= 0); } bool moveNext() { _remaining--; if (_remaining >= 0) { return _iterator.moveNext(); } _remaining = -1; return false; } E get current { if (_remaining < 0) return null; return _iterator.current; } } class TakeWhileIterable extends Iterable { final Iterable _iterable; final _ElementPredicate _f; TakeWhileIterable(this._iterable, bool this._f(E element)); Iterator get iterator { return new TakeWhileIterator(_iterable.iterator, _f); } } class TakeWhileIterator extends Iterator { final Iterator _iterator; final _ElementPredicate _f; bool _isFinished = false; TakeWhileIterator(this._iterator, bool this._f(E element)); bool moveNext() { if (_isFinished) return false; if (!_iterator.moveNext() || !_f(_iterator.current)) { _isFinished = true; return false; } return true; } E get current { if (_isFinished) return null; return _iterator.current; } } class SkipIterable extends Iterable { final Iterable _iterable; final int _skipCount; factory SkipIterable(Iterable iterable, int count) { if (iterable is EfficientLength) { return new EfficientLengthSkipIterable(iterable, count); } return new SkipIterable._(iterable, count); } SkipIterable._(this._iterable, this._skipCount) { if (_skipCount is! int) { throw new ArgumentError.value(_skipCount, "count is not an integer"); } RangeError.checkNotNegative(_skipCount, "count"); } Iterable skip(int count) { if (_skipCount is! int) { throw new ArgumentError.value(_skipCount, "count is not an integer"); } RangeError.checkNotNegative(_skipCount, "count"); return new SkipIterable._(_iterable, _skipCount + count); } Iterator get iterator { return new SkipIterator(_iterable.iterator, _skipCount); } } class EfficientLengthSkipIterable extends SkipIterable implements EfficientLength { EfficientLengthSkipIterable(Iterable iterable, int skipCount) : super._(iterable, skipCount); int get length { int length = _iterable.length - _skipCount; if (length >= 0) return length; return 0; } } class SkipIterator extends Iterator { final Iterator _iterator; int _skipCount; SkipIterator(this._iterator, this._skipCount) { assert(_skipCount is int && _skipCount >= 0); } bool moveNext() { for (int i = 0; i < _skipCount; i++) _iterator.moveNext(); _skipCount = 0; return _iterator.moveNext(); } E get current => _iterator.current; } class SkipWhileIterable extends Iterable { final Iterable _iterable; final _ElementPredicate _f; SkipWhileIterable(this._iterable, bool this._f(E element)); Iterator get iterator { return new SkipWhileIterator(_iterable.iterator, _f); } } class SkipWhileIterator extends Iterator { final Iterator _iterator; final _ElementPredicate _f; bool _hasSkipped = false; SkipWhileIterator(this._iterator, bool this._f(E element)); bool moveNext() { if (!_hasSkipped) { _hasSkipped = true; while (_iterator.moveNext()) { if (!_f(_iterator.current)) return true; } } return _iterator.moveNext(); } E get current => _iterator.current; } /** * The always empty [Iterable]. */ class EmptyIterable extends Iterable implements EfficientLength { const EmptyIterable(); Iterator get iterator => const EmptyIterator(); void forEach(void action(E element)) {} bool get isEmpty => true; int get length => 0; E get first { throw IterableElementError.noElement(); } E get last { throw IterableElementError.noElement(); } E get single { throw IterableElementError.noElement(); } E elementAt(int index) { throw new RangeError.range(index, 0, 0, "index"); } bool contains(Object element) => false; bool every(bool test(E element)) => true; bool any(bool test(E element)) => false; E firstWhere(bool test(E element), { E orElse() }) { if (orElse != null) return orElse(); throw IterableElementError.noElement(); } E lastWhere(bool test(E element), { E orElse() }) { if (orElse != null) return orElse(); throw IterableElementError.noElement(); } E singleWhere(bool test(E element), { E orElse() }) { if (orElse != null) return orElse(); throw IterableElementError.noElement(); } String join([String separator = ""]) => ""; Iterable where(bool test(E element)) => this; Iterable map(f(E element)) => const EmptyIterable(); E reduce(E combine(E value, E element)) { throw IterableElementError.noElement(); } fold(var initialValue, combine(var previousValue, E element)) { return initialValue; } Iterable skip(int count) { RangeError.checkNotNegative(count, "count"); return this; } Iterable skipWhile(bool test(E element)) => this; Iterable take(int count) { RangeError.checkNotNegative(count, "count"); return this; } Iterable takeWhile(bool test(E element)) => this; List toList({ bool growable: true }) => growable ? [] : new List(0); Set toSet() => new Set(); } /** The always empty iterator. */ class EmptyIterator implements Iterator { const EmptyIterator(); bool moveNext() => false; E get current => null; } /** An [Iterator] that can move in both directions. */ abstract class BidirectionalIterator implements Iterator { bool movePrevious(); } /** * Creates errors throw by [Iterable] when the element count is wrong. */ abstract class IterableElementError { /** Error thrown thrown by, e.g., [Iterable.first] when there is no result. */ static StateError noElement() => new StateError("No element"); /** Error thrown by, e.g., [Iterable.single] if there are too many results. */ static StateError tooMany() => new StateError("Too many elements"); /** Error thrown by, e.g., [List.setRange] if there are too few elements. */ static StateError tooFew() => new StateError("Too few elements"); }