blob: a9a1413f8af2d295230b87242f050bfeea591a9f [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 '../list.dart';
/// 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> {
late List<E> _list;
_BuiltList<E>? _listOwner;
/// Instantiates with elements from an [Iterable].
factory ListBuilder([Iterable iterable = const []]) {
return 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(_BuiltList<E>.withSafeList(_list));
}
return _listOwner!;
}
/// Applies a function to `this`.
void update(Function(ListBuilder<E>) updates) {
updates(this);
}
/// Replaces all elements with elements from an [Iterable].
void replace(Iterable iterable) {
if (iterable is _BuiltList<E>) {
_setOwner(iterable);
} else {
_setSafeList(List<E>.from(iterable));
}
}
// Based on List.
/// As [List.elementAt].
E operator [](int index) => _list[index];
/// As [List].
void operator []=(int index, E element) {
_maybeCheckElement(element);
_safeList[index] = element;
}
/// As [List.first].
E get first => _list.first;
/// As [List.first].
set first(E value) {
_maybeCheckElement(value);
_safeList.first = value;
}
/// As [List.last].
E get last => _list.last;
/// As [List.last].
set last(E value) {
_maybeCheckElement(value);
_safeList.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) {
_maybeCheckElement(value);
_safeList.add(value);
}
/// As [List.addAll].
void addAll(Iterable<E> iterable) {
// Add directly to the underlying `List` then check elements there, for
// performance. Roll back the changes if validation fails.
var safeList = _safeList;
var lengthBefore = safeList.length;
safeList.addAll(iterable);
if (!_needsNullCheck) return;
try {
for (var i = lengthBefore; i != safeList.length; ++i) {
_checkElement(safeList[i]);
}
} catch (_) {
safeList.removeRange(lengthBefore, safeList.length);
rethrow;
}
}
/// 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 Function(E, E)? compare]) {
_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) {
_maybeCheckElement(element);
_safeList.insert(index, element);
}
/// As [List.insertAll].
void insertAll(int index, Iterable<E> iterable) {
// Add directly to the underlying `List` then check elements there, for
// performance. Roll back the changes if validation fails.
var safeList = _safeList;
var lengthBefore = safeList.length;
safeList.insertAll(index, iterable);
if (!_needsNullCheck) return;
var insertedLength = safeList.length - lengthBefore;
try {
for (var i = index; i != index + insertedLength; ++i) {
_checkElement(safeList[i]);
}
} catch (_) {
safeList.removeRange(index, index + insertedLength);
rethrow;
}
}
/// As [List.setAll].
void setAll(int index, Iterable<E> iterable) {
iterable = evaluateIterable(iterable);
_maybeCheckElements(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 Function(E) test) {
_safeList.removeWhere(test);
}
/// As [List.retainWhere].
///
/// This method is an alias of [where].
void retainWhere(bool Function(E) test) {
_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]) {
iterable = evaluateIterable(iterable);
_maybeCheckElements(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) {
_maybeCheckElement(fillValue);
_safeList.fillRange(start, end, fillValue);
}
/// As [List.replaceRange].
void replaceRange(int start, int end, Iterable<E> iterable) {
iterable = evaluateIterable(iterable);
_maybeCheckElements(iterable);
_safeList.replaceRange(start, end, iterable);
}
// Based on Iterable.
/// As [Iterable.map], but updates the builder in place. Returns nothing.
void map(E Function(E) f) {
var result = _list.map(f).toList(growable: true);
_maybeCheckElements(result);
_setSafeList(result);
}
/// As [Iterable.where], but updates the builder in place. Returns nothing.
///
/// This method is an alias of [retainWhere].
void where(bool Function(E) test) => retainWhere(test);
/// As [Iterable.expand], but updates the builder in place. Returns nothing.
void expand(Iterable<E> Function(E) f) {
var result = _list.expand(f).toList(growable: true);
_maybeCheckElements(result);
_setSafeList(result);
}
/// As [Iterable.take], but updates the builder in place. Returns nothing.
void take(int n) {
_setSafeList(_list.take(n).toList(growable: true));
}
/// As [Iterable.takeWhile], but updates the builder in place. Returns
/// nothing.
void takeWhile(bool Function(E) test) {
_setSafeList(_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 Function(E) test) {
_setSafeList(_list.skipWhile(test).toList(growable: true));
}
// Internal.
ListBuilder._uninitialized();
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(List<E>.from(_list, growable: true));
}
return _list;
}
bool get _needsNullCheck => !isSoundMode && null is! E;
void _maybeCheckElement(E element) {
if (_needsNullCheck) _checkElement(element);
}
void _checkElement(E element) {
if (identical(element, null)) {
throw ArgumentError('null element');
}
}
void _maybeCheckElements(Iterable<E> elements) {
if (!_needsNullCheck) return;
for (var element in elements) {
_checkElement(element);
}
}
}