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 }