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

Side by Side Diff: lib/observe/map.dart

Issue 12225039: Support for observable models, fixes #259 (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
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 library web_ui.observe.map;
6
7 import 'dart:collection';
8 import 'package:web_ui/observe.dart';
9 import 'list.dart';
10
11 typedef Map<K, dynamic> MapFactory<K>();
12
13 // TODO(jmesserly): this needs to be faster. We currently require multiple
14 // lookups per key to get the old value. Most likely this needs to be based on
15 // a modified HashMap source code.
16 /**
17 * Represents an observable map 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 ObservableMap<K, V> implements Map<K, V> {
22 final Map<K, V> _map;
23 final Map<K, Object> _observeKey;
24 Object _observeLength;
25 _ObservableMapKeyIterable<K, V> _keys;
26 _ObservableMapValueIterable<K, V> _values;
27
28 /**
29 * Creates an observable map, optionally using the provided factory
30 * [createMap] to construct a custom map type.
31 */
32 ObservableMap({MapFactory<K> createMap})
33 : _map = createMap != null ? createMap() : new Map<K, V>(),
34 _observeKey = createMap != null ? createMap() : new Map<K, Object>() {
35 _keys = new _ObservableMapKeyIterable<K, V>(this);
36 _values = new _ObservableMapValueIterable<K, V>(this);
37 }
38
39 /** Creates a new observable map using a [LinkedHashMap]. */
40 ObservableMap.linked() : this(createMap: () => new LinkedHashMap());
41
42 /**
43 * Creates an observable map that contains all key value pairs of [other].
44 */
45 factory ObservableMap.from(Map<K, V> other, {MapFactory<K> createMap}) {
46 var result = new ObservableMap<K, V>(createMap: createMap);
47 other.forEach((K key, V value) { result[key] = value; });
48 return result;
49 }
50
51
52 Iterable<K> get keys => _keys;
53
54 Iterable<V> get values => _values;
55
56 int get length {
57 _notifyReadLength();
58 return _map.length;
59 }
60
61 bool get isEmpty => length == 0;
62
63 void _notifyReadKey(K key) {
64 if (observeReads) _observeKey[key] = notifyRead(_observeKey[key]);
65 }
66
67 void _notifyReadLength() {
68 if (observeReads) _observeLength = notifyRead(_observeLength);
69 }
70
71 void _notifyReadAll() {
72 if (!observeReads) return;
73 _observeLength = notifyRead(_observeLength);
74 for (K key in _map.keys) {
75 _observeKey[key] = notifyRead(_observeKey[key]);
76 }
77 }
78
79 void _notifyWriteLength(int originalLength) {
80 if (_observeLength != null && originalLength != _map.length) {
81 _observeLength = notifyWrite(_observeLength);
82 }
83 }
84
85 void _notifyWriteKey(K key) {
86 var observer = _observeKey.remove(key);
87 if (observer != null) notifyWrite(observer);
88 }
89
90 bool containsValue(V value) {
91 _notifyReadAll();
92 return _map.containsValue(value);
93 }
94
95 bool containsKey(K key) {
96 _notifyReadKey(key);
97 return _map.containsKey(key);
98 }
99
100 V operator [](K key) {
101 _notifyReadKey(key);
102 return _map[key];
103 }
104
105 void operator []=(K key, V value) {
106 int len = _map.length;
107 V oldValue = _map[key];
108 _map[key] = value;
109 // Note: if length changed, it means the key was added, so we need to
110 // _notifyWriteKey. Also _notifyWriteLength will check if length changed.
111 if (len != _map.length || oldValue != value) {
112 _notifyWriteKey(key);
113 _notifyWriteLength(len);
114 }
115 }
116
117 V putIfAbsent(K key, V ifAbsent()) {
118 // notifyRead because result depends on if the key already exists
119 _notifyReadKey(key);
120
121 int len = _map.length;
122 V result = _map.putIfAbsent(key, ifAbsent);
123 // Note: if length changed, it means the key was added, so we need to
124 // _notifyWriteKey. Also _notifyWriteLength will check if length changed.
Siggi Cherem (dart-lang) 2013/02/13 19:28:54 yeah, this one is more obvious, because if it is n
Jennifer Messerly 2013/02/14 00:38:09 Done.
125 if (len != _map.length) {
126 _notifyWriteKey(key);
127 _notifyWriteLength(len);
128 }
129 return result;
130 }
131
132 V remove(K key) {
133 // notifyRead because result depends on if the key already exists
134 _notifyReadKey(key);
135
136 int len = _map.length;
137 V result = _map.remove(key);
138 if (len != _map.length) {
139 _notifyWriteKey(key);
140 _notifyWriteLength(len);
141 }
142 return result;
143 }
144
145 void clear() {
146 int len = _map.length;
147 _map.clear();
148 _notifyWriteLength(len);
149 _observeKey.values.forEach(notifyWrite);
150 _observeKey.clear();
151 }
152
153 void forEach(void f(K key, V value)) {
154 _notifyReadAll();
155 _map.forEach(f);
156 }
157
158 String toString() => Maps.mapToString(this);
159 }
160
161 class _ObservableMapKeyIterable<K, V> extends Iterable<K> {
162 final ObservableMap<K, V> _map;
163 _ObservableMapKeyIterable(this._map);
164
165 Iterator<K> get iterator => new _ObservableMapKeyIterator<K, V>(_map);
166 }
167
168 class _ObservableMapKeyIterator<K, V> implements Iterator<K> {
169 final ObservableMap<K, V> _map;
170 final Iterator<K> _keys;
171
172 _ObservableMapKeyIterator(ObservableMap<K, V> map)
173 : _map = map,
174 _keys = map._map.keys.iterator;
175
176 bool moveNext() {
177 _map._notifyReadLength();
178 return _keys.moveNext();
179 }
180
181 K get current {
182 var key = _keys.current;
183 if (key != null) _map._notifyReadKey(key);
184 return key;
185 }
186 }
187
188
189 class _ObservableMapValueIterable<K, V> extends Iterable<V> {
190 final ObservableMap<K, V> _map;
191 _ObservableMapValueIterable(this._map);
192
193 Iterator<V> get iterator => new _ObservableMapValueIterator<K, V>(_map);
194 }
195
196 class _ObservableMapValueIterator<K, V> implements Iterator<V> {
197 final ObservableMap<K, V> _map;
198 final Iterator<K> _keys;
199 final Iterator<V> _values;
200
201 _ObservableMapValueIterator(ObservableMap<K, V> map)
202 : _map = map,
203 _keys = map._map.keys.iterator,
204 _values = map._map.values.iterator;
205
206 bool moveNext() {
207 _map._notifyReadLength();
208 bool moreKeys = _keys.moveNext();
209 bool moreValues = _values.moveNext();
210 if (moreKeys != moreValues) {
211 throw new StateError('keys and values should be the same length');
212 }
213 return moreValues;
214 }
215
216 V get current {
217 var key = _keys.current;
218 if (key != null) _map._notifyReadKey(key);
219 return _values.current;
220 }
221 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698