// Copyright (c) 2012, 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. class _GrowableObjectArray implements List { static final int _classId = (new _GrowableObjectArray(0))._cid; void insert(int index, T element) { if (index < 0 || index > length) { throw new RangeError.range(index, 0, length); } if (index == this.length) { add(element); return; } int oldLength = this.length; // We are modifying the length just below the is-check. Without the check // Array.copy could throw an exception, leaving the list in a bad state // (with a length that has been increased, but without a new element). if (index is! int) throw new ArgumentError(index); this.length++; Arrays.copy(this, index, this, index + 1, oldLength - index); this[index] = element; } T removeAt(int index) { if (index is! int) throw new ArgumentError(index); T result = this[index]; int newLength = this.length - 1; Arrays.copy(this, index + 1, this, index, newLength - index); this.length = newLength; return result; } bool remove(Object element) { for (int i = 0; i < this.length; i++) { if (this[i] == element) { removeAt(i); return true; } } return false; } void insertAll(int index, Iterable iterable) { if (index < 0 || index > length) { throw new RangeError.range(index, 0, length); } // TODO(floitsch): we can probably detect more cases. if (iterable is! List && iterable is! Set && iterable is! SubListIterable) { iterable = iterable.toList(); } int insertionLength = iterable.length; // There might be errors after the length change, in which case the list // will end up being modified but the operation not complete. Unless we // always go through a "toList" we can't really avoid that. this.length += insertionLength; setRange(index + insertionLength, this.length, this, index); setAll(index, iterable); } void setAll(int index, Iterable iterable) { if (iterable is List) { setRange(index, index + iterable.length, iterable); } else { for (T element in iterable) { this[index++] = element; } } } void removeWhere(bool test(T element)) { IterableMixinWorkaround.removeWhereList(this, test); } void retainWhere(bool test(T element)) { IterableMixinWorkaround.removeWhereList(this, (T element) => !test(element)); } Iterable getRange(int start, int end) { return IterableMixinWorkaround.getRangeList(this, start, end); } void setRange(int start, int end, Iterable iterable, [int skipCount = 0]) { IterableMixinWorkaround.setRangeList(this, start, end, iterable, skipCount); } void removeRange(int start, int end) { Arrays.indicesCheck(this, start, end); Arrays.copy(this, end, this, start, this.length - end); this.length = this.length - (end - start); } void replaceRange(int start, int end, Iterable iterable) { IterableMixinWorkaround.replaceRangeList(this, start, end, iterable); } void fillRange(int start, int end, [T fillValue]) { IterableMixinWorkaround.fillRangeList(this, start, end, fillValue); } List sublist(int start, [int end]) { Arrays.indicesCheck(this, start, end); if (end == null) end = length; int length = end - start; if (start == end) return []; List list = new _GrowableObjectArray.withCapacity(length); list.length = length; Arrays.copy(this, start, list, 0, length); return list; } factory _GrowableObjectArray(int length) { var data = new _ObjectArray((length == 0) ? 4 : length); var result = new _GrowableObjectArray.withData(data); if (length > 0) { result._setLength(length); } return result; } factory _GrowableObjectArray.withCapacity(int capacity) { var data = new _ObjectArray((capacity == 0)? 4 : capacity); return new _GrowableObjectArray.withData(data); } factory _GrowableObjectArray.from(Iterable other) { List result = new _GrowableObjectArray(); result.addAll(other); return result; } factory _GrowableObjectArray.withData(_ObjectArray data) native "GrowableObjectArray_allocate"; int get length native "GrowableObjectArray_getLength"; int get _capacity native "GrowableObjectArray_getCapacity"; void set length(int new_length) { if (new_length > _capacity) { _grow(new_length); } else { for (int i = new_length; i < length; i++) { this[i] = null; } } _setLength(new_length); } void _setLength(int new_length) native "GrowableObjectArray_setLength"; void _setData(_ObjectArray array) native "GrowableObjectArray_setData"; T operator [](int index) native "GrowableObjectArray_getIndexed"; void operator []=(int index, T value) native "GrowableObjectArray_setIndexed"; // The length of this growable array. It is always less than or equal to the // length of the object array, which itself is always greater than 0, so that // grow() does not have to check for a zero length object array before // doubling its size. void add(T value) { var len = length; if (len == _capacity) { _grow(len * 2); } _setLength(len + 1); this[len] = value; } void addAll(Iterable iterable) { for (T elem in iterable) { add(elem); } } T removeLast() { var len = length - 1; var elem = this[len]; this[len] = null; _setLength(len); return elem; } T get first { if (length > 0) return this[0]; throw new StateError("No elements"); } T get last { if (length > 0) return this[length - 1]; throw new StateError("No elements"); } T get single { if (length == 1) return this[0]; if (length == 0) throw new StateError("No elements"); throw new StateError("More than one element"); } int indexOf(Object element, [int start = 0]) { return IterableMixinWorkaround.indexOfList(this, element, start); } int lastIndexOf(Object element, [int start = null]) { return IterableMixinWorkaround.lastIndexOfList(this, element, start); } void _grow(int new_length) { var new_data = new _ObjectArray(new_length); for (int i = 0; i < length; i++) { new_data[i] = this[i]; } _setData(new_data); } // Collection interface. bool contains(Object element) { return IterableMixinWorkaround.contains(this, element); } void forEach(f(T element)) { int initialLength = length; for (int i = 0; i < length; i++) { f(this[i]); if (length != initialLength) throw new ConcurrentModificationError(this); } } String join([String separator = ""]) { if (isEmpty) return ""; if (this.length == 1) return "${this[0]}"; StringBuffer buffer = new StringBuffer(); if (separator.isEmpty) { for (int i = 0; i < this.length; i++) { buffer.write("${this[i]}"); } } else { buffer.write("${this[0]}"); for (int i = 1; i < this.length; i++) { buffer.write(separator); buffer.write("${this[i]}"); } } return buffer.toString(); } Iterable map(f(T element)) { return IterableMixinWorkaround.mapList(this, f); } T reduce(T combine(T value, T element)) { return IterableMixinWorkaround.reduce(this, combine); } fold(initialValue, combine(previousValue, T element)) { return IterableMixinWorkaround.fold(this, initialValue, combine); } Iterable where(bool f(T element)) { return IterableMixinWorkaround.where(this, f); } Iterable expand(Iterable f(T element)) { return IterableMixinWorkaround.expand(this, f); } Iterable take(int n) { return IterableMixinWorkaround.takeList(this, n); } Iterable takeWhile(bool test(T value)) { return IterableMixinWorkaround.takeWhile(this, test); } Iterable skip(int n) { return IterableMixinWorkaround.skipList(this, n); } Iterable skipWhile(bool test(T value)) { return IterableMixinWorkaround.skipWhile(this, test); } bool every(bool f(T element)) { return IterableMixinWorkaround.every(this, f); } bool any(bool f(T element)) { return IterableMixinWorkaround.any(this, f); } T firstWhere(bool test(T value), {T orElse()}) { return IterableMixinWorkaround.firstWhere(this, test, orElse); } T lastWhere(bool test(T value), {T orElse()}) { return IterableMixinWorkaround.lastWhereList(this, test, orElse); } T singleWhere(bool test(T value)) { return IterableMixinWorkaround.singleWhere(this, test); } T elementAt(int index) { return this[index]; } bool get isEmpty { return this.length == 0; } bool get isNotEmpty => !isEmpty; void clear() { this.length = 0; } Iterable get reversed => IterableMixinWorkaround.reversedList(this); void sort([int compare(T a, T b)]) { IterableMixinWorkaround.sortList(this, compare); } String toString() { return IterableMixinWorkaround.toStringIterable(this, '[', ']'); } Iterator get iterator { return new ListIterator(this); } List toList({ bool growable: true }) { return new List.from(this, growable: growable); } Set toSet() { return new Set.from(this); } Map asMap() { return IterableMixinWorkaround.asMapList(this); } }