blob: 44db7cbb8be7fdf94d79c62056afa80571f4aa04 [file] [log] [blame]
// Copyright (c) 2015, Google Inc. 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 built_collection.list;
/// The Built Collection builder for [BuiltList].
///
/// It implements the mutating part of the [List] interface.
///
/// See the
/// [Built Collection library documentation](#built_collection/built_collection)
/// for the general properties of Built Collections.
class ListBuilder<E> {
List<E> _list;
_BuiltList<E> _listOwner;
/// Instantiates with elements from an [Iterable].
///
/// Must be called with a generic type parameter.
///
/// Wrong: `new ListBuilder([1, 2, 3])`.
///
/// Right: `new ListBuilder<int>([1, 2, 3])`,
///
/// Rejects nulls. Rejects elements of the wrong type.
factory ListBuilder([Iterable iterable = const []]) {
return new ListBuilder<E>._uninitialized()..replace(iterable);
}
/// Converts to a [BuiltList].
///
/// The `ListBuilder` can be modified again and used to create any number
/// of `BuiltList`s.
BuiltList<E> build() {
if (_listOwner == null) {
_setOwner(new _BuiltList<E>.withSafeList(_list));
}
return _listOwner;
}
/// Applies a function to `this`.
void update(updates(ListBuilder<E> builder)) {
updates(this);
}
/// Replaces all elements with elements from an [Iterable].
void replace(Iterable iterable) {
if (iterable is _BuiltList<E>) {
_setOwner(iterable);
} else {
_setSafeList(new List<E>.from(iterable));
}
}
// Based on List.
/// As [List.elementAt].
E operator [](int index) => _list[index];
/// As [List].
void operator []=(int index, E element) {
_checkElement(element);
_safeList[index] = element;
}
/// As [List.first].
E get first => _list.first;
/// As [List.first].
set first(E value) {
_list.first = value;
}
/// As [List.last].
E get last => _list.last;
/// As [List.last].
set last(E value) {
_list.last = value;
}
/// As [List.length].
int get length => _list.length;
/// As [List.isEmpty].
bool get isEmpty => _list.isEmpty;
/// As [List.isNotEmpty].
bool get isNotEmpty => _list.isNotEmpty;
/// As [List.add].
void add(E value) {
_checkElement(value);
_safeList.add(value);
}
/// As [List.addAll].
void addAll(Iterable<E> iterable) {
_checkElements(iterable);
_safeList.addAll(iterable);
}
/// As [List.reversed], but updates the builder in place. Returns nothing.
void reverse() {
_list = _list.reversed.toList(growable: true);
_listOwner = null;
}
/// As [List.sort].
void sort([int compare(E a, E b)]) {
_safeList.sort(compare);
}
/// As [List.shuffle].
void shuffle([Random random]) {
_safeList.shuffle(random);
}
/// As [List.clear].
void clear() {
_safeList.clear();
}
/// As [List.insert].
void insert(int index, E element) {
_checkElement(element);
_safeList.insert(index, element);
}
/// As [List.insertAll].
void insertAll(int index, Iterable<E> iterable) {
_checkElements(iterable);
_safeList.insertAll(index, iterable);
}
/// As [List.setAll].
void setAll(int index, Iterable<E> iterable) {
_checkElements(iterable);
_safeList.setAll(index, iterable);
}
/// As [List.remove].
bool remove(Object value) => _safeList.remove(value);
/// As [List.removeAt].
E removeAt(int index) => _safeList.removeAt(index);
/// As [List.removeLast].
E removeLast() => _safeList.removeLast();
/// As [List.removeWhere].
void removeWhere(bool test(E element)) {
_safeList.removeWhere(test);
}
/// As [List.retainWhere].
///
/// This method is an alias of [where].
void retainWhere(bool test(E element)) {
_safeList.retainWhere(test);
}
/// As [List.sublist], but updates the builder in place. Returns nothing.
void sublist(int start, [int end]) {
_setSafeList(_list.sublist(start, end));
}
/// As [List.setRange].
void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0]) {
_checkElements(iterable);
_safeList.setRange(start, end, iterable, skipCount);
}
/// As [List.removeRange].
void removeRange(int start, int end) {
_safeList.removeRange(start, end);
}
/// As [List.fillRange], but requires a value.
void fillRange(int start, int end, E fillValue) {
_checkElement(fillValue);
_safeList.fillRange(start, end, fillValue);
}
/// As [List.replaceRange].
void replaceRange(int start, int end, Iterable<E> iterable) {
_checkElements(iterable);
_safeList.replaceRange(start, end, iterable);
}
// Based on Iterable.
/// As [Iterable.map], but updates the builder in place. Returns nothing.
void map(E f(E element)) {
final result = _list.map(f).toList(growable: true);
_checkElements(result);
_setSafeList(result);
}
/// As [Iterable.where], but updates the builder in place. Returns nothing.
///
/// This method is an alias of [retainWhere].
void where(bool test(E element)) => retainWhere(test);
/// As [Iterable.expand], but updates the builder in place. Returns nothing.
void expand(Iterable<E> f(E element)) {
final result = _list.expand(f).toList(growable: true);
_checkElements(result);
_setSafeList(result);
}
/// As [Iterable.take], but updates the builder in place. Returns nothing.
void take(int n) {
_setSafeList(_list = _list.take(n).toList(growable: true));
}
/// As [Iterable.takeWhile], but updates the builder in place. Returns
/// nothing.
void takeWhile(bool test(E value)) {
_setSafeList(_list = _list.takeWhile(test).toList(growable: true));
}
/// As [Iterable.skip], but updates the builder in place. Returns nothing.
void skip(int n) {
_setSafeList(_list.skip(n).toList(growable: true));
}
/// As [Iterable.skipWhile], but updates the builder in place. Returns
/// nothing.
void skipWhile(bool test(E value)) {
_setSafeList(_list.skipWhile(test).toList(growable: true));
}
// Internal.
ListBuilder._uninitialized() {
_checkGenericTypeParameter();
}
void _setOwner(_BuiltList<E> listOwner) {
_list = listOwner._list;
_listOwner = listOwner;
}
void _setSafeList(List<E> list) {
_list = list;
_listOwner = null;
}
List<E> get _safeList {
if (_listOwner != null) {
_setSafeList(new List<E>.from(_list, growable: true));
}
return _list;
}
void _checkGenericTypeParameter() {
if (E == dynamic) {
throw new UnsupportedError('explicit element type required, '
'for example "new ListBuilder<int>"');
}
}
void _checkElement(E element) {
if (identical(element, null)) {
throw new ArgumentError('null element');
}
}
void _checkElements(Iterable elements) {
for (final element in elements) {
if (element is! E) {
throw new ArgumentError('invalid element: $element');
}
}
}
}