OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 174 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
174 ilm 1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
182 ilm 4
 * Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved.
174 ilm 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.cc;
15
 
16
import java.util.Arrays;
17
import java.util.Collection;
18
import java.util.HashMap;
19
import java.util.List;
20
import java.util.Map;
21
import java.util.Map.Entry;
22
import java.util.Objects;
23
import java.util.Set;
182 ilm 24
import java.util.function.Function;
174 ilm 25
 
26
import com.google.gson.reflect.TypeToken;
27
 
28
import net.jcip.annotations.GuardedBy;
29
import net.jcip.annotations.ThreadSafe;
30
 
31
/**
32
 * Type-safer map (e.g. can store multiple types and collections).
33
 *
34
 * @author Sylvain
35
 */
36
@ThreadSafe
37
public class Cookies {
38
 
39
    @GuardedBy("this")
40
    private final Map<Object, Object> map;
41
    @GuardedBy("this")
42
    private Map<Object, Object> collectionTypes;
43
 
44
    public Cookies() {
45
        this(8);
46
    }
47
 
48
    public Cookies(final int initialCapacity) {
49
        this.map = new HashMap<>(initialCapacity);
50
        this.collectionTypes = null;
51
    }
52
 
53
    public Cookies(final Cookies source) {
54
        synchronized (source) {
55
            this.map = new HashMap<>(source.map);
56
            this.collectionTypes = source.collectionTypes == null ? null : new HashMap<>(source.collectionTypes);
57
        }
58
    }
59
 
60
    public final void putAll(final Cookies other) {
61
        if (this == other)
62
            return;
63
        final Map<Object, Object> map;
64
        final Map<Object, Object> collectionTypes;
65
        synchronized (other) {
66
            map = new HashMap<>(other.map);
67
            collectionTypes = other.collectionTypes == null ? null : new HashMap<>(other.collectionTypes);
68
        }
69
        synchronized (this) {
70
            // don't just use putAll() since it won't remove entries from collectionTypes for
71
            // replaced items
72
            for (final Entry<Object, Object> e : map.entrySet()) {
73
                this.put(e.getKey(), e.getValue(), collectionTypes == null ? null : collectionTypes.get(e.getKey()));
74
            }
75
        }
76
    }
77
 
78
    public final Object put(Object k, Object v) {
79
        return this.put(k, v, null);
80
    }
81
 
82
    private final Object putCollectionType(Object k, Object v, Object type) {
83
        if (type == null)
84
            throw new IllegalArgumentException("Null type");
85
        return this.put(k, v, type);
86
    }
87
 
88
    private final synchronized Object put(Object k, Object v, Object type) {
89
        if (type != null) {
90
            if (this.collectionTypes == null)
91
                this.collectionTypes = new HashMap<>(4);
92
            this.collectionTypes.put(k, type);
93
        } else if (this.collectionTypes != null) {
94
            this.collectionTypes.remove(k);
95
        }
96
        return this.map.put(k, v);
97
    }
98
 
99
    public final <T> Object putCollection(Object k, Collection<T> v, Class<T> clazz) {
100
        return this.putCollectionType(k, v, clazz);
101
    }
102
 
103
    public final <K, V> Object putMap(Object k, Map<K, V> v, Class<K> keyClass, Class<V> valueClass) {
104
        Objects.requireNonNull(keyClass, "Key class");
105
        Objects.requireNonNull(valueClass, "value class");
106
        return this.putCollectionType(k, v, Arrays.asList(keyClass, valueClass));
107
    }
108
 
109
    public final <T> Object putGeneric(Object k, T v, TypeToken<T> clazz) {
110
        return this.putCollectionType(k, v, clazz);
111
    }
112
 
113
    public final synchronized boolean containsKey(Object k) {
114
        return this.map.containsKey(k);
115
    }
116
 
117
    public final synchronized Object getObject(Object k) {
118
        return this.map.get(k);
119
    }
120
 
121
    public final int getInt(Object k) {
122
        return getObjectAs(k, Number.class).intValue();
123
    }
124
 
125
    public final long getLong(Object k) {
126
        return getObjectAs(k, Number.class).longValue();
127
    }
128
 
129
    public final Boolean getBoolean(Object k) {
130
        return getObjectAs(k, Boolean.class);
131
    }
132
 
133
    public final boolean getBoolean(Object k, final boolean def) {
134
        final Boolean res = getBoolean(k);
135
        return res == null ? def : res.booleanValue();
136
    }
137
 
138
    public final String getString(Object k) {
139
        return getObjectAs(k, String.class);
140
    }
141
 
142
    public synchronized final <T> T getContainedObjectAs(Object key, Class<T> clazz) {
143
        if (!this.containsKey(key))
144
            throw new IllegalArgumentException(key + " not present in this : " + this.map.keySet());
145
        return this.getObjectAs(key, clazz);
146
    }
147
 
148
    public final <T> T getObjectAs(Object k, Class<T> clazz) {
149
        return this.getObjectAs(k, clazz, null);
150
    }
151
 
152
    public final <T> T getObjectAs(Object k, Class<T> clazz, final T defaultVal) {
153
        return this.getObjectAs(k, clazz, defaultVal, true);
154
    }
155
 
156
    public synchronized final <T> T getObjectAs(Object k, Class<T> clazz, final T defaultVal, final boolean useDefaultIfNullValue) {
157
        final Object res = this.getObject(k);
158
        if (res == null && (useDefaultIfNullValue || !this.containsKey(k)))
159
            return defaultVal;
160
        try {
161
            return clazz.cast(res);
162
        } catch (ClassCastException e) {
163
            throw new IllegalArgumentException("Couldn't access " + k + " in " + this + " as " + clazz.getSimpleName(), e);
164
        }
165
    }
166
 
167
    public final <T> Object checkType(Object k, Object wantedType, final String needMethodName) {
168
        final Object object;
169
        final Object type;
170
        synchronized (this) {
171
            object = this.getObject(k);
172
            type = this.collectionTypes == null ? null : this.collectionTypes.get(k);
173
        }
174
        // as getObject() : don't fail on null
175
        if (object == null)
176
            return null;
177
        if (type == null)
178
            throw new IllegalStateException("Wasn't stored with " + needMethodName + " : " + k);
179
        if (!type.equals(wantedType))
180
            throw new IllegalArgumentException("Wasn't stored with the passed type : " + wantedType + " != " + type);
181
        return object;
182
    }
183
 
184
    public final <T> Collection<T> getCollection(Object k, Class<T> clazz) {
185
        final Object object = checkType(k, clazz, "putCollection()");
186
        @SuppressWarnings("unchecked")
187
        final Collection<T> res = (Collection<T>) object;
188
        return res;
189
    }
182 ilm 190
 
191
    public final <T> Collection<T> computeCollectionIfAbsent(Object k, Class<T> clazz, Function<Object, ? extends Collection<T>> mappingFunction) {
192
        Collection<T> res;
193
        synchronized (this) {
194
            res = this.getCollection(k, clazz);
195
            // same algorithm as java.util.Map
196
            if (res == null) {
197
                res = mappingFunction.apply(k);
198
                if (res != null)
199
                    this.putCollection(k, res, clazz);
200
            }
201
        }
202
        return res;
203
    }
174 ilm 204
 
182 ilm 205
 
174 ilm 206
    public final <T> List<T> getList(Object k, Class<T> clazz) {
207
        return (List<T>) this.getCollection(k, clazz);
208
    }
209
 
210
    public final <T> Set<T> getSet(Object k, Class<T> clazz) {
211
        return (Set<T>) this.getCollection(k, clazz);
212
    }
213
 
214
    public final <K, V> Map<K, V> getMap(Object k, Class<K> keyClass, Class<V> valueClass) {
215
        final Object object = checkType(k, Arrays.asList(keyClass, valueClass), "putMap()");
216
        @SuppressWarnings("unchecked")
217
        final Map<K, V> res = (Map<K, V>) object;
218
        return res;
219
    }
220
 
182 ilm 221
    public final <K, V> Map<K, V> computeMapIfAbsent(Object k, Class<K> keyClass, Class<V> valueClass, Function<Object, ? extends Map<K, V>> mappingFunction) {
222
        Map<K, V> res;
223
        synchronized (this) {
224
            res = this.getMap(k, keyClass, valueClass);
225
            // same algorithm as java.util.Map
226
            if (res == null) {
227
                res = mappingFunction.apply(k);
228
                if (res != null)
229
                    this.putMap(k, res, keyClass, valueClass);
230
            }
231
        }
232
        return res;
233
    }
234
 
174 ilm 235
    public final <T> T getGeneric(Object k, TypeToken<T> clazz) {
236
        final Object object = checkType(k, clazz, "putGeneric()");
237
        @SuppressWarnings("unchecked")
238
        final T res = (T) object;
239
        return res;
240
    }
241
 
182 ilm 242
    public final <T> T computeIfAbsent(Object k, TypeToken<T> clazz, Function<Object, ? extends T> mappingFunction) {
243
        T res;
244
        synchronized (this) {
245
            res = this.getGeneric(k, clazz);
246
            // same algorithm as java.util.Map
247
            if (res == null) {
248
                res = mappingFunction.apply(k);
249
                if (res != null)
250
                    this.putGeneric(k, res, clazz);
251
            }
252
        }
253
        return res;
254
    }
255
 
174 ilm 256
    @Override
257
    public synchronized String toString() {
258
        return this.getClass().getSimpleName() + " of " + this.map.keySet();
259
    }
260
}