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.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 if (len != _map.length || oldValue != value) { | |
110 _notifyWriteKey(key); | |
111 _notifyWriteLength(len); | |
Siggi Cherem (dart-lang)
2013/02/13 01:43:24
should this be separate? I mean,
if (oldValue !=
Jennifer Messerly
2013/02/13 05:43:15
Not quite, it was a bit tricky to get right. Added
Siggi Cherem (dart-lang)
2013/02/13 19:28:54
I see, so basically you are handling when the valu
Jennifer Messerly
2013/02/13 20:21:20
Uh, as I said in comment, _notifyWriteLength will
Siggi Cherem (dart-lang)
2013/02/13 20:41:13
My bad, I don't know how but I managed to complete
| |
112 } | |
113 } | |
114 | |
115 V putIfAbsent(K key, V ifAbsent()) { | |
116 // notifyRead because result depends on if the key already exists | |
117 _notifyReadKey(key); | |
118 | |
119 int len = _map.length; | |
120 V result = _map.putIfAbsent(key, ifAbsent); | |
121 if (len != _map.length) { | |
122 _notifyWriteKey(key); | |
123 _notifyWriteLength(len); | |
124 } | |
125 return result; | |
126 } | |
127 | |
128 V remove(K key) { | |
129 // notifyRead because result depends on if the key already exists | |
130 _notifyReadKey(key); | |
131 | |
132 int len = _map.length; | |
133 V result = _map.remove(key); | |
134 if (len != _map.length) { | |
135 _notifyWriteKey(key); | |
136 _notifyWriteLength(len); | |
137 } | |
138 return result; | |
139 } | |
140 | |
141 void clear() { | |
142 int len = _map.length; | |
143 _map.clear(); | |
144 _notifyWriteLength(len); | |
145 _observeKey.values.forEach(notifyWrite); | |
146 _observeKey.clear(); | |
147 } | |
148 | |
149 void forEach(void f(K key, V value)) { | |
150 _notifyReadAll(); | |
151 _map.forEach(f); | |
152 } | |
153 | |
154 String toString() => Maps.mapToString(this); | |
155 } | |
156 | |
157 class _ObservableMapKeyIterable<K, V> extends Iterable<K> { | |
158 final ObservableMap<K, V> _map; | |
159 _ObservableMapKeyIterable(this._map); | |
160 | |
161 Iterator<K> get iterator => new _ObservableMapKeyIterator<K, V>(_map); | |
162 } | |
163 | |
164 class _ObservableMapKeyIterator<K, V> implements Iterator<K> { | |
165 final ObservableMap<K, V> _map; | |
166 final Iterator<K> _keys; | |
167 | |
168 _ObservableMapKeyIterator(ObservableMap<K, V> map) | |
169 : _map = map, | |
170 _keys = map._map.keys.iterator; | |
171 | |
172 bool moveNext() { | |
173 _map._notifyReadLength(); | |
174 return _keys.moveNext(); | |
175 } | |
176 | |
177 K get current { | |
178 var key = _keys.current; | |
179 if (key != null) _map._notifyReadKey(key); | |
180 return key; | |
181 } | |
182 } | |
183 | |
184 | |
185 class _ObservableMapValueIterable<K, V> extends Iterable<V> { | |
186 final ObservableMap<K, V> _map; | |
187 _ObservableMapValueIterable(this._map); | |
188 | |
189 Iterator<V> get iterator => new _ObservableMapValueIterator<K, V>(_map); | |
190 } | |
191 | |
192 class _ObservableMapValueIterator<K, V> implements Iterator<V> { | |
193 final ObservableMap<K, V> _map; | |
194 final Iterator<K> _keys; | |
195 final Iterator<V> _values; | |
196 | |
197 _ObservableMapValueIterator(ObservableMap<K, V> map) | |
198 : _map = map, | |
199 _keys = map._map.keys.iterator, | |
200 _values = map._map.values.iterator; | |
201 | |
202 bool moveNext() { | |
203 _map._notifyReadLength(); | |
204 bool moreKeys = _keys.moveNext(); | |
205 bool moreValues = _values.moveNext(); | |
206 if (moreKeys != moreValues) { | |
207 throw new StateError('keys and values should be the same length'); | |
208 } | |
209 return moreValues; | |
210 } | |
211 | |
212 V get current { | |
213 var key = _keys.current; | |
214 if (key != null) _map._notifyReadKey(key); | |
215 return _values.current; | |
216 } | |
217 } | |
OLD | NEW |