2013-01-18 14:08:41 +00:00
|
|
|
// 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.
|
|
|
|
|
2013-02-06 15:11:47 +00:00
|
|
|
part of dart._collection.dev;
|
2013-01-18 14:08:41 +00:00
|
|
|
|
2013-02-14 09:51:43 +00:00
|
|
|
/**
|
|
|
|
* 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<E> extends Iterable<E> {
|
|
|
|
int get length;
|
|
|
|
E elementAt(int i);
|
|
|
|
|
2013-02-18 10:35:51 +00:00
|
|
|
Iterator<E> get iterator => new ListIterator<E>(this);
|
2013-02-14 09:51:43 +00:00
|
|
|
|
|
|
|
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 new StateError("No elements");
|
|
|
|
return elementAt(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
E get last {
|
|
|
|
if (length == 0) throw new StateError("No elements");
|
|
|
|
return elementAt(length - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
E get single {
|
|
|
|
if (length == 0) throw new StateError("No elements");
|
|
|
|
if (length > 1) throw new StateError("Too many elements");
|
|
|
|
return elementAt(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool contains(E 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 firstMatching(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 new StateError("No matching element");
|
|
|
|
}
|
|
|
|
|
|
|
|
E lastMatching(bool test(E element), { E orElse() }) {
|
|
|
|
int length = this.length;
|
2013-02-18 10:35:51 +00:00
|
|
|
for (int i = length - 1; i >= 0; i--) {
|
2013-02-14 09:51:43 +00:00
|
|
|
E element = elementAt(i);
|
|
|
|
if (test(element)) return element;
|
|
|
|
if (length != this.length) {
|
|
|
|
throw new ConcurrentModificationError(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (orElse != null) return orElse();
|
|
|
|
throw new StateError("No matching element");
|
|
|
|
}
|
|
|
|
|
|
|
|
E singleMatching(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 new StateError("More than one matching element");
|
|
|
|
}
|
|
|
|
matchFound = true;
|
|
|
|
match = element;
|
|
|
|
}
|
|
|
|
if (length != this.length) {
|
|
|
|
throw new ConcurrentModificationError(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (matchFound) return match;
|
|
|
|
throw new StateError("No matching element");
|
|
|
|
}
|
|
|
|
|
|
|
|
E min([int compare(E a, E b)]) {
|
|
|
|
if (length == 0) return null;
|
|
|
|
if (compare == null) {
|
|
|
|
var defaultCompare = Comparable.compare;
|
|
|
|
compare = defaultCompare;
|
|
|
|
}
|
|
|
|
E min = elementAt(0);
|
|
|
|
int length = this.length;
|
|
|
|
for (int i = 1; i < length; i++) {
|
|
|
|
E element = elementAt(i);
|
|
|
|
if (compare(min, element) > 0) {
|
|
|
|
min = element;
|
|
|
|
}
|
|
|
|
if (length != this.length) {
|
|
|
|
throw new ConcurrentModificationError(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return min;
|
|
|
|
}
|
|
|
|
|
|
|
|
E max([int compare(E a, E b)]) {
|
|
|
|
if (length == 0) return null;
|
|
|
|
if (compare == null) {
|
|
|
|
var defaultCompare = Comparable.compare;
|
|
|
|
compare = defaultCompare;
|
|
|
|
}
|
|
|
|
E max = elementAt(0);
|
|
|
|
int length = this.length;
|
|
|
|
for (int i = 1; i < length; i++) {
|
|
|
|
E element = elementAt(i);
|
|
|
|
if (compare(max, element) < 0) {
|
|
|
|
max = element;
|
|
|
|
}
|
|
|
|
if (length != this.length) {
|
|
|
|
throw new ConcurrentModificationError(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return max;
|
|
|
|
}
|
|
|
|
|
|
|
|
String join([String separator]) {
|
|
|
|
int length = this.length;
|
|
|
|
if (separator != null && !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.add(separator);
|
|
|
|
buffer.add("${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.add("${elementAt(i)}");
|
|
|
|
if (length != this.length) {
|
|
|
|
throw new ConcurrentModificationError(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return buffer.toString();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Iterable<E> where(bool test(E element)) => super.where(test);
|
|
|
|
|
2013-02-18 10:35:51 +00:00
|
|
|
Iterable map(f(E element)) => new MappedListIterable(this, f);
|
2013-02-15 18:38:42 +00:00
|
|
|
|
2013-02-14 09:51:43 +00:00
|
|
|
reduce(var initialValue, combine(var previousValue, E element)) {
|
|
|
|
var value = initialValue;
|
|
|
|
int length = this.length;
|
|
|
|
for (int i = 0; i < length; i++) {
|
2013-02-18 10:35:51 +00:00
|
|
|
value = combine(value, elementAt(i));
|
2013-02-14 09:51:43 +00:00
|
|
|
if (length != this.length) {
|
|
|
|
throw new ConcurrentModificationError(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
Iterable<E> skip(int count) => new SubListIterable(this, count, null);
|
|
|
|
|
|
|
|
Iterable<E> skipWhile(bool test(E element)) => super.skipWhile(test);
|
|
|
|
|
|
|
|
Iterable<E> take(int count) => new SubListIterable(this, 0, count);
|
|
|
|
|
|
|
|
Iterable<E> takeWhile(bool test(E element)) => super.takeWhile(test);
|
|
|
|
|
|
|
|
List<E> toList() {
|
|
|
|
List<E> result = new List(length);
|
|
|
|
for (int i = 0; i < length; i++) {
|
|
|
|
result[i] = elementAt(i);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
Set<E> toSet() {
|
|
|
|
Set<E> result = new Set<E>();
|
|
|
|
for (int i = 0; i < length; i++) {
|
|
|
|
result.add(elementAt(i));
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-18 10:35:51 +00:00
|
|
|
class SubListIterable<E> extends ListIterable<E> {
|
2013-02-14 09:51:43 +00:00
|
|
|
final Iterable<E> _iterable;
|
|
|
|
final int _start;
|
|
|
|
/** If null, represents the length of the iterable. */
|
|
|
|
final int _endOrLength;
|
|
|
|
|
|
|
|
SubListIterable(this._iterable, this._start, this._endOrLength);
|
|
|
|
|
|
|
|
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.range(index, 0, length);
|
|
|
|
}
|
|
|
|
return _iterable.elementAt(realIndex);
|
|
|
|
}
|
|
|
|
|
|
|
|
Iterable<E> skip(int count) {
|
|
|
|
if (count < 0) throw new ArgumentError(count);
|
|
|
|
return new SubListIterable(_iterable, _start + count, _endOrLength);
|
|
|
|
}
|
|
|
|
|
|
|
|
Iterable<E> take(int count) {
|
|
|
|
if (count < 0) throw new ArgumentError(count);
|
|
|
|
if (_endOrLength == null) {
|
|
|
|
return new SubListIterable(_iterable, _start, _start + count);
|
|
|
|
} else {
|
2013-02-18 10:35:51 +00:00
|
|
|
int newEnd = _start + count;
|
2013-02-14 09:51:43 +00:00
|
|
|
if (_endOrLength < newEnd) return this;
|
|
|
|
return new SubListIterable(_iterable, _start, newEnd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-18 10:35:51 +00:00
|
|
|
/**
|
|
|
|
* 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<E> implements Iterator<E> {
|
2013-02-14 09:51:43 +00:00
|
|
|
final Iterable<E> _iterable;
|
|
|
|
final int _length;
|
|
|
|
int _index;
|
|
|
|
E _current;
|
2013-02-18 10:35:51 +00:00
|
|
|
|
|
|
|
ListIterator(Iterable<E> iterable)
|
2013-02-14 09:51:43 +00:00
|
|
|
: _iterable = iterable, _length = iterable.length, _index = 0;
|
|
|
|
|
|
|
|
E get current => _current;
|
|
|
|
|
|
|
|
bool moveNext() {
|
|
|
|
if (_length != _iterable.length) {
|
|
|
|
throw new ConcurrentModificationError(_iterable);
|
|
|
|
}
|
|
|
|
if (_index == _length) {
|
|
|
|
_current = null;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
_current = _iterable.elementAt(_index);
|
|
|
|
_index++;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-18 14:08:41 +00:00
|
|
|
typedef T _Transformation<S, T>(S value);
|
|
|
|
|
|
|
|
class MappedIterable<S, T> extends Iterable<T> {
|
|
|
|
final Iterable<S> _iterable;
|
|
|
|
// TODO(ahe): Restore type when feature is implemented in dart2js
|
|
|
|
// checked mode. http://dartbug.com/7733
|
|
|
|
final /* _Transformation<S, T> */ _f;
|
|
|
|
|
|
|
|
MappedIterable(this._iterable, T this._f(S element));
|
|
|
|
|
|
|
|
Iterator<T> get iterator => new MappedIterator<S, T>(_iterable.iterator, _f);
|
|
|
|
|
|
|
|
// Length related functions are independent of the mapping.
|
|
|
|
int get length => _iterable.length;
|
|
|
|
bool get isEmpty => _iterable.isEmpty;
|
2013-02-04 09:30:36 +00:00
|
|
|
|
2013-02-18 10:35:51 +00:00
|
|
|
// 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));
|
|
|
|
}
|
2013-02-15 18:38:42 +00:00
|
|
|
|
2013-01-18 14:08:41 +00:00
|
|
|
class MappedIterator<S, T> extends Iterator<T> {
|
|
|
|
T _current;
|
|
|
|
final Iterator<S> _iterator;
|
|
|
|
// TODO(ahe): Restore type when feature is implemented in dart2js
|
|
|
|
// checked mode. http://dartbug.com/7733
|
|
|
|
final /* _Transformation<S, T> */ _f;
|
|
|
|
|
|
|
|
MappedIterator(this._iterator, T this._f(S element));
|
|
|
|
|
|
|
|
bool moveNext() {
|
|
|
|
if (_iterator.moveNext()) {
|
|
|
|
_current = _f(_iterator.current);
|
|
|
|
return true;
|
|
|
|
}
|
2013-02-18 10:35:51 +00:00
|
|
|
_current = null;
|
|
|
|
return false;
|
2013-01-18 14:08:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
T get current => _current;
|
|
|
|
}
|
|
|
|
|
2013-02-04 09:30:36 +00:00
|
|
|
/** Specialized alternative to [MappedIterable] for mapped [List]s. */
|
2013-02-18 10:35:51 +00:00
|
|
|
class MappedListIterable<S, T> extends ListIterable<T> {
|
|
|
|
final Iterable<S> _source;
|
2013-02-15 18:38:42 +00:00
|
|
|
// TODO(ahe): Restore type when feature is implemented in dart2js
|
|
|
|
// checked mode. http://dartbug.com/7733
|
|
|
|
final /* _Transformation<S, T> */ _f;
|
|
|
|
|
2013-02-18 10:35:51 +00:00
|
|
|
MappedListIterable(this._source, T this._f(S value));
|
2013-02-15 18:38:42 +00:00
|
|
|
|
2013-02-18 10:35:51 +00:00
|
|
|
int get length => _source.length;
|
|
|
|
T elementAt(int index) => _f(_source.elementAt(index));
|
2013-02-15 18:38:42 +00:00
|
|
|
}
|
|
|
|
|
2013-02-04 09:30:36 +00:00
|
|
|
|
2013-01-18 14:08:41 +00:00
|
|
|
typedef bool _ElementPredicate<E>(E element);
|
|
|
|
|
|
|
|
class WhereIterable<E> extends Iterable<E> {
|
|
|
|
final Iterable<E> _iterable;
|
|
|
|
// TODO(ahe): Restore type when feature is implemented in dart2js
|
|
|
|
// checked mode. http://dartbug.com/7733
|
|
|
|
final /* _ElementPredicate */ _f;
|
|
|
|
|
|
|
|
WhereIterable(this._iterable, bool this._f(E element));
|
|
|
|
|
|
|
|
Iterator<E> get iterator => new WhereIterator<E>(_iterable.iterator, _f);
|
|
|
|
}
|
|
|
|
|
|
|
|
class WhereIterator<E> extends Iterator<E> {
|
|
|
|
final Iterator<E> _iterator;
|
|
|
|
// TODO(ahe): Restore type when feature is implemented in dart2js
|
|
|
|
// checked mode. http://dartbug.com/7733
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-02-05 09:48:05 +00:00
|
|
|
typedef Iterable<T> _ExpandFunction<S, T>(S sourceElement);
|
|
|
|
|
|
|
|
class ExpandIterable<S, T> extends Iterable<T> {
|
|
|
|
final Iterable<S> _iterable;
|
|
|
|
// TODO(ahe): Restore type when feature is implemented in dart2js
|
|
|
|
// checked mode. http://dartbug.com/7733
|
|
|
|
final /* _ExpandFunction */ _f;
|
|
|
|
|
|
|
|
ExpandIterable(this._iterable, Iterable<T> this._f(S element));
|
|
|
|
|
|
|
|
Iterator<T> get iterator => new ExpandIterator<S, T>(_iterable.iterator, _f);
|
|
|
|
}
|
|
|
|
|
|
|
|
class ExpandIterator<S, T> implements Iterator<T> {
|
|
|
|
final Iterator<S> _iterator;
|
|
|
|
// TODO(ahe): Restore type when feature is implemented in dart2js
|
|
|
|
// checked mode. http://dartbug.com/7733
|
|
|
|
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<T> _currentExpansion = const EmptyIterator();
|
|
|
|
T _current;
|
|
|
|
|
|
|
|
ExpandIterator(this._iterator, Iterable<T> 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-18 14:08:41 +00:00
|
|
|
class TakeIterable<E> extends Iterable<E> {
|
|
|
|
final Iterable<E> _iterable;
|
|
|
|
final int _takeCount;
|
|
|
|
|
|
|
|
TakeIterable(this._iterable, this._takeCount) {
|
|
|
|
if (_takeCount is! int || _takeCount < 0) {
|
|
|
|
throw new ArgumentError(_takeCount);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Iterator<E> get iterator {
|
|
|
|
return new TakeIterator<E>(_iterable.iterator, _takeCount);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class TakeIterator<E> extends Iterator<E> {
|
|
|
|
final Iterator<E> _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<E> extends Iterable<E> {
|
|
|
|
final Iterable<E> _iterable;
|
|
|
|
// TODO(ahe): Restore type when feature is implemented in dart2js
|
|
|
|
// checked mode. http://dartbug.com/7733
|
|
|
|
final /* _ElementPredicate */ _f;
|
|
|
|
|
|
|
|
TakeWhileIterable(this._iterable, bool this._f(E element));
|
|
|
|
|
|
|
|
Iterator<E> get iterator {
|
|
|
|
return new TakeWhileIterator<E>(_iterable.iterator, _f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class TakeWhileIterator<E> extends Iterator<E> {
|
|
|
|
final Iterator<E> _iterator;
|
|
|
|
// TODO(ahe): Restore type when feature is implemented in dart2js
|
|
|
|
// checked mode. http://dartbug.com/7733
|
|
|
|
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<E> extends Iterable<E> {
|
|
|
|
final Iterable<E> _iterable;
|
|
|
|
final int _skipCount;
|
|
|
|
|
|
|
|
SkipIterable(this._iterable, this._skipCount) {
|
|
|
|
if (_skipCount is! int || _skipCount < 0) {
|
|
|
|
throw new ArgumentError(_skipCount);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Iterable<E> skip(int n) {
|
|
|
|
if (n is! int || n < 0) {
|
|
|
|
throw new ArgumentError(n);
|
|
|
|
}
|
|
|
|
return new SkipIterable<E>(_iterable, _skipCount + n);
|
|
|
|
}
|
|
|
|
|
|
|
|
Iterator<E> get iterator {
|
|
|
|
return new SkipIterator<E>(_iterable.iterator, _skipCount);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class SkipIterator<E> extends Iterator<E> {
|
|
|
|
final Iterator<E> _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<E> extends Iterable<E> {
|
|
|
|
final Iterable<E> _iterable;
|
|
|
|
// TODO(ahe): Restore type when feature is implemented in dart2js
|
|
|
|
// checked mode. http://dartbug.com/7733
|
|
|
|
final /* _ElementPredicate */ _f;
|
|
|
|
|
|
|
|
SkipWhileIterable(this._iterable, bool this._f(E element));
|
|
|
|
|
|
|
|
Iterator<E> get iterator {
|
|
|
|
return new SkipWhileIterator<E>(_iterable.iterator, _f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class SkipWhileIterator<E> extends Iterator<E> {
|
|
|
|
final Iterator<E> _iterator;
|
|
|
|
// TODO(ahe): Restore type when feature is implemented in dart2js
|
|
|
|
// checked mode. http://dartbug.com/7733
|
|
|
|
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;
|
|
|
|
}
|
2013-02-04 09:30:36 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The always empty [Iterable].
|
|
|
|
*/
|
|
|
|
class EmptyIterable<E> extends Iterable<E> {
|
|
|
|
const EmptyIterable();
|
|
|
|
|
|
|
|
Iterator<E> get iterator => const EmptyIterator();
|
|
|
|
|
|
|
|
void forEach(void action(E element)) {}
|
|
|
|
|
|
|
|
bool get isEmpty => true;
|
|
|
|
|
|
|
|
int get length => 0;
|
|
|
|
|
|
|
|
E get first { throw new StateError("No elements"); }
|
|
|
|
|
|
|
|
E get last { throw new StateError("No elements"); }
|
|
|
|
|
|
|
|
E get single { throw new StateError("No elements"); }
|
|
|
|
|
|
|
|
E elementAt(int index) { throw new RangeError.value(index); }
|
|
|
|
|
|
|
|
bool contains(E element) => false;
|
|
|
|
|
|
|
|
bool every(bool test(E element)) => true;
|
|
|
|
|
|
|
|
bool any(bool test(E element)) => false;
|
|
|
|
|
|
|
|
E firstMatching(bool test(E element), { E orElse() }) {
|
|
|
|
if (orElse != null) return orElse();
|
|
|
|
throw new StateError("No matching element");
|
|
|
|
}
|
|
|
|
|
|
|
|
E lastMatching(bool test(E element), { E orElse() }) {
|
2013-02-04 10:41:55 +00:00
|
|
|
if (orElse != null) return orElse();
|
|
|
|
throw new StateError("No matching element");
|
2013-02-04 09:30:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
E singleMatching(bool test(E element), { E orElse() }) {
|
2013-02-04 10:41:55 +00:00
|
|
|
if (orElse != null) return orElse();
|
|
|
|
throw new StateError("No matching element");
|
2013-02-04 09:30:36 +00:00
|
|
|
}
|
|
|
|
|
2013-02-04 10:41:55 +00:00
|
|
|
E min([int compare(E a, E b)]) => null;
|
2013-02-04 09:30:36 +00:00
|
|
|
|
2013-02-04 10:41:55 +00:00
|
|
|
E max([int compare(E a, E b)]) => null;
|
2013-02-04 09:30:36 +00:00
|
|
|
|
|
|
|
String join([String separator]) => "";
|
|
|
|
|
|
|
|
Iterable<E> where(bool test(E element)) => this;
|
|
|
|
|
2013-02-04 10:41:55 +00:00
|
|
|
Iterable map(f(E element)) => const EmptyIterable();
|
2013-02-04 09:30:36 +00:00
|
|
|
|
|
|
|
reduce(var initialValue, combine(var previousValue, E element)) {
|
|
|
|
return initialValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
Iterable<E> skip(int count) => this;
|
|
|
|
|
|
|
|
Iterable<E> skipWhile(bool test(E element)) => this;
|
|
|
|
|
|
|
|
Iterable<E> take(int count) => this;
|
|
|
|
|
|
|
|
Iterable<E> takeWhile(bool test(E element)) => this;
|
|
|
|
|
|
|
|
List toList() => <E>[];
|
|
|
|
|
|
|
|
Set toSet() => new Set<E>();
|
|
|
|
}
|
|
|
|
|
|
|
|
/** The always empty iterator. */
|
|
|
|
class EmptyIterator<E> implements Iterator<E> {
|
|
|
|
const EmptyIterator();
|
|
|
|
bool moveNext() => false;
|
|
|
|
E get current => null;
|
|
|
|
}
|
2013-02-12 15:22:31 +00:00
|
|
|
|
|
|
|
/** An [Iterator] that can move in both directions. */
|
|
|
|
abstract class BiDirectionalIterator<T> implements Iterator<T> {
|
|
|
|
bool movePrevious();
|
|
|
|
}
|