Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(605)

Unified Diff: lib/observe/list.dart

Issue 12096106: work in progress: observable implementation using detailed change records (Closed) Base URL: https://github.com/dart-lang/web-ui.git@master
Patch Set: Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « lib/observe/expression.dart ('k') | lib/observe/map.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/observe/list.dart
diff --git a/lib/observe/list.dart b/lib/observe/list.dart
new file mode 100644
index 0000000000000000000000000000000000000000..b4f16e72b814c4e78710eba74ac2973c014eb70e
--- /dev/null
+++ b/lib/observe/list.dart
@@ -0,0 +1,255 @@
+// 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.
+
+library web_ui.observe.list;
+
+import 'dart:collection';
+import 'dart:collection-dev';
+import 'package:web_ui/observe.dart';
+
+// TODO(jmesserly): this should extend the real list implementation.
+// See http://dartbug.com/2600. The workaround was to copy+paste lots of code
+// from the VM.
+// TODO(jmesserly): also this shold extend Collection<E>, so we can delete
+// copy+paste code. Fix once we have mixins.
+/**
+ * Represents an observable list of model values. If any items are added,
+ * removed, or replaced, then observers that are registered with
+ * [observe] will be notified.
+ */
+class ObservableList<E> extends Observable implements List<E> {
+ /** The inner [List<E>] with the actual storage. */
+ final List<E> _list;
+
+ /**
+ * Creates an observable list of the given [length].
+ *
+ * If no [length] argument is supplied an extendable list of
+ * length 0 is created.
+ *
+ * If a [length] argument is supplied, a fixed size list of that
+ * length is created.
+ */
+ ObservableList([int length = 0]) : _list = new List<E>(length);
+
+ /**
+ * Creates an observable list with the elements of [other]. The order in
+ * the list will be the order provided by the iterator of [other].
+ */
+ factory ObservableList.from(Iterable<E> other)
+ => new ObservableList<E>()..addAll(other);
+
+ // TODO(jmesserly): This should be on List.
+ // See http://code.google.com/p/dart/issues/detail?id=947
+ bool remove(E item) {
+ int i = indexOf(item);
+ if (i == -1) return false;
+ removeAt(i);
+ return true;
+ }
+
+ // TODO(jmesserly): This should be on List, to match removeAt.
+ // See http://code.google.com/p/dart/issues/detail?id=5375
+ void insertAt(int index, E item) => insertRange(index, 1, item);
+
+ E get first => this[0];
+
+ Iterator<E> get iterator => new ListIterator<E>(this);
+
+ int get length {
+ if (observeReads) notifyRead(ChangeRecord.FIELD, 'length');
+ return _list.length;
+ }
+
+ set length(int value) {
+ int len = _list.length;
+ if (len == value) return;
+
+ // Produce notifications if needed
+ if (hasObservers) {
+ if (value < len) {
+ // Remove items, then adjust length
+ for (int i = len - 1; i >= value; i--) {
+ notifyChange(ChangeRecord.INDEX, i, null, null);
+ }
+ notifyChange(ChangeRecord.FIELD, 'length', len, value);
+ } else {
+ // Adjust length then add items
+ notifyChange(ChangeRecord.FIELD, 'length', len, value);
+ for (int i = len; i < value; i++) {
+ notifyChange(ChangeRecord.INDEX, i, null, null);
+ }
+ }
+ }
+
+ _list.length = value;
+ }
+
+ E operator [](int index) {
+ if (observeReads) notifyRead(ChangeRecord.INDEX, index);
+ return _list[index];
+ }
+
+ operator []=(int index, E value) {
+ var oldValue = _list[index];
+ if (hasObservers) notifyChange(ChangeRecord.INDEX, index, oldValue, value);
+ _list[index] = value;
+ }
+
+ void add(E value) {
+ int len = _list.length;
+ if (hasObservers) {
+ notifyChange(ChangeRecord.FIELD, 'length', len, len + 1);
+ notifyChange(ChangeRecord.INDEX, len, null, value);
+ }
+ _list.add(value);
+ }
+
+ // ---------------------------------------------------------------------------
+ // Note: below this comment, methods are either:
+ // * redirect to Arrays
+ // * copy+paste from VM GrowableObjectArray.
+ // The general idea is to have these methods operate in terms of our primitive
+ // methods above, so they correctly track reads/writes.
+ // ---------------------------------------------------------------------------
+
+ E removeLast() {
+ var len = length - 1;
+ var elem = this[len];
+ length = len;
+ return elem;
+ }
+
+ int indexOf(E element, [int start = 0]) =>
+ Arrays.indexOf(this, element, start, length);
+
+ int lastIndexOf(E element, [int start]) =>
+ Arrays.lastIndexOf(this, element, start);
+
+ ObservableList<E> getRange(int start, int length) {
+ if (length == 0) return [];
+ Arrays.rangeCheck(this, start, length);
+ List list = new ObservableList<E>(length);
+ Arrays.copy(this, start, list, 0, length);
+ return list;
+ }
+
+ bool get isEmpty => length == 0;
+
+ E get last => this[length - 1];
+
+ void addLast(E value) => add(value);
+
+ void sort([compare = Comparable.compare]) =>
+ IterableMixinWorkaround.sortList(this, compare);
+
+ void clear() {
+ this.length = 0;
+ }
+
+ E removeAt(int index) {
+ if (index is! int) throw new ArgumentError(index);
+ E result = this[index];
+ int newLength = this.length - 1;
+ Arrays.copy(this,
+ index + 1,
+ this,
+ index,
+ newLength - index);
+ this.length = newLength;
+ return result;
+ }
+
+ void setRange(int start, int length, List<E> from, [int startFrom = 0]) {
+ Arrays.copy(from, startFrom, this, start, length);
+ }
+
+ void removeRange(int start, int length) {
+ if (length == 0) {
+ return;
+ }
+ Arrays.rangeCheck(this, start, length);
+ Arrays.copy(this,
+ start + length,
+ this,
+ start,
+ this.length - length - start);
+ this.length = this.length - length;
+ }
+
+ void insertRange(int start, int length, [E initialValue]) {
+ if (length == 0) {
+ return;
+ }
+ if ((length < 0) || (length is! int)) {
+ throw new ArgumentError("invalid length specified $length");
+ }
+ if (start < 0 || start > this.length) {
+ throw new RangeError.value(start);
+ }
+ var oldLength = this.length;
+ this.length = oldLength + length; // Will expand if needed.
+ Arrays.copy(this,
+ start,
+ this,
+ start + length,
+ oldLength - start);
+ for (int i = start; i < start + length; i++) {
+ this[i] = initialValue;
+ }
+ }
+
+ String toString() => Collections.collectionToString(this);
+
+ // ---------------------------------------------------------------------------
+ // Note: below this comment, methods are copy+paste from Collection<E> and
+ // Iterable<E>. Remove when we have mixins.
+ // ---------------------------------------------------------------------------
+
+ void addAll(Iterable<E> elements) {
+ for (E element in elements) add(element);
+ }
+
+ List<E> get reversed => new ReversedListView<E>(this, 0, length);
+
+ void removeAll(Iterable elements) =>
+ IterableMixinWorkaround.removeAll(this, elements);
+ void retainAll(Iterable elements) =>
+ IterableMixinWorkaround.retainAll(this, elements);
+ void removeMatching(bool test(E element)) =>
+ IterableMixinWorkaround.removeMatching(this, test);
+ void retainMatching(bool test(E element)) =>
+ IterableMixinWorkaround.retainMatching(this, test);
+ Iterable mappedBy(f(E element)) =>
+ IterableMixinWorkaround.mappedByList(this, f);
+ Iterable<E> where(bool f(E element)) =>
+ IterableMixinWorkaround.where(this, f);
+ bool contains(E element) => IterableMixinWorkaround.contains(this, element);
+ void forEach(void f(E element)) => IterableMixinWorkaround.forEach(this, f);
+ dynamic reduce(var initialValue,
+ dynamic combine(var previousValue, E element)) =>
+ IterableMixinWorkaround.reduce(this, initialValue, combine);
+ bool every(bool f(E element)) => IterableMixinWorkaround.every(this, f);
+ String join([String separator]) =>
+ IterableMixinWorkaround.join(this, separator);
+ bool any(bool f(E element)) => IterableMixinWorkaround.any(this, f);
+ List<E> toList() => new List<E>.from(this);
+ Set<E> toSet() => new Set<E>.from(this);
+ E min([int compare(E a, E b)]) => IterableMixinWorkaround.min(this, compare);
+ E max([int compare(E a, E b)]) => IterableMixinWorkaround.max(this, compare);
+ Iterable<E> take(int n) => IterableMixinWorkaround.takeList(this, n);
+ Iterable<E> takeWhile(bool test(E value))
+ => IterableMixinWorkaround.takeWhile(this, test);
+ Iterable<E> skip(int n) => IterableMixinWorkaround.skipList(this, n);
+ Iterable<E> skipWhile(bool test(E value)) =>
+ IterableMixinWorkaround.skipWhile(this, test);
+ E get single => IterableMixinWorkaround.single(this);
+ E firstMatching(bool test(E value), { E orElse() }) =>
+ IterableMixinWorkaround.firstMatching(this, test, orElse);
+ E lastMatching(bool test(E value), {E orElse()}) =>
+ IterableMixinWorkaround.lastMatching(this, test, orElse);
+ E singleMatching(bool test(E value)) =>
+ IterableMixinWorkaround.singleMatching(this, test);
+ E elementAt(int index) => IterableMixinWorkaround.elementAt(this, index);
+}
« no previous file with comments | « lib/observe/expression.dart ('k') | lib/observe/map.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698