Line data Source code
1 : // Copyright (c) 2014, 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 : import 'dart:collection';
6 :
7 : import 'utils.dart';
8 :
9 : typedef C _Canonicalize<C, K>(K key);
10 :
11 : typedef bool _IsValidKey(Object key);
12 :
13 : /// A map whose keys are converted to canonical values of type `C`.
14 : ///
15 : /// This is useful for using case-insensitive String keys, for example. It's
16 : /// more efficient than a [LinkedHashMap] with a custom equality operator
17 : /// because it only canonicalizes each key once, rather than doing so for each
18 : /// comparison.
19 : ///
20 : /// By default, `null` is allowed as a key. It can be forbidden via the
21 : /// `isValidKey` parameter.
22 : class CanonicalizedMap<C, K, V> implements Map<K, V> {
23 : final _Canonicalize<C, K> _canonicalize;
24 :
25 : final _IsValidKey _isValidKeyFn;
26 :
27 : final _base = new Map<C, Pair<K, V>>();
28 :
29 : /// Creates an empty canonicalized map.
30 : ///
31 : /// The [canonicalize] function should return the canonical value for the
32 : /// given key. Keys with the same canonical value are considered equivalent.
33 : ///
34 : /// The [isValidKey] function is called before calling [canonicalize] for
35 : /// methods that take arbitrary objects. It can be used to filter out keys
36 : /// that can't be canonicalized.
37 : CanonicalizedMap(C canonicalize(K key), {bool isValidKey(Object key)})
38 : : _canonicalize = canonicalize,
39 0 : _isValidKeyFn = isValidKey;
40 :
41 : /// Creates a canonicalized map that is initialized with the key/value pairs
42 : /// of [other].
43 : ///
44 : /// The [canonicalize] function should return the canonical value for the
45 : /// given key. Keys with the same canonical value are considered equivalent.
46 : ///
47 : /// The [isValidKey] function is called before calling [canonicalize] for
48 : /// methods that take arbitrary objects. It can be used to filter out keys
49 : /// that can't be canonicalized.
50 : CanonicalizedMap.from(Map<K, V> other, C canonicalize(K key),
51 : {bool isValidKey(Object key)})
52 : : _canonicalize = canonicalize,
53 0 : _isValidKeyFn = isValidKey {
54 0 : addAll(other);
55 : }
56 :
57 : V operator [](Object key) {
58 0 : if (!_isValidKey(key)) return null;
59 0 : var pair = _base[_canonicalize(key as K)];
60 0 : return pair == null ? null : pair.last;
61 : }
62 :
63 : void operator []=(K key, V value) {
64 0 : if (!_isValidKey(key)) return;
65 0 : _base[_canonicalize(key)] = new Pair(key, value);
66 : }
67 :
68 : void addAll(Map<K, V> other) {
69 0 : other.forEach((key, value) => this[key] = value);
70 : }
71 :
72 : void clear() {
73 0 : _base.clear();
74 : }
75 :
76 : bool containsKey(Object key) {
77 0 : if (!_isValidKey(key)) return false;
78 0 : return _base.containsKey(_canonicalize(key as K));
79 : }
80 :
81 : bool containsValue(Object value) =>
82 0 : _base.values.any((pair) => pair.last == value);
83 :
84 : void forEach(void f(K key, V value)) {
85 0 : _base.forEach((key, pair) => f(pair.first, pair.last));
86 : }
87 :
88 0 : bool get isEmpty => _base.isEmpty;
89 :
90 0 : bool get isNotEmpty => _base.isNotEmpty;
91 :
92 0 : Iterable<K> get keys => _base.values.map((pair) => pair.first);
93 :
94 0 : int get length => _base.length;
95 :
96 : V putIfAbsent(K key, V ifAbsent()) {
97 0 : return _base
98 0 : .putIfAbsent(_canonicalize(key), () => new Pair(key, ifAbsent()))
99 0 : .last;
100 : }
101 :
102 : V remove(Object key) {
103 0 : if (!_isValidKey(key)) return null;
104 0 : var pair = _base.remove(_canonicalize(key as K));
105 0 : return pair == null ? null : pair.last;
106 : }
107 :
108 0 : Iterable<V> get values => _base.values.map((pair) => pair.last);
109 :
110 0 : String toString() => Maps.mapToString(this);
111 :
112 : bool _isValidKey(Object key) =>
113 0 : (key == null || key is K) &&
114 0 : (_isValidKeyFn == null || _isValidKeyFn(key));
115 : }
|