1 package org.nuiton.util;
2
3 /*
4 * #%L
5 * Nuiton Utils
6 * %%
7 * Copyright (C) 2004 - 2013 CodeLutin
8 * %%
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU Lesser General Public License as
11 * published by the Free Software Foundation, either version 3 of the
12 * License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Lesser Public License for more details.
18 *
19 * You should have received a copy of the GNU General Lesser Public
20 * License along with this program. If not, see
21 * <http://www.gnu.org/licenses/lgpl-3.0.html>.
22 * #L%
23 */
24
25
26 import org.apache.commons.collections4.CollectionUtils;
27 import org.apache.commons.collections4.MultiValuedMap;
28 import org.apache.commons.collections4.multimap.HashSetValuedHashMap;
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31
32 import java.util.ArrayList;
33 import java.util.Collection;
34 import java.util.Collections;
35 import java.util.HashMap;
36 import java.util.HashSet;
37 import java.util.UUID;
38
39 /**
40 * Une map base sur une HashMap qui permet pour une valeur d'ajouter d'autres
41 * cles. Cette nouvelle cle est un alias, un alias peut-etre utilise pour
42 * plusieurs valeurs. Si l'on demande la valeur associe a un Alias, cela
43 * retourne une liste contenant toutes les valeurs pour lequel cette Alias est
44 * utilise.
45 *
46 * Cela permet de stocker des objets avec une cle principale et unique, puis
47 * avec des alias.
48 *
49 * Et ainsi recherche les valeurs qui ont un ensemble d'alias via la methode
50 * {@link #getValueAlias(Object[]) } ou supprimer les valeurs qui
51 * ont un ensemble d'Alias en commun via la methode {@link #removeValue}
52 *
53 * Si la cle ne vous importe que peu, vous pouvez par exemple utiliser
54 * {@link UUID#randomUUID()} pour generer une cle unique.
55 *
56 * @author Benjamin Poussin - poussin@codelutin.com
57 * @since 3.6.9
58 */
59 public class AliasMap<K, V, A> extends HashMap<K, V> {
60
61 /** Logger. */
62 private static final Log log = LogFactory.getLog(AliasMap.class);
63 private static final long serialVersionUID = 1L;
64
65 /** key: alias, value: key */
66 protected MultiValuedMap<A, K> aliases;
67 /** key: key, value: alias */
68 protected MultiValuedMap<K, A> keys;
69
70 public AliasMap() {
71 aliases = new HashSetValuedHashMap<>();
72 keys = new HashSetValuedHashMap<>();
73 }
74
75 /**
76 * Ajoute une valeur dans la map avec un ensemble d'alias associe
77 *
78 * @param key identifiant unique pour cette valeur
79 * @param value la valeur
80 * @param alias1 le premier alias à saisir
81 * @param alias les alias de la valeur
82 * @return FIXME
83 */
84 public V put(K key, V value, A alias1, A ... alias) {
85 V result = put(key, value);
86 putAlias(key, alias1, alias);
87
88 return result;
89 }
90
91 /**
92 * Ajoute des alias a une cle
93 * @param key FIXME
94 * @param alias1 FIXME
95 * @param alias FIXME
96 */
97 protected void putAlias(K key, A alias1, A ... alias) {
98 aliases.put(alias1, key);
99 keys.put(key, alias1);
100 for (A a : alias) {
101 aliases.put(a, key);
102 keys.put(key, a);
103 }
104 }
105
106 /**
107 * Retoure les cles en commun de tous les alias. Les cles retournees sont
108 * celle qui ont tous les alias.
109 * <pre>
110 * K1: a, b, c
111 * K2: b, c, d
112 * K3: c, d, e
113 *
114 * getKeyAlias(a, b, c) retourne [K1]
115 * getKeyAlias(b, c) retourne [K1, K2]
116 * getKeyAlias(c) retourne [K1, K2, K3]
117 * getKeyAlias(d) retourne [K2, K3]
118 * getKeyAlias(z) retourne []
119 * </pre>
120 *
121 *
122 * @param alias FIXME
123 * @return une liste vide si aucune valeur ne correspond au alias en argument
124 */
125 public Collection<K> getKeyAlias(A ... alias) {
126 Collection result = null;
127 for (A a : alias) {
128 Collection tmp = aliases.get(a);
129 if (tmp != null) {
130 if (result == null) {
131 result = new HashSet(tmp);
132 } else {
133 result.retainAll(tmp);
134 }
135 }
136 }
137 if (result == null) {
138 result = Collections.emptySet();
139 }
140 return result;
141 }
142
143 /**
144 * Retoure les valeurs en commun de tous les alias. Les valeurs retournees sont
145 * celle qui ont tous les alias.
146 * <pre>
147 * V1: a, b, c
148 * V2: b, c, d
149 * V3: c, d, e
150 *
151 * getKeyAlias(a, b, c) retourne [V1]
152 * getKeyAlias(b, c) retourne [V1, V2]
153 * getKeyAlias(c) retourne [V1, V2, V3]
154 * getKeyAlias(d) retourne [V2, V3]
155 * getKeyAlias(z) retourne []
156 * </pre>
157 *
158 *
159 * @param alias FIXME
160 * @return une liste vide si aucune valeur ne correspond au alias en argument
161 */
162 public Collection<V> getValueAlias(A ... alias) {
163 Collection keys = getKeyAlias(alias);
164 Collection result = new HashSet(keys.size());
165 for (Object k : keys) {
166 result.add(get(k));
167 }
168 return result;
169 }
170
171 /**
172 * Retourne la liste d'alias associee avec une cle
173 *
174 * <pre>
175 * K1: a, b, c
176 * K2: b, c, d
177 * K3: c, d, e
178 *
179 * getAlias(K1) retourne [a, b, c]
180 * getAlias(k3) retourne [c, d, e]
181 * getKeyAlias(k9) retourne []
182 * </pre>
183 *
184 * @param key FIXME
185 * @return FIXME
186 */
187 public Collection<A> getAlias(K key) {
188 Collection<A> result = keys.get(key);
189 if (result == null) {
190 result = Collections.emptySet();
191 }
192 return result;
193 }
194
195 /**
196 * Retire une cle ainsi que tous ses alias
197 *
198 * <pre>
199 * K1: a, b, c
200 * K2: b, c, d
201 * K3: c, d, e
202 *
203 * remove(K1) il reste K2: [b, c, d], K3: [c, d, e]
204 * </pre>
205 *
206 * @param key FIXME
207 * @return FIXME
208 */
209 @Override
210 public V remove(Object key) {
211 V result = super.remove(key);
212 Collection<A> alias = getAlias((K)key);
213 if (alias != null) {
214 for (A a : alias) {
215 aliases.removeMapping(a, key);
216 if (CollectionUtils.isEmpty(aliases.get(a))) {
217 aliases.remove(a);
218 }
219 }
220 }
221 keys.remove(key);
222 return result;
223 }
224
225 /**
226 * Supprime toutes les valeurs et leur cle associe aux alias
227 *
228 * <pre>
229 * K1: a, b, c
230 * K2: b, c, d
231 * K3: c, d, e
232 *
233 * removeValue(b, c) il reste K3: [c, d, e]
234 * </pre>
235 *
236 * @param alias FIXME
237 * @return la liste de valeur qui a ete supprime de la map
238 */
239 public Collection<V> removeValue(A ... alias) {
240 Collection keys = getKeyAlias(alias);
241 Collection result = new ArrayList(keys.size());
242 for (Object k : keys) {
243 result.add(remove(k));
244 }
245 return result;
246 }
247
248 /**
249 * Supprime des alias quelque soit leur cle
250 *
251 * <pre>
252 * K1: a, b, c
253 * K2: b, c, d
254 * K3: c, d, e
255 *
256 * removeAlias(a, b) alors K1: [c], k2: [c, d], k3: [c, d, e]
257 * removeAlias(c) alors K1: [a, b], k2: [b, d], k3: [d, e]
258 * getKeyAlias(z) alors rien ne change car cette alias n'existe pas
259 * </pre>
260 *
261 * @param alias FIXME
262 */
263 public void removeAlias(A ... alias) {
264 for (A a : alias) {
265 Collection ks = aliases.get(a);
266 aliases.remove(a);
267 if (ks != null) {
268 for (Object k : ks) {
269 keys.removeMapping(k, a);
270 }
271 }
272 }
273 }
274 }