OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Details | 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
 *
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.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;
24
 
25
import com.google.gson.reflect.TypeToken;
26
 
27
import net.jcip.annotations.GuardedBy;
28
import net.jcip.annotations.ThreadSafe;
29
 
30
/**
31
 * Type-safer map (e.g. can store multiple types and collections).
32
 *
33
 * @author Sylvain
34
 */
35
@ThreadSafe
36
public class Cookies {
37
 
38
    @GuardedBy("this")
39
    private final Map<Object, Object> map;
40
    @GuardedBy("this")
41
    private Map<Object, Object> collectionTypes;
42
 
43
    public Cookies() {
44
        this(8);
45
    }
46
 
47
    public Cookies(final int initialCapacity) {
48
        this.map = new HashMap<>(initialCapacity);
49
        this.collectionTypes = null;
50
    }
51
 
52
    public Cookies(final Cookies source) {
53
        synchronized (source) {
54
            this.map = new HashMap<>(source.map);
55
            this.collectionTypes = source.collectionTypes == null ? null : new HashMap<>(source.collectionTypes);
56
        }
57
    }
58
 
59
    public final void putAll(final Cookies other) {
60
        if (this == other)
61
            return;
62
        final Map<Object, Object> map;
63
        final Map<Object, Object> collectionTypes;
64
        synchronized (other) {
65
            map = new HashMap<>(other.map);
66
            collectionTypes = other.collectionTypes == null ? null : new HashMap<>(other.collectionTypes);
67
        }
68
        synchronized (this) {
69
            // don't just use putAll() since it won't remove entries from collectionTypes for
70
            // replaced items
71
            for (final Entry<Object, Object> e : map.entrySet()) {
72
                this.put(e.getKey(), e.getValue(), collectionTypes == null ? null : collectionTypes.get(e.getKey()));
73
            }
74
        }
75
    }
76
 
77
    public final Object put(Object k, Object v) {
78
        return this.put(k, v, null);
79
    }
80
 
81
    private final Object putCollectionType(Object k, Object v, Object type) {
82
        if (type == null)
83
            throw new IllegalArgumentException("Null type");
84
        return this.put(k, v, type);
85
    }
86
 
87
    private final synchronized Object put(Object k, Object v, Object type) {
88
        if (type != null) {
89
            if (this.collectionTypes == null)
90
                this.collectionTypes = new HashMap<>(4);
91
            this.collectionTypes.put(k, type);
92
        } else if (this.collectionTypes != null) {
93
            this.collectionTypes.remove(k);
94
        }
95
        return this.map.put(k, v);
96
    }
97
 
98
    public final <T> Object putCollection(Object k, Collection<T> v, Class<T> clazz) {
99
        return this.putCollectionType(k, v, clazz);
100
    }
101
 
102
    public final <K, V> Object putMap(Object k, Map<K, V> v, Class<K> keyClass, Class<V> valueClass) {
103
        Objects.requireNonNull(keyClass, "Key class");
104
        Objects.requireNonNull(valueClass, "value class");
105
        return this.putCollectionType(k, v, Arrays.asList(keyClass, valueClass));
106
    }
107
 
108
    public final <T> Object putGeneric(Object k, T v, TypeToken<T> clazz) {
109
        return this.putCollectionType(k, v, clazz);
110
    }
111
 
112
    public final synchronized boolean containsKey(Object k) {
113
        return this.map.containsKey(k);
114
    }
115
 
116
    public final synchronized Object getObject(Object k) {
117
        return this.map.get(k);
118
    }
119
 
120
    public final int getInt(Object k) {
121
        return getObjectAs(k, Number.class).intValue();
122
    }
123
 
124
    public final long getLong(Object k) {
125
        return getObjectAs(k, Number.class).longValue();
126
    }
127
 
128
    public final Boolean getBoolean(Object k) {
129
        return getObjectAs(k, Boolean.class);
130
    }
131
 
132
    public final boolean getBoolean(Object k, final boolean def) {
133
        final Boolean res = getBoolean(k);
134
        return res == null ? def : res.booleanValue();
135
    }
136
 
137
    public final String getString(Object k) {
138
        return getObjectAs(k, String.class);
139
    }
140
 
141
    public synchronized final <T> T getContainedObjectAs(Object key, Class<T> clazz) {
142
        if (!this.containsKey(key))
143
            throw new IllegalArgumentException(key + " not present in this : " + this.map.keySet());
144
        return this.getObjectAs(key, clazz);
145
    }
146
 
147
    public final <T> T getObjectAs(Object k, Class<T> clazz) {
148
        return this.getObjectAs(k, clazz, null);
149
    }
150
 
151
    public final <T> T getObjectAs(Object k, Class<T> clazz, final T defaultVal) {
152
        return this.getObjectAs(k, clazz, defaultVal, true);
153
    }
154
 
155
    public synchronized final <T> T getObjectAs(Object k, Class<T> clazz, final T defaultVal, final boolean useDefaultIfNullValue) {
156
        final Object res = this.getObject(k);
157
        if (res == null && (useDefaultIfNullValue || !this.containsKey(k)))
158
            return defaultVal;
159
        try {
160
            return clazz.cast(res);
161
        } catch (ClassCastException e) {
162
            throw new IllegalArgumentException("Couldn't access " + k + " in " + this + " as " + clazz.getSimpleName(), e);
163
        }
164
    }
165
 
166
    public final <T> Object checkType(Object k, Object wantedType, final String needMethodName) {
167
        final Object object;
168
        final Object type;
169
        synchronized (this) {
170
            object = this.getObject(k);
171
            type = this.collectionTypes == null ? null : this.collectionTypes.get(k);
172
        }
173
        // as getObject() : don't fail on null
174
        if (object == null)
175
            return null;
176
        if (type == null)
177
            throw new IllegalStateException("Wasn't stored with " + needMethodName + " : " + k);
178
        if (!type.equals(wantedType))
179
            throw new IllegalArgumentException("Wasn't stored with the passed type : " + wantedType + " != " + type);
180
        return object;
181
    }
182
 
183
    public final <T> Collection<T> getCollection(Object k, Class<T> clazz) {
184
        final Object object = checkType(k, clazz, "putCollection()");
185
        @SuppressWarnings("unchecked")
186
        final Collection<T> res = (Collection<T>) object;
187
        return res;
188
    }
189
 
190
    public final <T> List<T> getList(Object k, Class<T> clazz) {
191
        return (List<T>) this.getCollection(k, clazz);
192
    }
193
 
194
    public final <T> Set<T> getSet(Object k, Class<T> clazz) {
195
        return (Set<T>) this.getCollection(k, clazz);
196
    }
197
 
198
    public final <K, V> Map<K, V> getMap(Object k, Class<K> keyClass, Class<V> valueClass) {
199
        final Object object = checkType(k, Arrays.asList(keyClass, valueClass), "putMap()");
200
        @SuppressWarnings("unchecked")
201
        final Map<K, V> res = (Map<K, V>) object;
202
        return res;
203
    }
204
 
205
    public final <T> T getGeneric(Object k, TypeToken<T> clazz) {
206
        final Object object = checkType(k, clazz, "putGeneric()");
207
        @SuppressWarnings("unchecked")
208
        final T res = (T) object;
209
        return res;
210
    }
211
 
212
    @Override
213
    public synchronized String toString() {
214
        return this.getClass().getSimpleName() + " of " + this.map.keySet();
215
    }
216
}