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

Side by Side Diff: lib/src/observable_list.dart

Issue 1616953004: Fixed strong mode errors and warnings reachable from lib/observe.dart (Closed) Base URL: https://github.com/dart-lang/observe.git@master
Patch Set: Reformat Created 4 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 unified diff | Download patch
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 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 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. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library observe.src.observable_list; 5 library observe.src.observable_list;
6 6
7 import 'dart:async'; 7 import 'dart:async';
8 import 'dart:collection' show ListBase, UnmodifiableListView; 8 import 'dart:collection' show ListBase, UnmodifiableListView;
9 import 'package:observe/observe.dart'; 9 import 'package:observe/observe.dart';
10 import 'list_diff.dart' show projectListSplices, calcSplices; 10 import 'list_diff.dart' show projectListSplices, calcSplices;
11 11
12 /// Represents an observable list of model values. If any items are added, 12 /// Represents an observable list of model values. If any items are added,
13 /// removed, or replaced, then observers that are listening to [changes] 13 /// removed, or replaced, then observers that are listening to [changes]
14 /// will be notified. 14 /// will be notified.
15 class ObservableList<E> extends ListBase<E> with ChangeNotifier { 15 class ObservableList<E> extends ListBase<E> with ChangeNotifier {
16 List<ListChangeRecord> _listRecords; 16 List<ListChangeRecord> _listRecords;
17 17
18 StreamController _listChanges; 18 StreamController<List<ListChangeRecord>> _listChanges;
19 19
20 /// The inner [List<E>] with the actual storage. 20 /// The inner [List<E>] with the actual storage.
21 final List<E> _list; 21 final List<E> _list;
22 22
23 /// Creates an observable list of the given [length]. 23 /// Creates an observable list of the given [length].
24 /// 24 ///
25 /// If no [length] argument is supplied an extendable list of 25 /// If no [length] argument is supplied an extendable list of
26 /// length 0 is created. 26 /// length 0 is created.
27 /// 27 ///
28 /// If a [length] argument is supplied, a fixed size list of that 28 /// If a [length] argument is supplied, a fixed size list of that
(...skipping 22 matching lines...) Expand all
51 /// The change records will be summarized so they can be "played back", using 51 /// The change records will be summarized so they can be "played back", using
52 /// the final list positions to figure out which item was added: 52 /// the final list positions to figure out which item was added:
53 /// 53 ///
54 /// #<ListChangeRecord index: 0, removed: [], addedCount: 2> 54 /// #<ListChangeRecord index: 0, removed: [], addedCount: 2>
55 /// #<ListChangeRecord index: 3, removed: [b], addedCount: 0> 55 /// #<ListChangeRecord index: 3, removed: [b], addedCount: 0>
56 /// 56 ///
57 /// [deliverChanges] can be called to force synchronous delivery. 57 /// [deliverChanges] can be called to force synchronous delivery.
58 Stream<List<ListChangeRecord>> get listChanges { 58 Stream<List<ListChangeRecord>> get listChanges {
59 if (_listChanges == null) { 59 if (_listChanges == null) {
60 // TODO(jmesserly): split observed/unobserved notions? 60 // TODO(jmesserly): split observed/unobserved notions?
61 _listChanges = new StreamController.broadcast(sync: true, 61 _listChanges = new StreamController.broadcast(sync: true, onCancel: () {
62 onCancel: () { _listChanges = null; }); 62 _listChanges = null;
63 });
63 } 64 }
64 return _listChanges.stream; 65 return _listChanges.stream;
65 } 66 }
66 67
67 bool get hasListObservers => 68 bool get hasListObservers => _listChanges != null && _listChanges.hasListener;
68 _listChanges != null && _listChanges.hasListener;
69 69
70 @reflectable int get length => _list.length; 70 @reflectable int get length => _list.length;
71 71
72 @reflectable set length(int value) { 72 @reflectable set length(int value) {
73 int len = _list.length; 73 int len = _list.length;
74 if (len == value) return; 74 if (len == value) return;
75 75
76 // Produce notifications if needed 76 // Produce notifications if needed
77 _notifyChangeLength(len, value); 77 _notifyChangeLength(len, value);
78 if (hasListObservers) { 78 if (hasListObservers) {
79 if (value < len) { 79 if (value < len) {
80 _recordChange(new ListChangeRecord(this, value, 80 _recordChange(new ListChangeRecord(this, value,
81 removed: _list.getRange(value, len).toList())); 81 removed: _list.getRange(value, len).toList()));
82 } else { 82 } else {
83 _recordChange(new ListChangeRecord(this, len, addedCount: value - len)); 83 _recordChange(new ListChangeRecord(this, len, addedCount: value - len));
84 } 84 }
85 } 85 }
86 86
87 _list.length = value; 87 _list.length = value;
88 } 88 }
89 89
90 @reflectable E operator [](int index) => _list[index]; 90 @reflectable E operator [](int index) => _list[index];
91 91
92 @reflectable void operator []=(int index, E value) { 92 @reflectable void operator []=(int index, E value) {
93 var oldValue = _list[index]; 93 var oldValue = _list[index];
94 if (hasListObservers && oldValue != value) { 94 if (hasListObservers && oldValue != value) {
95 _recordChange(new ListChangeRecord(this, index, addedCount: 1, 95 _recordChange(new ListChangeRecord(this, index,
96 removed: [oldValue])); 96 addedCount: 1, removed: [oldValue]));
97 } 97 }
98 _list[index] = value; 98 _list[index] = value;
99 } 99 }
100 100
101 // Forwarders so we can reflect on the properties. 101 // Forwarders so we can reflect on the properties.
102 @reflectable bool get isEmpty => super.isEmpty; 102 @reflectable bool get isEmpty => super.isEmpty;
103 @reflectable bool get isNotEmpty => super.isNotEmpty; 103 @reflectable bool get isNotEmpty => super.isNotEmpty;
104 104
105 // TODO(jmesserly): should we support first/last/single? They're kind of 105 // TODO(jmesserly): should we support first/last/single? They're kind of
106 // dangerous to use in a path because they throw exceptions. Also we'd need 106 // dangerous to use in a path because they throw exceptions. Also we'd need
107 // to produce property change notifications which seems to conflict with our 107 // to produce property change notifications which seems to conflict with our
108 // existing list notifications. 108 // existing list notifications.
109 109
110 // The following methods are here so that we can provide nice change events. 110 // The following methods are here so that we can provide nice change events.
111 111
112 void setAll(int index, Iterable<E> iterable) { 112 void setAll(int index, Iterable<E> iterable) {
113 if (iterable is! List && iterable is! Set) { 113 if (iterable is! List && iterable is! Set) {
114 iterable = iterable.toList(); 114 iterable = iterable.toList();
115 } 115 }
116 var len = iterable.length; 116 var len = iterable.length;
Bob Nystrom 2016/01/22 18:50:25 Nit: "len" -> "length" since you're in here anywa
vsm 2016/01/22 19:05:05 Done.
117 if (hasListObservers && len > 0) { 117 if (hasListObservers && len > 0) {
118 _recordChange(new ListChangeRecord(this, index, addedCount: len, 118 _recordChange(new ListChangeRecord(this, index,
119 removed: _list.getRange(index, len).toList())); 119 addedCount: len, removed: _list.getRange(index, len).toList()));
Bob Nystrom 2016/01/22 18:50:25 "getRange(index, len).toList()" -> "sublist(index,
vsm 2016/01/22 19:05:05 Done.
120 } 120 }
121 _list.setAll(index, iterable); 121 _list.setAll(index, iterable);
122 } 122 }
123 123
124 void add(E value) { 124 void add(E value) {
125 int len = _list.length; 125 int len = _list.length;
126 _notifyChangeLength(len, len + 1); 126 _notifyChangeLength(len, len + 1);
127 if (hasListObservers) { 127 if (hasListObservers) {
128 _recordChange(new ListChangeRecord(this, len, addedCount: 1)); 128 _recordChange(new ListChangeRecord(this, len, addedCount: 1));
129 } 129 }
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
181 // always go through a "toList" we can't really avoid that. 181 // always go through a "toList" we can't really avoid that.
182 int len = _list.length; 182 int len = _list.length;
183 _list.length += insertionLength; 183 _list.length += insertionLength;
184 184
185 _list.setRange(index + insertionLength, this.length, this, index); 185 _list.setRange(index + insertionLength, this.length, this, index);
186 _list.setAll(index, iterable); 186 _list.setAll(index, iterable);
187 187
188 _notifyChangeLength(len, _list.length); 188 _notifyChangeLength(len, _list.length);
189 189
190 if (hasListObservers && insertionLength > 0) { 190 if (hasListObservers && insertionLength > 0) {
191 _recordChange(new ListChangeRecord(this, index, 191 _recordChange(
192 addedCount: insertionLength)); 192 new ListChangeRecord(this, index, addedCount: insertionLength));
193 } 193 }
194 } 194 }
195 195
196 void insert(int index, E element) { 196 void insert(int index, E element) {
197 if (index < 0 || index > length) { 197 if (index < 0 || index > length) {
198 throw new RangeError.range(index, 0, length); 198 throw new RangeError.range(index, 0, length);
199 } 199 }
200 if (index == length) { 200 if (index == length) {
201 add(element); 201 add(element);
202 return; 202 return;
203 } 203 }
204 // We are modifying the length just below the is-check. Without the check 204 // We are modifying the length just below the is-check. Without the check
205 // Array.copy could throw an exception, leaving the list in a bad state 205 // Array.copy could throw an exception, leaving the list in a bad state
206 // (with a length that has been increased, but without a new element). 206 // (with a length that has been increased, but without a new element).
207 if (index is! int) throw new ArgumentError(index); 207 if (index is! int) throw new ArgumentError(index);
208 _list.length++; 208 _list.length++;
209 _list.setRange(index + 1, length, this, index); 209 _list.setRange(index + 1, length, this, index);
210 210
211 _notifyChangeLength(_list.length - 1, _list.length); 211 _notifyChangeLength(_list.length - 1, _list.length);
212 if (hasListObservers) { 212 if (hasListObservers) {
213 _recordChange(new ListChangeRecord(this, index, addedCount: 1)); 213 _recordChange(new ListChangeRecord(this, index, addedCount: 1));
214 } 214 }
215 _list[index] = element; 215 _list[index] = element;
216 } 216 }
217 217
218
219 E removeAt(int index) { 218 E removeAt(int index) {
220 E result = this[index]; 219 E result = this[index];
221 removeRange(index, index + 1); 220 removeRange(index, index + 1);
222 return result; 221 return result;
223 } 222 }
224 223
225 void _rangeCheck(int start, int end) { 224 void _rangeCheck(int start, int end) {
226 if (start < 0 || start > this.length) { 225 if (start < 0 || start > this.length) {
227 throw new RangeError.range(start, 0, this.length); 226 throw new RangeError.range(start, 0, this.length);
228 } 227 }
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
274 /// This is not needed for change records produced by [ObservableList] itself, 273 /// This is not needed for change records produced by [ObservableList] itself,
275 /// but it can be used if the list instance was replaced by another list. 274 /// but it can be used if the list instance was replaced by another list.
276 /// 275 ///
277 /// The minimal set of splices can be synthesized given the previous state and 276 /// The minimal set of splices can be synthesized given the previous state and
278 /// final state of a list. The basic approach is to calculate the edit 277 /// final state of a list. The basic approach is to calculate the edit
279 /// distance matrix and choose the shortest path through it. 278 /// distance matrix and choose the shortest path through it.
280 /// 279 ///
281 /// Complexity is `O(l * p)` where `l` is the length of the current list and 280 /// Complexity is `O(l * p)` where `l` is the length of the current list and
282 /// `p` is the length of the old list. 281 /// `p` is the length of the old list.
283 static List<ListChangeRecord> calculateChangeRecords( 282 static List<ListChangeRecord> calculateChangeRecords(
284 List<Object> oldValue, List<Object> newValue) => 283 List<Object> oldValue, List<Object> newValue) =>
285 calcSplices(newValue, 0, newValue.length, oldValue, 0, oldValue.length); 284 calcSplices(newValue, 0, newValue.length, oldValue, 0, oldValue.length);
286 285
287 /// Updates the [previous] list using the change [records]. For added items, 286 /// Updates the [previous] list using the change [records]. For added items,
288 /// the [current] list is used to find the current value. 287 /// the [current] list is used to find the current value.
289 static void applyChangeRecords(List<Object> previous, List<Object> current, 288 static void applyChangeRecords(List<Object> previous, List<Object> current,
290 List<ListChangeRecord> changeRecords) { 289 List<ListChangeRecord> changeRecords) {
291
292 if (identical(previous, current)) { 290 if (identical(previous, current)) {
293 throw new ArgumentError("can't use same list for previous and current"); 291 throw new ArgumentError("can't use same list for previous and current");
294 } 292 }
295 293
296 for (var change in changeRecords) { 294 for (var change in changeRecords) {
297 int addEnd = change.index + change.addedCount; 295 int addEnd = change.index + change.addedCount;
298 int removeEnd = change.index + change.removed.length; 296 int removeEnd = change.index + change.removed.length;
299 297
300 var addedItems = current.getRange(change.index, addEnd); 298 var addedItems = current.getRange(change.index, addEnd);
301 previous.replaceRange(change.index, removeEnd, addedItems); 299 previous.replaceRange(change.index, removeEnd, addedItems);
302 } 300 }
303 } 301 }
304 } 302 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698