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 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
83 ilm 1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
5
 *
6
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
7
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
8
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
9
 * language governing permissions and limitations under the License.
10
 *
11
 * When distributing the software, include this License Header Notice in each file.
12
 */
13
 
14
 package org.openconcerto.utils;
15
 
182 ilm 16
import java.util.AbstractMap;
17
import java.util.AbstractSet;
83 ilm 18
import java.util.ArrayList;
182 ilm 19
import java.util.ConcurrentModificationException;
83 ilm 20
import java.util.Iterator;
21
import java.util.Map;
182 ilm 22
import java.util.NoSuchElementException;
23
import java.util.Objects;
83 ilm 24
import java.util.Set;
182 ilm 25
import java.util.function.Function;
83 ilm 26
 
182 ilm 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;
83 ilm 30
 
182 ilm 31
    private final ArrayList<Entry<K, V>> entries;
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;
40
 
83 ilm 41
    public TinyMap() {
42
        this(10);
43
    }
44
 
182 ilm 45
    public TinyMap(final int initialCapacity) {
46
        this.entries = new ArrayList<>(initialCapacity);
83 ilm 47
    }
48
 
182 ilm 49
    public TinyMap(final Map<? extends K, ? extends V> m) {
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;
56
    }
57
 
83 ilm 58
    @Override
59
    public int size() {
182 ilm 60
        return this.entries.size();
83 ilm 61
    }
62
 
63
    @Override
64
    public boolean isEmpty() {
182 ilm 65
        return this.entries.isEmpty();
83 ilm 66
    }
67
 
68
    @Override
182 ilm 69
    public boolean containsKey(final Object key) {
70
        return indexOfKey(key) >= 0;
83 ilm 71
    }
72
 
73
    @Override
182 ilm 74
    public boolean containsValue(final Object value) {
75
        return this.indexOf(value, VALUE_GETTER) >= 0;
83 ilm 76
    }
77
 
182 ilm 78
    private int indexOfKey(final Object key) {
79
        return this.indexOf(key, KEY_GETTER);
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;
83 ilm 88
        }
182 ilm 89
        return -1;
83 ilm 90
    }
91
 
92
    @Override
182 ilm 93
    public V get(final Object key) {
94
        final int i = indexOfKey(key);
95
        if (i < 0)
96
            return null;
97
        return this.entries.get(i).getValue();
83 ilm 98
    }
99
 
100
    @Override
182 ilm 101
    public V put(final K key, final V value) {
102
        final int i = this.indexOfKey(key);
103
        final V res;
104
        if (i < 0) {
105
            this.entries.add(new SimpleEntry<>(key, value));
106
            ++this.modCount;
107
            res = null;
108
        } else {
109
            res = this.entries.get(i).setValue(value);
83 ilm 110
        }
182 ilm 111
        return res;
83 ilm 112
    }
113
 
114
    @Override
182 ilm 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)
121
            return null;
122
        final Entry<K, V> res = this.entries.remove(i);
123
        ++this.modCount;
124
        return res.getValue();
125
    }
126
 
127
    @Override
128
    public boolean remove(final Object key, final Object value) {
129
        final int i = indexOfKey(key);
130
        if (i < 0)
131
            return false;
132
        final boolean eqVal = Objects.equals(this.entries.get(i).getValue(), value);
133
        if (eqVal) {
134
            this.remove(i);
83 ilm 135
        }
182 ilm 136
        return eqVal;
83 ilm 137
    }
138
 
139
    @Override
140
    public void clear() {
182 ilm 141
        this.entries.clear();
142
        ++this.modCount;
83 ilm 143
    }
144
 
145
    // Views
146
 
147
    @Override
182 ilm 148
    public Set<Map.Entry<K, V>> entrySet() {
149
        Set<Map.Entry<K, V>> es;
150
        return (es = this.entrySet) == null ? (this.entrySet = new EntrySet()) : es;
83 ilm 151
    }
152
 
182 ilm 153
    final class EntrySet extends AbstractSet<Map.Entry<K, V>> {
154
        @Override
155
        public final int size() {
156
            return TinyMap.this.size();
157
        }
83 ilm 158
 
182 ilm 159
        @Override
160
        public final void clear() {
161
            TinyMap.this.clear();
162
        }
83 ilm 163
 
182 ilm 164
        @Override
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;
179
 
180
                @Override
181
                public boolean hasNext() {
182
                    final int nextIndex = this.currentPos + 1;
183
                    return nextIndex < size();
83 ilm 184
                }
185
 
182 ilm 186
                @Override
187
                public Entry<K, V> next() {
188
                    checkForComodification();
189
                    if (!hasNext())
190
                        throw new NoSuchElementException();
191
                    this.currentPos++;
192
                    final Entry<K, V> res = TinyMap.this.entries.get(this.currentPos);
193
                    this.lastReturned = res;
194
                    return res;
195
                }
83 ilm 196
 
182 ilm 197
                @Override
198
                public void remove() {
199
                    checkForComodification();
200
                    if (this.lastReturned == null)
201
                        throw new IllegalStateException();
202
                    TinyMap.this.remove(this.currentPos);
203
                    this.currentPos--;
204
                    // per doc : cannot be called twice
205
                    this.lastReturned = null;
206
                    this.expectedModCount = TinyMap.this.modCount;
83 ilm 207
                }
208
 
182 ilm 209
                final void checkForComodification() {
210
                    if (TinyMap.this.modCount != this.expectedModCount)
211
                        throw new ConcurrentModificationException();
83 ilm 212
                }
182 ilm 213
            };
214
        }
83 ilm 215
 
182 ilm 216
        @Override
217
        public final boolean contains(final Object o) {
218
            if (!(o instanceof Map.Entry))
219
                return false;
220
            final Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
221
            return TinyMap.this.entries.contains(e);
222
        }
223
 
224
        @Override
225
        public final boolean remove(final Object o) {
226
            if (o instanceof Map.Entry) {
227
                final Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
228
                final Object key = e.getKey();
229
                final Object value = e.getValue();
230
                return TinyMap.this.remove(key, value);
83 ilm 231
            }
182 ilm 232
            return false;
83 ilm 233
        }
234
    }
235
}