Author: bpoussin Date: 2013-02-23 15:00:57 +0100 (Sat, 23 Feb 2013) New Revision: 2503 Url: http://nuiton.org/projects/nuiton-utils/repository/revisions/2503 Log: Ajout d'un nouvelle implantation de Map qui permet de mettre des alias et de retourne une cle ou une valeur en passant en parametre les alias Added: trunk/nuiton-utils/src/main/java/org/nuiton/util/AliasMap.java trunk/nuiton-utils/src/test/java/org/nuiton/util/AliasMapTest.java Added: trunk/nuiton-utils/src/main/java/org/nuiton/util/AliasMap.java =================================================================== --- trunk/nuiton-utils/src/main/java/org/nuiton/util/AliasMap.java (rev 0) +++ trunk/nuiton-utils/src/main/java/org/nuiton/util/AliasMap.java 2013-02-23 14:00:57 UTC (rev 2503) @@ -0,0 +1,252 @@ +package org.nuiton.util; + + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.UUID; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections.map.MultiValueMap; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Une map base sur une HashMap qui permet pour une valeur d'ajouter d'autres + * cles. Cette nouvelle cle est un alias, un alias peut-etre utilise pour + * plusieurs valeurs. Si l'on demande la valeur associe a un Alias, cela + * retourne une liste contenant toutes les valeurs pour lequel cette Alias est + * utilise. + * + * Cela permet de stocker des objets avec une cle principale et unique, puis + * avec des alias. + * + * Et ainsi recherche les valeurs qui ont un ensemble d'alias via la methode + * {@link #getValueAlias(java.lang.Object[]) } ou supprimer les valeurs qui + * ont un ensemble d'Alias en commun via la methode {@link #removeValue} + * + * Si la cle ne vous importe que peu, vous pouvez par exemple utiliser + * {@link UUID#randomUUID()} pour generer une cle unique. + * + * @author poussin + * @version $Revision$ + * + * Last update: $Date$ + * by : $Author$ + */ +public class AliasMap<K, V, A> extends HashMap<K, V> { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private Log log = LogFactory.getLog(AliasMap.class); + private static final long serialVersionUID = 1L; + + /** key: alias, value: key */ + protected MultiValueMap aliases; + /** key: key, value: alias */ + protected MultiValueMap keys; + + public AliasMap() { + aliases = MultiValueMap.decorate(new HashMap<A, K>(), HashSet.class); + keys = MultiValueMap.decorate(new HashMap<K, A>(), HashSet.class); + } + + /** + * Ajoute une valeur dans la map avec un ensemble d'alias associe + * + * @param key identifiant unique pour cette valeur + * @param value la valeur + * @param alias les alias de la valeur + * @return + */ + public V put(K key, V value, A alias1, A ... alias) { + V result = put(key, value); + putAlias(key, alias1, alias); + + return result; + } + + /** + * Ajoute des alias a une cle + * @param key + * @param alias1 + * @param alias + */ + protected void putAlias(K key, A alias1, A ... alias) { + aliases.put(alias1, key); + keys.put(key, alias1); + for (Object a : alias) { + aliases.put(a, key); + keys.put(key, a); + } + } + + /** + * Retoure les cles en commun de tous les alias. Les cles retournees sont + * celle qui ont tous les alias. + * <pre> + * K1: a, b, c + * K2: b, c, d + * K3: c, d, e + * + * getKeyAlias(a, b, c) retourne [K1] + * getKeyAlias(b, c) retourne [K1, K2] + * getKeyAlias(c) retourne [K1, K2, K3] + * getKeyAlias(d) retourne [K2, K3] + * getKeyAlias(z) retourne [] + * </pre> + * + * + * @param alias + * @return une liste vide si aucune valeur ne correspond au alias en argument + */ + public Collection<K> getKeyAlias(A ... alias) { + Collection result = null; + for (Object a : alias) { + Collection tmp = aliases.getCollection(a); + if (tmp != null) { + if (result == null) { + result = new HashSet(tmp); + } else { + result.retainAll(tmp); + } + } + } + if (result == null) { + result = Collections.emptySet(); + } + return result; + } + + /** + * Retoure les valeurs en commun de tous les alias. Les valeurs retournees sont + * celle qui ont tous les alias. + * <pre> + * V1: a, b, c + * V2: b, c, d + * V3: c, d, e + * + * getKeyAlias(a, b, c) retourne [V1] + * getKeyAlias(b, c) retourne [V1, V2] + * getKeyAlias(c) retourne [V1, V2, V3] + * getKeyAlias(d) retourne [V2, V3] + * getKeyAlias(z) retourne [] + * </pre> + * + * + * @param alias + * @return une liste vide si aucune valeur ne correspond au alias en argument + */ + public Collection<V> getValueAlias(A ... alias) { + Collection keys = getKeyAlias(alias); + Collection result = new HashSet(keys.size()); + for (Object k : keys) { + result.add(get(k)); + } + return result; + } + + /** + * Retourne la liste d'alias associee avec une cle + * + * <pre> + * K1: a, b, c + * K2: b, c, d + * K3: c, d, e + * + * getAlias(K1) retourne [a, b, c] + * getAlias(k3) retourne [c, d, e] + * getKeyAlias(k9) retourne [] + * </pre> + * + * @param key + * @return + */ + public Collection<A> getAlias(K key) { + Collection result = keys.getCollection(key); + if (result == null) { + result = Collections.emptySet(); + } + return result; + } + + /** + * Retire une cle ainsi que tous ses alias + * + * <pre> + * K1: a, b, c + * K2: b, c, d + * K3: c, d, e + * + * remove(K1) il reste K2: [b, c, d], K3: [c, d, e] + * </pre> + * + * @param key + * @return + */ + @Override + public V remove(Object key) { + V result = super.remove(key); + Collection alias = getAlias((K)key); + keys.remove(key); + if (alias != null) { + for (Object a : alias) { + aliases.remove(a, key); + if (CollectionUtils.isEmpty(aliases.getCollection(a))) { + aliases.remove(a); + } + } + } + return result; + } + + /** + * Supprime toutes les valeurs et leur cle associe aux alias + * + * <pre> + * K1: a, b, c + * K2: b, c, d + * K3: c, d, e + * + * removeValue(b, c) il reste K3: [c, d, e] + * </pre> + * + * @param alias + * @return la liste de valeur qui a ete supprime de la map + */ + public Collection<V> removeValue(A ... alias) { + Collection keys = getKeyAlias(alias); + Collection result = new ArrayList(keys.size()); + for (Object k : keys) { + result.add(remove(k)); + } + return result; + } + + /** + * Supprime des alias quelque soit leur cle + * + * <pre> + * K1: a, b, c + * K2: b, c, d + * K3: c, d, e + * + * removeAlias(a, b) alors K1: [c], k2: [c, d], k3: [c, d, e] + * removeAlias(c) alors K1: [a, b], k2: [b, d], k3: [d, e] + * getKeyAlias(z) alors rien ne change car cette alias n'existe pas + * </pre> + * + * @param alias + */ + public void removeAlias(A ... alias) { + for (Object a : alias) { + Collection ks = aliases.getCollection(a); + aliases.remove(a); + if (ks != null) { + for (Object k : ks) { + keys.remove(k, a); + } + } + } + } +} Added: trunk/nuiton-utils/src/test/java/org/nuiton/util/AliasMapTest.java =================================================================== --- trunk/nuiton-utils/src/test/java/org/nuiton/util/AliasMapTest.java (rev 0) +++ trunk/nuiton-utils/src/test/java/org/nuiton/util/AliasMapTest.java 2013-02-23 14:00:57 UTC (rev 2503) @@ -0,0 +1,57 @@ +package org.nuiton.util; + + +import java.util.Arrays; +import java.util.Collection; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.Assert; +import org.junit.Test; + +/** + * + * @author poussin + * @version $Revision$ + * + * Last update: $Date$ + * by : $Author$ + */ +public class AliasMapTest { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private Log log = LogFactory.getLog(AliasMapTest.class); + + protected Object[] toArray(Collection c) { + Object[] result = c.toArray(); + Arrays.sort(result); + return result; + } + + @Test + public void testAlias() { + AliasMap<String, String, String> map = new AliasMap<String, String, String>(); + map.put("a", "A", "à", "â", "ä", "commun"); + map.put("b", "B"); + map.put("e", "E", "é", "è", "ê", "ë", "commun"); + map.put("ebis", "EBIS", "é", "è", "commun"); + + Assert.assertEquals("A", map.get("a")); + Assert.assertArrayEquals(new String[]{"commun", "à", "â", "ä"}, toArray(map.getAlias("a"))); + Assert.assertArrayEquals(new String[]{"a"}, toArray(map.getKeyAlias("à"))); + Assert.assertArrayEquals(new String[]{"e", "ebis"}, toArray(map.getKeyAlias("é"))); + Assert.assertArrayEquals(new String[]{"E", "EBIS"}, toArray(map.getValueAlias("é", "è"))); + Assert.assertArrayEquals(new String[]{}, toArray(map.getValueAlias("é", "è", "à"))); + + String v = map.remove("e"); + Assert.assertEquals("E", v); + Assert.assertArrayEquals(new String[]{}, toArray(map.getAlias("e"))); + Assert.assertArrayEquals(new String[]{"ebis"}, toArray(map.getKeyAlias("é"))); + + Collection<String> cr = map.removeValue("à", "é"); + Assert.assertArrayEquals(new String[]{}, toArray(cr)); + + cr = map.removeValue("commun"); + Assert.assertArrayEquals(new String[]{"A", "EBIS"}, toArray(cr)); + } + +}