OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2013, 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 'package:web_ui/observe.dart'; | |
9 | |
10 // TODO(jmesserly): this should extend the real list implementation. | |
11 // See http://dartbug.com/2600. The workaround was to copy+paste lots of code | |
12 // from the VM. | |
13 /** | |
14 * Represents an observable list of model values. If any items are added, | |
15 * removed, or replaced, then observers that are registered with | |
16 * [observe] will be notified. | |
17 */ | |
18 class ObservableList<E> extends Collection<E> implements List<E> { | |
19 /** The inner [List<E>] with the actual storage. */ | |
20 final List<E> _list; | |
21 | |
22 final List<Object> _observeIndex; | |
23 Object _observeLength; | |
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]) | |
35 : _list = new List<E>(length), | |
36 _observeIndex = new List<Object>(length); | |
37 | |
38 /** | |
39 * Creates an observable list with the elements of [other]. The order in | |
40 * the list will be the order provided by the iterator of [other]. | |
41 */ | |
42 factory ObservableList.from(Iterable<E> other) => | |
43 new ObservableList<E>()..addAll(other); | |
44 | |
45 // TODO(jmesserly): This should be on List. | |
46 // See http://code.google.com/p/dart/issues/detail?id=947 | |
Siggi Cherem (dart-lang)
2013/02/13 01:43:24
947 seems to be marked as fixed... it seems that '
Jennifer Messerly
2013/02/13 05:43:15
yeah. It seems to be abstract though, so we still
| |
47 bool remove(E item) { | |
48 int i = indexOf(item); | |
49 if (i == -1) return false; | |
50 removeAt(i); | |
51 return true; | |
52 } | |
53 | |
54 // TODO(jmesserly): This should be on List, to match removeAt. | |
55 // See http://code.google.com/p/dart/issues/detail?id=5375 | |
56 void insertAt(int index, E item) => insertRange(index, 1, item); | |
57 | |
58 bool contains(E item) => Collections.contains(_list, item); | |
Siggi Cherem (dart-lang)
2013/02/13 01:43:24
Are you using Collections to skip over any overhea
Jennifer Messerly
2013/02/13 05:43:15
I think _list.contains would have less overhead. B
| |
59 | |
60 E get first => this[0]; | |
61 | |
62 Iterator<E> get iterator => new ListIterator<E>(this); | |
63 | |
64 int get length { | |
65 if (observeReads) _observeLength = notifyRead(_observeLength); | |
66 return _list.length; | |
67 } | |
68 | |
69 set length(int value) { | |
70 if (length == value) return; | |
71 | |
72 if (_observeLength != null) _observeLength = notifyWrite(_observeLength); | |
73 | |
74 // If we are shrinking the list, explicitly null out items so we track | |
75 // the change to those items. | |
76 for (int i = value; i < _list.length; i++) { | |
77 this[i] = null; | |
78 } | |
79 _observeIndex.length = value; | |
80 _list.length = value; | |
81 } | |
82 | |
83 E operator [](int index) { | |
84 if (observeReads) _observeIndex[index] = notifyRead(_observeIndex[index]); | |
85 return _list[index]; | |
86 } | |
87 | |
88 operator []=(int index, E value) { | |
89 var observer = _observeIndex[index]; | |
90 var oldValue = _list[index]; | |
91 if (observer != null && oldValue != value) { | |
92 _observeIndex[index] = notifyWrite(observer); | |
93 } | |
94 _list[index] = value; | |
95 } | |
96 | |
97 void add(E value) { | |
98 if (_observeLength != null) _observeLength = notifyWrite(_observeLength); | |
99 _list.add(value); | |
100 _observeIndex.add(null); | |
101 } | |
102 | |
103 // --------------------------------------------------------------------------- | |
104 // Note: below this comment, methods are either: | |
105 // * redirect to Arrays | |
106 // * redirect to Collections | |
107 // * copy+paste from VM GrowableObjectArray. | |
108 // The general idea is to have these methods operate in terms of our primitive | |
109 // methods above, so they correctly track reads/writes. | |
Siggi Cherem (dart-lang)
2013/02/13 01:43:24
consider moving contains, insertAt, first, and ite
Jennifer Messerly
2013/02/13 05:43:15
good call
| |
110 // --------------------------------------------------------------------------- | |
111 | |
112 E removeLast() { | |
113 var len = length - 1; | |
114 var elem = this[len]; | |
115 length = len; | |
116 return elem; | |
117 } | |
118 | |
119 int indexOf(E element, [int start = 0]) => | |
120 Arrays.indexOf(this, element, start, length); | |
121 | |
122 int lastIndexOf(E element, [int start]) => | |
123 Arrays.lastIndexOf(this, element, start); | |
124 | |
125 ObservableList<E> getRange(int start, int length) { | |
126 if (length == 0) return []; | |
127 Arrays.rangeCheck(this, start, length); | |
128 List list = new ObservableList<E>(length); | |
129 Arrays.copy(this, start, list, 0, length); | |
130 return list; | |
131 } | |
132 | |
133 bool get isEmpty => length == 0; | |
134 | |
135 E get last => this[length - 1]; | |
136 | |
137 void addLast(E value) => add(value); | |
138 | |
139 void addAll(Iterable<E> collection) { | |
140 for (E elem in collection) { | |
141 add(elem); | |
142 } | |
143 } | |
144 | |
145 void sort([compare = Comparable.compare]) => | |
146 IterableMixinWorkaround.sortList(this, compare); | |
147 | |
148 List<E> get reversed => IterableMixinWorkaround.reversedList(this); | |
149 | |
150 void clear() { | |
151 this.length = 0; | |
152 } | |
153 | |
154 E removeAt(int index) { | |
155 if (index is! int) throw new ArgumentError(index); | |
156 E result = this[index]; | |
157 int newLength = this.length - 1; | |
158 Arrays.copy(this, | |
159 index + 1, | |
160 this, | |
161 index, | |
162 newLength - index); | |
163 this.length = newLength; | |
164 return result; | |
165 } | |
166 | |
167 void setRange(int start, int length, List<E> from, [int startFrom = 0]) { | |
168 Arrays.copy(from, startFrom, this, start, length); | |
169 } | |
170 | |
171 void removeRange(int start, int length) { | |
172 if (length == 0) { | |
173 return; | |
174 } | |
175 Arrays.rangeCheck(this, start, length); | |
176 Arrays.copy(this, | |
177 start + length, | |
178 this, | |
179 start, | |
180 this.length - length - start); | |
181 this.length = this.length - length; | |
182 } | |
183 | |
184 void insertRange(int start, int length, [E initialValue]) { | |
185 if (length == 0) { | |
186 return; | |
187 } | |
188 if ((length < 0) || (length is! int)) { | |
189 throw new ArgumentError("invalid length specified $length"); | |
190 } | |
191 if (start < 0 || start > this.length) { | |
192 throw new RangeError.value(start); | |
193 } | |
194 var oldLength = this.length; | |
195 this.length = oldLength + length; // Will expand if needed. | |
196 Arrays.copy(this, | |
197 start, | |
198 this, | |
199 start + length, | |
200 oldLength - start); | |
201 for (int i = start; i < start + length; i++) { | |
202 this[i] = initialValue; | |
203 } | |
204 } | |
205 | |
206 String toString() => Collections.collectionToString(this); | |
207 } | |
208 | |
209 // TODO(jmesserly): copy+paste from collection-dev | |
Siggi Cherem (dart-lang)
2013/02/13 01:43:24
I assume collection-dev can't be used, or we shoud
Jennifer Messerly
2013/02/13 05:43:15
yup. According to Florian/Lasse everything you nee
| |
210 /** | |
211 * Iterates over a [List] in growing index order. | |
212 */ | |
213 class ListIterator<E> implements Iterator<E> { | |
214 final List<E> _list; | |
215 final int _length; | |
216 int _position; | |
217 E _current; | |
218 | |
219 ListIterator(List<E> list) | |
220 : _list = list, _position = -1, _length = list.length; | |
221 | |
222 bool moveNext() { | |
223 if (_list.length != _length) { | |
224 throw new ConcurrentModificationError(_list); | |
225 } | |
226 int nextPosition = _position + 1; | |
227 if (nextPosition < _length) { | |
228 _position = nextPosition; | |
229 _current = _list[nextPosition]; | |
230 return true; | |
231 } | |
232 _current = null; | |
233 return false; | |
234 } | |
235 | |
236 E get current => _current; | |
237 } | |
OLD | NEW |