View Javadoc
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 }