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 // 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 } | |
OLD | NEW |