OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Rev 83 | Go to most recent revision | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 83 Rev 182
Line 11... Line 11...
11
 * When distributing the software, include this License Header Notice in each file.
11
 * When distributing the software, include this License Header Notice in each file.
12
 */
12
 */
13
 
13
 
14
 package org.openconcerto.utils;
14
 package org.openconcerto.utils;
15
 
15
 
-
 
16
import java.util.AbstractMap;
16
import java.util.AbstractMap.SimpleImmutableEntry;
17
import java.util.AbstractSet;
17
import java.util.ArrayList;
18
import java.util.ArrayList;
18
import java.util.Collection;
19
import java.util.ConcurrentModificationException;
19
import java.util.HashSet;
-
 
20
import java.util.Iterator;
20
import java.util.Iterator;
21
import java.util.Map;
21
import java.util.Map;
-
 
22
import java.util.NoSuchElementException;
-
 
23
import java.util.Objects;
22
import java.util.Set;
24
import java.util.Set;
-
 
25
import java.util.function.Function;
23
 
26
 
24
public class TinyMap<K, V> implements Map<K, V> {
27
public class TinyMap<K, V> extends AbstractMap<K, V> {
-
 
28
    private static final Function<Entry<?, ?>, Object> KEY_GETTER = Entry::getKey;
-
 
29
    private static final Function<Entry<?, ?>, Object> VALUE_GETTER = Entry::getValue;
-
 
30
 
25
    private final ArrayList<K> keys;
31
    private final ArrayList<Entry<K, V>> entries;
26
    private final ArrayList<V> values;
32
    private transient Set<Map.Entry<K, V>> entrySet;
-
 
33
    /**
-
 
34
     * The number of times this HashMap has been structurally modified Structural modifications are
-
 
35
     * those that change the number of mappings in the HashMap or otherwise modify its internal
-
 
36
     * structure (e.g., rehash). This field is used to make iterators on Collection-views of the
-
 
37
     * HashMap fail-fast. (See ConcurrentModificationException).
-
 
38
     */
-
 
39
    transient int modCount = 0;
27
 
40
 
28
    public TinyMap() {
41
    public TinyMap() {
29
        this(10);
42
        this(10);
30
    }
43
    }
31
 
44
 
32
    public TinyMap(int initialCapacity) {
45
    public TinyMap(final int initialCapacity) {
33
        keys = new ArrayList<K>(initialCapacity);
46
        this.entries = new ArrayList<>(initialCapacity);
-
 
47
    }
-
 
48
 
-
 
49
    public TinyMap(final Map<? extends K, ? extends V> m) {
34
        values = new ArrayList<V>(initialCapacity);
50
        this.entries = new ArrayList<>(m.size());
-
 
51
        // don't call putAll() to avoid indexOfKey()
-
 
52
        for (final Entry<? extends K, ? extends V> e : m.entrySet()) {
-
 
53
            this.entries.add(new SimpleEntry<>(e));
-
 
54
        }
-
 
55
        ++this.modCount;
35
    }
56
    }
36
 
57
 
37
    @Override
58
    @Override
38
    public int size() {
59
    public int size() {
39
        return keys.size();
60
        return this.entries.size();
40
    }
61
    }
41
 
62
 
42
    @Override
63
    @Override
43
    public boolean isEmpty() {
64
    public boolean isEmpty() {
44
        return keys.isEmpty();
65
        return this.entries.isEmpty();
45
    }
66
    }
46
 
67
 
47
    @Override
68
    @Override
48
    public boolean containsKey(Object key) {
69
    public boolean containsKey(final Object key) {
49
        return keys.contains(key);
70
        return indexOfKey(key) >= 0;
50
    }
71
    }
51
 
72
 
52
    @Override
73
    @Override
53
    public boolean containsValue(Object value) {
74
    public boolean containsValue(final Object value) {
54
        return values.contains(value);
75
        return this.indexOf(value, VALUE_GETTER) >= 0;
55
    }
76
    }
56
 
77
 
57
    @Override
-
 
58
    public V get(Object key) {
78
    private int indexOfKey(final Object key) {
59
        final int size = this.keys.size();
-
 
60
        for (int i = 0; i < size; i++) {
-
 
61
            if (this.keys.get(i).equals(key)) {
-
 
62
                return this.values.get(i);
79
        return this.indexOf(key, KEY_GETTER);
63
            }
80
    }
-
 
81
 
-
 
82
    private final int indexOf(final Object o, final Function<Entry<?, ?>, Object> getter) {
-
 
83
        final int stop = this.entries.size();
-
 
84
        for (int i = 0; i < stop; i++) {
-
 
85
            final Entry<K, V> e = this.entries.get(i);
-
 
86
            if (Objects.equals(o, getter.apply(e)))
-
 
87
                return i;
64
        }
88
        }
65
        return null;
89
        return -1;
66
    }
90
    }
67
 
91
 
68
    @Override
92
    @Override
69
    public V put(K key, V value) {
93
    public V get(final Object key) {
70
        final int size = this.keys.size();
94
        final int i = indexOfKey(key);
71
        for (int i = 0; i < size; i++) {
-
 
72
            if (this.keys.get(i).equals(key)) {
-
 
73
                final V old = this.values.get(i);
-
 
74
                this.values.set(i, value);
-
 
75
                return old;
-
 
76
            }
95
        if (i < 0)
77
        }
-
 
78
        this.keys.add(key);
-
 
79
        this.values.add(value);
-
 
80
        return null;
96
            return null;
-
 
97
        return this.entries.get(i).getValue();
81
    }
98
    }
82
 
99
 
83
    @Override
100
    @Override
84
    public V remove(Object key) {
101
    public V put(final K key, final V value) {
85
        final int size = this.keys.size();
102
        final int i = this.indexOfKey(key);
-
 
103
        final V res;
86
        for (int i = 0; i < size; i++) {
104
        if (i < 0) {
87
            if (this.keys.get(i).equals(key)) {
105
            this.entries.add(new SimpleEntry<>(key, value));
88
                this.keys.remove(i);
106
            ++this.modCount;
-
 
107
            res = null;
-
 
108
        } else {
89
                return this.values.remove(i);
109
            res = this.entries.get(i).setValue(value);
90
            }
110
        }
-
 
111
        return res;
91
        }
112
    }
-
 
113
 
-
 
114
    @Override
-
 
115
    public V remove(final Object key) {
-
 
116
        return this.remove(indexOfKey(key));
-
 
117
    }
-
 
118
 
-
 
119
    private V remove(final int i) {
-
 
120
        if (i < 0)
92
        return null;
121
            return null;
-
 
122
        final Entry<K, V> res = this.entries.remove(i);
-
 
123
        ++this.modCount;
-
 
124
        return res.getValue();
93
    }
125
    }
94
 
126
 
95
    @Override
127
    @Override
96
    public void putAll(Map<? extends K, ? extends V> m) {
128
    public boolean remove(final Object key, final Object value) {
97
        final Set<? extends K> keySet = m.keySet();
129
        final int i = indexOfKey(key);
-
 
130
        if (i < 0)
-
 
131
            return false;
98
        for (Iterator<? extends K> iterator = keySet.iterator(); iterator.hasNext();) {
132
        final boolean eqVal = Objects.equals(this.entries.get(i).getValue(), value);
99
            K key = (K) iterator.next();
133
        if (eqVal) {
100
            put(key, m.get(key));
134
            this.remove(i);
101
        }
135
        }
-
 
136
        return eqVal;
102
    }
137
    }
103
 
138
 
104
    @Override
139
    @Override
105
    public void clear() {
140
    public void clear() {
106
        this.keys.clear();
141
        this.entries.clear();
107
        this.values.clear();
142
        ++this.modCount;
108
    }
143
    }
109
 
144
 
110
    // Views
145
    // Views
111
 
146
 
112
    @Override
147
    @Override
113
    public Set<K> keySet() {
148
    public Set<Map.Entry<K, V>> entrySet() {
114
        return new HashSet<K>(this.keys) {
149
        Set<Map.Entry<K, V>> es;
115
            @Override
-
 
116
            public boolean remove(Object o) {
-
 
117
                TinyMap.this.remove(o);
150
        return (es = this.entrySet) == null ? (this.entrySet = new EntrySet()) : es;
118
                return super.remove(o);
-
 
119
            }
151
    }
120
 
152
 
-
 
153
    final class EntrySet extends AbstractSet<Map.Entry<K, V>> {
121
            @Override
154
        @Override
122
            public void clear() {
155
        public final int size() {
123
                clear();
-
 
124
                super.clear();
156
            return TinyMap.this.size();
125
            }
157
        }
-
 
158
 
126
        };
159
        @Override
-
 
160
        public final void clear() {
-
 
161
            TinyMap.this.clear();
127
    }
162
        }
128
 
163
 
129
    @Override
164
        @Override
130
    public Collection<V> values() {
165
        public final Iterator<Map.Entry<K, V>> iterator() {
-
 
166
            return new Iterator<Map.Entry<K, V>>() {
-
 
167
 
-
 
168
                private int expectedModCount = TinyMap.this.modCount;
-
 
169
                /**
-
 
170
                 * The index last returned, i.e. initial value just before first item.
-
 
171
                 * 
-
 
172
                 * <pre>
-
 
173
                 *    a   b   c
-
 
174
                 * -1   0   1   2
-
 
175
                 * </pre>
-
 
176
                 */
-
 
177
                private int currentPos = -1;
-
 
178
                private Entry<K, V> lastReturned = null;
131
 
179
 
132
        return new ArrayList<V>(this.values()) {
-
 
133
            @Override
180
                @Override
134
            public V remove(int index) {
181
                public boolean hasNext() {
135
                keys.remove(index);
-
 
136
                values.remove(index);
182
                    final int nextIndex = this.currentPos + 1;
137
                return super.remove(index);
183
                    return nextIndex < size();
138
            }
184
                }
139
 
185
 
140
            @Override
186
                @Override
141
            public boolean remove(Object o) {
187
                public Entry<K, V> next() {
142
                int index = values.indexOf(o);
188
                    checkForComodification();
143
                if (index >= 0) {
189
                    if (!hasNext())
144
                    keys.remove(index);
190
                        throw new NoSuchElementException();
145
                    values.remove(index);
191
                    this.currentPos++;
-
 
192
                    final Entry<K, V> res = TinyMap.this.entries.get(this.currentPos);
146
                }
193
                    this.lastReturned = res;
147
                return super.remove(o);
194
                    return res;
148
            }
195
                }
149
 
196
 
150
            @Override
197
                @Override
151
            public void clear() {
198
                public void remove() {
152
                clear();
199
                    checkForComodification();
153
                super.clear();
200
                    if (this.lastReturned == null)
-
 
201
                        throw new IllegalStateException();
-
 
202
                    TinyMap.this.remove(this.currentPos);
154
            }
203
                    this.currentPos--;
-
 
204
                    // per doc : cannot be called twice
155
        };
205
                    this.lastReturned = null;
-
 
206
                    this.expectedModCount = TinyMap.this.modCount;
156
    }
207
                }
157
 
208
 
158
    @Override
-
 
159
    public Set<Entry<K, V>> entrySet() {
-
 
160
        final Set<Entry<K, V>> set = new HashSet<Map.Entry<K, V>>() {
-
 
161
            @Override
-
 
162
            public boolean remove(Object o) {
209
                final void checkForComodification() {
163
                Map.Entry<K, V> entry = (Map.Entry<K, V>) o;
-
 
164
                int index = values.indexOf(entry.getValue());
210
                    if (TinyMap.this.modCount != this.expectedModCount)
165
                if (index >= 0) {
-
 
166
                    keys.remove(index);
-
 
167
                    values.remove(index);
211
                        throw new ConcurrentModificationException();
168
                }
212
                }
169
                return super.remove(o);
213
            };
170
            }
214
        }
171
 
215
 
172
            @Override
216
        @Override
173
            public boolean removeAll(Collection<?> c) {
217
        public final boolean contains(final Object o) {
174
                for (Iterator iterator = c.iterator(); iterator.hasNext();) {
-
 
175
                    Entry<K, V> entry = (Entry<K, V>) iterator.next();
-
 
176
                    int index = values.indexOf(entry.getValue());
-
 
177
                    if (index >= 0) {
218
            if (!(o instanceof Map.Entry))
178
                        keys.remove(index);
219
                return false;
179
                        values.remove(index);
220
            final Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
180
                    }
-
 
181
                }
-
 
182
                return super.removeAll(c);
221
            return TinyMap.this.entries.contains(e);
183
            }
222
        }
184
 
223
 
185
            @Override
224
        @Override
-
 
225
        public final boolean remove(final Object o) {
186
            public void clear() {
226
            if (o instanceof Map.Entry) {
-
 
227
                final Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
187
                clear();
228
                final Object key = e.getKey();
188
                super.clear();
229
                final Object value = e.getValue();
-
 
230
                return TinyMap.this.remove(key, value);
189
            }
231
            }
190
 
-
 
191
        };
-
 
192
        final int size = this.keys.size();
-
 
193
        for (int i = 0; i < size; i++) {
232
            return false;
194
            set.add(new SimpleImmutableEntry<K, V>(this.keys.get(i), this.values.get(i)));
-
 
195
        }
233
        }
196
        return set;
-
 
197
    }
234
    }
198
 
-
 
199
}
235
}