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

Side by Side 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, 10 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 unified diff | Download patch
« no previous file with comments | « lib/observe/expression.dart ('k') | lib/observe/map.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 library web_ui.observe.list;
6
7 import 'dart:collection';
8 import 'dart:collection-dev';
9 import 'package:web_ui/observe.dart';
10
11 // TODO(jmesserly): this should extend the real list implementation.
12 // See http://dartbug.com/2600. The workaround was to copy+paste lots of code
13 // from the VM.
14 // TODO(jmesserly): also this shold extend Collection<E>, so we can delete
15 // copy+paste code. Fix once we have mixins.
16 /**
17 * Represents an observable list of model values. If any items are added,
18 * removed, or replaced, then observers that are registered with
19 * [observe] will be notified.
20 */
21 class ObservableList<E> extends Observable implements List<E> {
22 /** The inner [List<E>] with the actual storage. */
23 final List<E> _list;
24
25 /**
26 * Creates an observable list of the given [length].
27 *
28 * If no [length] argument is supplied an extendable list of
29 * length 0 is created.
30 *
31 * If a [length] argument is supplied, a fixed size list of that
32 * length is created.
33 */
34 ObservableList([int length = 0]) : _list = new List<E>(length);
35
36 /**
37 * Creates an observable list with the elements of [other]. The order in
38 * the list will be the order provided by the iterator of [other].
39 */
40 factory ObservableList.from(Iterable<E> other)
41 => new ObservableList<E>()..addAll(other);
42
43 // TODO(jmesserly): This should be on List.
44 // See http://code.google.com/p/dart/issues/detail?id=947
45 bool remove(E item) {
46 int i = indexOf(item);
47 if (i == -1) return false;
48 removeAt(i);
49 return true;
50 }
51
52 // TODO(jmesserly): This should be on List, to match removeAt.
53 // See http://code.google.com/p/dart/issues/detail?id=5375
54 void insertAt(int index, E item) => insertRange(index, 1, item);
55
56 E get first => this[0];
57
58 Iterator<E> get iterator => new ListIterator<E>(this);
59
60 int get length {
61 if (observeReads) notifyRead(ChangeRecord.FIELD, 'length');
62 return _list.length;
63 }
64
65 set length(int value) {
66 int len = _list.length;
67 if (len == value) return;
68
69 // Produce notifications if needed
70 if (hasObservers) {
71 if (value < len) {
72 // Remove items, then adjust length
73 for (int i = len - 1; i >= value; i--) {
74 notifyChange(ChangeRecord.INDEX, i, null, null);
75 }
76 notifyChange(ChangeRecord.FIELD, 'length', len, value);
77 } else {
78 // Adjust length then add items
79 notifyChange(ChangeRecord.FIELD, 'length', len, value);
80 for (int i = len; i < value; i++) {
81 notifyChange(ChangeRecord.INDEX, i, null, null);
82 }
83 }
84 }
85
86 _list.length = value;
87 }
88
89 E operator [](int index) {
90 if (observeReads) notifyRead(ChangeRecord.INDEX, index);
91 return _list[index];
92 }
93
94 operator []=(int index, E value) {
95 var oldValue = _list[index];
96 if (hasObservers) notifyChange(ChangeRecord.INDEX, index, oldValue, value);
97 _list[index] = value;
98 }
99
100 void add(E value) {
101 int len = _list.length;
102 if (hasObservers) {
103 notifyChange(ChangeRecord.FIELD, 'length', len, len + 1);
104 notifyChange(ChangeRecord.INDEX, len, null, value);
105 }
106 _list.add(value);
107 }
108
109 // ---------------------------------------------------------------------------
110 // Note: below this comment, methods are either:
111 // * redirect to Arrays
112 // * copy+paste from VM GrowableObjectArray.
113 // The general idea is to have these methods operate in terms of our primitive
114 // methods above, so they correctly track reads/writes.
115 // ---------------------------------------------------------------------------
116
117 E removeLast() {
118 var len = length - 1;
119 var elem = this[len];
120 length = len;
121 return elem;
122 }
123
124 int indexOf(E element, [int start = 0]) =>
125 Arrays.indexOf(this, element, start, length);
126
127 int lastIndexOf(E element, [int start]) =>
128 Arrays.lastIndexOf(this, element, start);
129
130 ObservableList<E> getRange(int start, int length) {
131 if (length == 0) return [];
132 Arrays.rangeCheck(this, start, length);
133 List list = new ObservableList<E>(length);
134 Arrays.copy(this, start, list, 0, length);
135 return list;
136 }
137
138 bool get isEmpty => length == 0;
139
140 E get last => this[length - 1];
141
142 void addLast(E value) => add(value);
143
144 void sort([compare = Comparable.compare]) =>
145 IterableMixinWorkaround.sortList(this, compare);
146
147 void clear() {
148 this.length = 0;
149 }
150
151 E removeAt(int index) {
152 if (index is! int) throw new ArgumentError(index);
153 E result = this[index];
154 int newLength = this.length - 1;
155 Arrays.copy(this,
156 index + 1,
157 this,
158 index,
159 newLength - index);
160 this.length = newLength;
161 return result;
162 }
163
164 void setRange(int start, int length, List<E> from, [int startFrom = 0]) {
165 Arrays.copy(from, startFrom, this, start, length);
166 }
167
168 void removeRange(int start, int length) {
169 if (length == 0) {
170 return;
171 }
172 Arrays.rangeCheck(this, start, length);
173 Arrays.copy(this,
174 start + length,
175 this,
176 start,
177 this.length - length - start);
178 this.length = this.length - length;
179 }
180
181 void insertRange(int start, int length, [E initialValue]) {
182 if (length == 0) {
183 return;
184 }
185 if ((length < 0) || (length is! int)) {
186 throw new ArgumentError("invalid length specified $length");
187 }
188 if (start < 0 || start > this.length) {
189 throw new RangeError.value(start);
190 }
191 var oldLength = this.length;
192 this.length = oldLength + length; // Will expand if needed.
193 Arrays.copy(this,
194 start,
195 this,
196 start + length,
197 oldLength - start);
198 for (int i = start; i < start + length; i++) {
199 this[i] = initialValue;
200 }
201 }
202
203 String toString() => Collections.collectionToString(this);
204
205 // ---------------------------------------------------------------------------
206 // Note: below this comment, methods are copy+paste from Collection<E> and
207 // Iterable<E>. Remove when we have mixins.
208 // ---------------------------------------------------------------------------
209
210 void addAll(Iterable<E> elements) {
211 for (E element in elements) add(element);
212 }
213
214 List<E> get reversed => new ReversedListView<E>(this, 0, length);
215
216 void removeAll(Iterable elements) =>
217 IterableMixinWorkaround.removeAll(this, elements);
218 void retainAll(Iterable elements) =>
219 IterableMixinWorkaround.retainAll(this, elements);
220 void removeMatching(bool test(E element)) =>
221 IterableMixinWorkaround.removeMatching(this, test);
222 void retainMatching(bool test(E element)) =>
223 IterableMixinWorkaround.retainMatching(this, test);
224 Iterable mappedBy(f(E element)) =>
225 IterableMixinWorkaround.mappedByList(this, f);
226 Iterable<E> where(bool f(E element)) =>
227 IterableMixinWorkaround.where(this, f);
228 bool contains(E element) => IterableMixinWorkaround.contains(this, element);
229 void forEach(void f(E element)) => IterableMixinWorkaround.forEach(this, f);
230 dynamic reduce(var initialValue,
231 dynamic combine(var previousValue, E element)) =>
232 IterableMixinWorkaround.reduce(this, initialValue, combine);
233 bool every(bool f(E element)) => IterableMixinWorkaround.every(this, f);
234 String join([String separator]) =>
235 IterableMixinWorkaround.join(this, separator);
236 bool any(bool f(E element)) => IterableMixinWorkaround.any(this, f);
237 List<E> toList() => new List<E>.from(this);
238 Set<E> toSet() => new Set<E>.from(this);
239 E min([int compare(E a, E b)]) => IterableMixinWorkaround.min(this, compare);
240 E max([int compare(E a, E b)]) => IterableMixinWorkaround.max(this, compare);
241 Iterable<E> take(int n) => IterableMixinWorkaround.takeList(this, n);
242 Iterable<E> takeWhile(bool test(E value))
243 => IterableMixinWorkaround.takeWhile(this, test);
244 Iterable<E> skip(int n) => IterableMixinWorkaround.skipList(this, n);
245 Iterable<E> skipWhile(bool test(E value)) =>
246 IterableMixinWorkaround.skipWhile(this, test);
247 E get single => IterableMixinWorkaround.single(this);
248 E firstMatching(bool test(E value), { E orElse() }) =>
249 IterableMixinWorkaround.firstMatching(this, test, orElse);
250 E lastMatching(bool test(E value), {E orElse()}) =>
251 IterableMixinWorkaround.lastMatching(this, test, orElse);
252 E singleMatching(bool test(E value)) =>
253 IterableMixinWorkaround.singleMatching(this, test);
254 E elementAt(int index) => IterableMixinWorkaround.elementAt(this, index);
255 }
OLDNEW
« 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