OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012, 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.collection; | |
6 | |
7 /** | |
8 * Base class for implementing a [Map]. | |
9 * | |
10 * This class has a basic implementation of all but five of the members of | |
11 * [Map]. | |
12 * A basic `Map` class can be implemented by extending this class and | |
13 * implementing `keys`, `operator[]`, `operator[]=`, `remove` and `clear`. | |
14 * The remaining operations are implemented in terms of these five. | |
15 * | |
16 * The `keys` iterable should have efficient [length] and [contains] | |
17 * operations, and it should catch concurrent modifications of the keys | |
18 * while iterating. | |
19 * | |
20 * A more efficient implementation is usually possible by overriding | |
21 * some of the other members as well. | |
22 */ | |
23 abstract class MapBase<K, V> = Object with MapMixin<K, V>; | |
24 | |
25 | |
26 /** | |
27 * Mixin implementing a [Map]. | |
28 * | |
29 * This mixin has a basic implementation of all but five of the members of | |
30 * [Map]. | |
31 * A basic `Map` class can be implemented by mixin in this class and | |
32 * implementing `keys`, `operator[]`, `operator[]=`, `remove` and `clear`. | |
33 * The remaining operations are implemented in terms of these five. | |
34 * | |
35 * The `keys` iterable should have efficient [length] and [contains] | |
36 * operations, and it should catch concurrent modifications of the keys | |
37 * while iterating. | |
38 * | |
39 * A more efficient implementation is usually possible by overriding | |
40 * some of the other members as well. | |
41 */ | |
42 abstract class MapMixin<K, V> implements Map<K, V> { | |
43 Iterable<K> get keys; | |
44 V operator[](Object key); | |
45 operator []=(K key, V value); | |
46 V remove(Object key); | |
47 // The `clear` operation should not be based on `remove`. | |
48 // It should clear the map even if some keys are not equal to themselves. | |
49 void clear(); | |
50 | |
51 void forEach(void action(K key, V value)) { | |
52 for (K key in keys) { | |
53 action(key, this[key]); | |
54 } | |
55 } | |
56 | |
57 void addAll(Map<K, V> other) { | |
58 for (K key in other.keys) { | |
59 this[key] = other[key]; | |
60 } | |
61 } | |
62 | |
63 bool containsValue(Object value) { | |
64 for (K key in keys) { | |
65 if (this[key] == value) return true; | |
66 } | |
67 return false; | |
68 } | |
69 | |
70 V putIfAbsent(K key, V ifAbsent()) { | |
71 if (containsKey(key)) { | |
72 return this[key]; | |
73 } | |
74 return this[key] = ifAbsent(); | |
75 } | |
76 | |
77 bool containsKey(Object key) => keys.contains(key); | |
78 int get length => keys.length; | |
79 bool get isEmpty => keys.isEmpty; | |
80 bool get isNotEmpty => keys.isNotEmpty; | |
81 Iterable<V> get values => new _MapBaseValueIterable<K, V>(this); | |
82 String toString() => Maps.mapToString(this); | |
83 } | |
84 | |
85 /** | |
86 * Basic implementation of an unmodifiable [Map]. | |
87 * | |
88 * This class has a basic implementation of all but two of the members of | |
89 * an umodifiable [Map]. | |
90 * A simple unmodifiable `Map` class can be implemented by extending this | |
91 * class and implementing `keys` and `operator[]`. | |
92 * | |
93 * Modifying operations throw when used. | |
94 * The remaining non-modifying operations are implemented in terms of `keys` | |
95 * and `operator[]`. | |
96 * | |
97 * The `keys` iterable should have efficient [length] and [contains] | |
98 * operations, and it should catch concurrent modifications of the keys | |
99 * while iterating. | |
100 * | |
101 * A more efficient implementation is usually possible by overriding | |
102 * some of the other members as well. | |
103 */ | |
104 abstract class UnmodifiableMapBase<K, V> = | |
105 MapBase<K, V> with _UnmodifiableMapMixin<K, V>; | |
106 | |
107 /** | |
108 * Implementation of [Map.values] based on the map and its [Map.keys] iterable. | |
109 * | |
110 * Iterable that iterates over the values of a `Map`. | |
111 * It accesses the values by iterating over the keys of the map, and using the | |
112 * map's `operator[]` to lookup the keys. | |
113 */ | |
114 class _MapBaseValueIterable<K, V> extends Iterable<V> | |
115 implements EfficientLength { | |
116 final Map<K, V> _map; | |
117 _MapBaseValueIterable(this._map); | |
118 | |
119 int get length => _map.length; | |
120 bool get isEmpty => _map.isEmpty; | |
121 bool get isNotEmpty => _map.isNotEmpty; | |
122 V get first => _map[_map.keys.first]; | |
123 V get single => _map[_map.keys.single]; | |
124 V get last => _map[_map.keys.last]; | |
125 | |
126 Iterator<V> get iterator => new _MapBaseValueIterator<K, V>(_map); | |
127 } | |
128 | |
129 /** | |
130 * Iterator created by [_MapBaseValueIterable]. | |
131 * | |
132 * Iterates over the values of a map by iterating its keys and lookup up the | |
133 * values. | |
134 */ | |
135 class _MapBaseValueIterator<K, V> implements Iterator<V> { | |
136 final Iterator<K> _keys; | |
137 final Map<K, V> _map; | |
138 V _current = null; | |
139 | |
140 _MapBaseValueIterator(Map<K, V> map) | |
141 : _map = map, | |
142 _keys = map.keys.iterator; | |
143 | |
144 bool moveNext() { | |
145 if (_keys.moveNext()) { | |
146 _current = _map[_keys.current]; | |
147 return true; | |
148 } | |
149 _current = null; | |
150 return false; | |
151 } | |
152 | |
153 V get current => _current; | |
154 } | |
155 | |
156 /** | |
157 * Mixin that overrides mutating map operations with implementations that throw. | |
158 */ | |
159 abstract class _UnmodifiableMapMixin<K, V> implements Map<K, V> { | |
160 void operator[]=(K key, V value) { | |
161 throw new UnsupportedError("Cannot modify unmodifiable map"); | |
162 } | |
163 void addAll(Map<K, V> other) { | |
164 throw new UnsupportedError("Cannot modify unmodifiable map"); | |
165 } | |
166 void clear() { | |
167 throw new UnsupportedError("Cannot modify unmodifiable map"); | |
168 } | |
169 V remove(Object key) { | |
170 throw new UnsupportedError("Cannot modify unmodifiable map"); | |
171 } | |
172 V putIfAbsent(K key, V ifAbsent()) { | |
173 throw new UnsupportedError("Cannot modify unmodifiable map"); | |
174 } | |
175 } | |
176 | |
177 /** | |
178 * Wrapper around a class that implements [Map] that only exposes `Map` members. | |
179 * | |
180 * A simple wrapper that delegates all `Map` members to the map provided in the | |
181 * constructor. | |
182 * | |
183 * Base for delegating map implementations like [UnmodifiableMapView]. | |
184 */ | |
185 class MapView<K, V> implements Map<K, V> { | |
186 final Map<K, V> _map; | |
187 const MapView(Map<K, V> map) : _map = map; | |
188 | |
189 V operator[](Object key) => _map[key]; | |
190 void operator[]=(K key, V value) { _map[key] = value; } | |
191 void addAll(Map<K, V> other) { _map.addAll(other); } | |
192 void clear() { _map.clear(); } | |
193 V putIfAbsent(K key, V ifAbsent()) => _map.putIfAbsent(key, ifAbsent); | |
194 bool containsKey(Object key) => _map.containsKey(key); | |
195 bool containsValue(Object value) => _map.containsValue(value); | |
196 void forEach(void action(K key, V value)) { _map.forEach(action); } | |
197 bool get isEmpty => _map.isEmpty; | |
198 bool get isNotEmpty => _map.isNotEmpty; | |
199 int get length => _map.length; | |
200 Iterable<K> get keys => _map.keys; | |
201 V remove(Object key) => _map.remove(key); | |
202 String toString() => _map.toString(); | |
203 Iterable<V> get values => _map.values; | |
204 } | |
205 | |
206 /** | |
207 * View of a [Map] that disallow modifying the map. | |
208 * | |
209 * A wrapper around a `Map` that forwards all members to the map provided in | |
210 * the constructor, except for operations that modify the map. | |
211 * Modifying operations throw instead. | |
212 */ | |
213 class UnmodifiableMapView<K, V> = | |
214 MapView<K, V> with _UnmodifiableMapMixin<K, V>; | |
215 | |
216 /** | |
217 * Helper class which implements complex [Map] operations | |
218 * in term of basic ones ([Map.keys], [Map.operator []], | |
219 * [Map.operator []=] and [Map.remove].) Not all methods are | |
220 * necessary to implement each particular operation. | |
221 */ | |
222 class Maps { | |
223 static bool containsValue(Map map, Object value) { | |
224 for (final v in map.values) { | |
225 if (v == value) { | |
226 return true; | |
227 } | |
228 } | |
229 return false; | |
230 } | |
231 | |
232 static bool containsKey(Map map, Object key) { | |
233 for (final k in map.keys) { | |
234 if (k == key) { | |
235 return true; | |
236 } | |
237 } | |
238 return false; | |
239 } | |
240 | |
241 static putIfAbsent(Map map, key, ifAbsent()) { | |
242 if (map.containsKey(key)) { | |
243 return map[key]; | |
244 } | |
245 final v = ifAbsent(); | |
246 map[key] = v; | |
247 return v; | |
248 } | |
249 | |
250 static clear(Map map) { | |
251 for (final k in map.keys.toList()) { | |
252 map.remove(k); | |
253 } | |
254 } | |
255 | |
256 static forEach(Map map, void f(key, value)) { | |
257 for (final k in map.keys) { | |
258 f(k, map[k]); | |
259 } | |
260 } | |
261 | |
262 static Iterable getValues(Map map) { | |
263 return map.keys.map((key) => map[key]); | |
264 } | |
265 | |
266 static int length(Map map) => map.keys.length; | |
267 | |
268 static bool isEmpty(Map map) => map.keys.isEmpty; | |
269 | |
270 static bool isNotEmpty(Map map) => map.keys.isNotEmpty; | |
271 | |
272 /** | |
273 * Returns a string representing the specified map. The returned string | |
274 * looks like this: [:'{key0: value0, key1: value1, ... keyN: valueN}':]. | |
275 * The value returned by its [toString] method is used to represent each | |
276 * key or value. | |
277 * | |
278 * If the map collection contains a reference to itself, either | |
279 * directly as a key or value, or indirectly through other collections | |
280 * or maps, the contained reference is rendered as [:'{...}':]. This | |
281 * prevents the infinite regress that would otherwise occur. So, for example, | |
282 * calling this method on a map whose sole entry maps the string key 'me' | |
283 * to a reference to the map would return [:'{me: {...}}':]. | |
284 * | |
285 * A typical implementation of a map's [toString] method will | |
286 * simply return the results of this method applied to the collection. | |
287 */ | |
288 static String mapToString(Map m) { | |
289 // Reuse the list in IterableBase for detecting toString cycles. | |
290 if (_isToStringVisiting(m)) { return '{...}'; } | |
291 | |
292 var result = new StringBuffer(); | |
293 try { | |
294 _toStringVisiting.add(m); | |
295 result.write('{'); | |
296 bool first = true; | |
297 m.forEach((k, v) { | |
298 if(!first) { | |
299 result.write(', '); | |
300 } | |
301 first = false; | |
302 result.write(k); | |
303 result.write(': '); | |
304 result.write(v); | |
305 }); | |
306 result.write('}'); | |
307 } finally { | |
308 assert(identical(_toStringVisiting.last, m)); | |
309 _toStringVisiting.removeLast(); | |
310 } | |
311 | |
312 return result.toString(); | |
313 } | |
314 | |
315 static _id(x) => x; | |
316 | |
317 /** | |
318 * Fills a map with key/value pairs computed from [iterable]. | |
319 * | |
320 * This method is used by Map classes in the named constructor fromIterable. | |
321 */ | |
322 static void _fillMapWithMappedIterable(Map map, Iterable iterable, | |
323 key(element), value(element)) { | |
324 if (key == null) key = _id; | |
325 if (value == null) value = _id; | |
326 | |
327 for (var element in iterable) { | |
328 map[key(element)] = value(element); | |
329 } | |
330 } | |
331 | |
332 /** | |
333 * Fills a map by associating the [keys] to [values]. | |
334 * | |
335 * This method is used by Map classes in the named constructor fromIterables. | |
336 */ | |
337 static void _fillMapWithIterables(Map map, Iterable keys, | |
338 Iterable values) { | |
339 Iterator keyIterator = keys.iterator; | |
340 Iterator valueIterator = values.iterator; | |
341 | |
342 bool hasNextKey = keyIterator.moveNext(); | |
343 bool hasNextValue = valueIterator.moveNext(); | |
344 | |
345 while (hasNextKey && hasNextValue) { | |
346 map[keyIterator.current] = valueIterator.current; | |
347 hasNextKey = keyIterator.moveNext(); | |
348 hasNextValue = valueIterator.moveNext(); | |
349 } | |
350 | |
351 if (hasNextKey || hasNextValue) { | |
352 throw new ArgumentError("Iterables do not have same length."); | |
353 } | |
354 } | |
355 } | |
OLD | NEW |