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

Side by Side Diff: sdk/lib/observe/observable_list.dart

Issue 14732003: Implement Model-Driven-Views spec for Dart (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: small fix Created 7 years, 7 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 | Annotate | Revision Log
OLDNEW
(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 part of dart.observe;
6
7 /**
8 * Represents an observable list of model values. If any items are added,
9 * removed, or replaced, then observers that are listening to [changes]
10 * will be notified.
11 */
12 class ObservableList<E> extends _ListBaseWorkaround with ObservableMixin
13 implements List<E> {
floitsch 2013/05/06 17:37:46 Add TODO: remove implements List<E> when switching
Jennifer Messerly 2013/05/07 05:43:38 Done.
14
15 /** The inner [List<E>] with the actual storage. */
16 final List<E> _list;
17
18 /**
19 * Creates an observable list of the given [length].
20 *
21 * If no [length] argument is supplied an extendable list of
22 * length 0 is created.
23 *
24 * If a [length] argument is supplied, a fixed size list of that
25 * length is created.
26 */
27 ObservableList([int length])
28 : _list = length != null ? new List<E>(length) : <E>[];
29
30 /**
31 * Creates an observable list with the elements of [other]. The order in
32 * the list will be the order provided by the iterator of [other].
33 */
34 factory ObservableList.from(Iterable<E> other) =>
35 new ObservableList<E>()..addAll(other);
floitsch 2013/05/06 17:37:46 Is this on purpose that we get the initial values
Jennifer Messerly 2013/05/07 05:43:38 Mainly just a simplification. It shouldn't record
36
37 // TODO(jmesserly): remove once we have mirrors
38 getValue(key) => key == 'length' ? length : null;
39 setValue(key, value) {
40 if (key == 'length') length = value;
41 }
42
43 int get length => _list.length;
44
45 set length(int value) {
46 int len = _list.length;
47 if (len == value) return;
48
49 // Produce notifications if needed
50 if (hasObservers) {
51 if (value < len) {
52 // Remove items, then adjust length. Note the reverse order.
53 for (int i = len - 1; i >= value; i--) {
54 notifyChange(i, _list[i], null, kind: ChangeRecord.REMOVE);
55 }
56 notifyChange('length', len, value);
57 } else {
58 // Adjust length then add items
59 notifyChange('length', len, value);
60 for (int i = len; i < value; i++) {
61 notifyChange(i, null, null, kind: ChangeRecord.INSERT);
62 }
63 }
64 }
65
66 _list.length = value;
67 }
68
69 E operator [](int index) => _list[index];
70
71 void operator []=(int index, E value) {
72 var oldValue = _list[index];
73 if (hasObservers) {
74 notifyChange(index, oldValue, value, kind: ChangeRecord.INDEX);
75 }
76 _list[index] = value;
77 }
78
79 // The following methods are here so that we can provide nice change events
80 // (insertions and removals). If we use the mixin implementation, we would
81 // only report changes on indices.
82 // TODO(jmesserly): do we need this now that we have [summarizeListChanges]?
83
84 void add(E value) {
85 int len = _list.length;
86 if (hasObservers) {
87 notifyChange('length', len, len + 1, kind: ChangeRecord.FIELD);
Lasse Reichstein Nielsen 2013/05/06 10:58:58 Have you considered using Symbol for field names i
Jennifer Messerly 2013/05/07 05:43:38 yeah. Chatted about this with Florian. It's tricky
88 notifyChange(len, null, value, kind: ChangeRecord.INSERT);
89 }
90
91 _list.add(value);
92 }
93
94 void addAll(Iterable<E> iterable) {
95 for (E element in iterable) {
96 add(element);
97 }
98 }
99
100 bool remove(Object element) {
101 for (int i = 0; i < this.length; i++) {
102 if (this[i] == element) {
103 removeRange(i, 1);
104 return true;
105 }
106 }
107 return false;
108 }
109
110 void removeRange(int start, int end) {
111 _rangeCheck(start, end);
112 if (hasObservers) {
113 for (int i = start; i < end; i++) {
114 notifyChange(i, this[i], null, kind: ChangeRecord.REMOVE);
115 }
116 }
117 int length = end - start;
118 setRange(start, this.length - length, this, end);
119 this.length -= length;
Lasse Reichstein Nielsen 2013/05/06 10:58:58 Why notifying about the REMOVE here too? The setRa
Jennifer Messerly 2013/05/07 05:43:38 good catch. we need setRange to notify about the c
120 }
121
122 void insertAll(int index, Iterable<E> iterable) {
123 if (index < 0 || index > length) {
124 throw new RangeError.range(index, 0, length);
125 }
126 // TODO(floitsch): we can probably detect more cases.
127 if (iterable is! List && iterable is! Set) {
128 iterable = iterable.toList();
129 }
130 int insertionLength = iterable.length;
131 // There might be errors after the length change, in which case the list
132 // will end up being modified but the operation not complete. Unless we
133 // always go through a "toList" we can't really avoid that.
134 this.length += insertionLength;
Lasse Reichstein Nielsen 2013/05/06 10:58:58 Again, this does a number of inserts at the end of
Jennifer Messerly 2013/05/07 05:43:38 Done.
135 setRange(index + insertionLength, this.length, this, index);
136
137 if (hasObservers) {
138 for (E element in iterable) {
139 notifyChange(index, _list[index], element, kind: ChangeRecord.INSERT);
140 _list[index++] = element;
141 }
142 } else {
143 setAll(index, iterable);
144 }
145 }
146
147 void insert(int index, E element) {
148 if (index < 0 || index > length) {
149 throw new RangeError.range(index, 0, length);
150 }
151 if (index == this.length) {
152 add(element);
153 return;
154 }
155 // We are modifying the length just below the is-check. Without the check
156 // Array.copy could throw an exception, leaving the list in a bad state
157 // (with a length that has been increased, but without a new element).
158 if (index is! int) throw new ArgumentError(index);
159 this.length++;
160 setRange(index + 1, this.length, this, index);
161 notifyChange(index, _list[index], element, kind: ChangeRecord.INSERT);
162 _list[index] = element;
163 }
164
165
166 E removeAt(int index) {
167 E result = this[index];
168 removeRange(index, index + 1);
169 return result;
170 }
171
172 void _rangeCheck(int start, int end) {
173 if (start < 0 || start > this.length) {
174 throw new RangeError.range(start, 0, this.length);
175 }
176 if (end < start || end > this.length) {
177 throw new RangeError.range(end, start, this.length);
178 }
179 }
180 }
181
182
183 // TODO(jmesserly): bogus type to workaround spurious VM bug with generic base
184 // class and mixins. Can we remove now that we aren't in a package?
185 abstract class _ListBaseWorkaround extends ListBase<dynamic> {}
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698