1 /* 2 * #%L 3 * Nuiton Utils 4 * %% 5 * Copyright (C) 2004 - 2010 CodeLutin 6 * %% 7 * This program is free software: you can redistribute it and/or modify 8 * it under the terms of the GNU Lesser General Public License as 9 * published by the Free Software Foundation, either version 3 of the 10 * License, or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Lesser Public License for more details. 16 * 17 * You should have received a copy of the GNU General Lesser Public 18 * License along with this program. If not, see 19 * <http://www.gnu.org/licenses/lgpl-3.0.html>. 20 * #L% 21 */ 22 23 24 /* * 25 * CategorisedListenerSet.java 26 * 27 * Created: 13 mai 2004 28 * 29 * @author Benjamin Poussin - poussin@codelutin.com 30 * Copyright Code Lutin 31 * 32 * 33 * Mise a jour: $Date$ 34 * par : */ 35 36 package org.nuiton.util; 37 38 import org.apache.commons.logging.Log; 39 import org.apache.commons.logging.LogFactory; 40 41 import java.beans.Statement; 42 import java.util.Iterator; 43 import java.util.WeakHashMap; 44 45 /** 46 * Objet permettant de géré plusieurs liste de listener de facon simple. 47 * Chaque liste de listener est rangé en fonction d'une cle (categorie) 48 * Une categorie peut avoir un pere, dans ce cas si un event doit etre lancé 49 * sur une categorie il est aussi lancer sur le pere de la categorie. 50 * Mais attention l'inverse n'est pas vrai, un event lancé sur un père n'est 51 * jamais lancé sur ses fils. 52 * Il existe une Category spéciale {@link #ALL} qui permet d'envoyer un event 53 * a tous les listeners. 54 * Si cette classe est la derniere classe a conserver l'objet categorie 55 * alors la categorie est libere et ainsi que les listeners si c'etait aussi 56 * leur derniers referencements 57 * 58 * <p> Si les categories sont representees par des Class, alors vous pouvez 59 * utiliser la hierachie de classe pour creer de facon automatique les peres. 60 * 61 * @param <L> listener type 62 * @see ListenerSet 63 */ 64 public class CategorisedListenerSet<L> { // CategorisedListenerSet 65 66 /** Logger. */ 67 private static final Log log = LogFactory.getLog(CategorisedListenerSet.class); 68 69 /** 70 * permet de remplacer toutes les categories. 71 * Si on utilise cette category, alors tous les listeners present 72 * seront utilisé. 73 */ 74 public static final Object ALL = new Object(); 75 76 /** 77 * HashMap de ListenerSet, en cle l'objet qui caracterise la categorie 78 * en valeur un ListenerSet 79 */ 80 protected WeakHashMap<Object, ListenerSet<L>> listeners = new WeakHashMap<Object, ListenerSet<L>>(); 81 82 protected WeakHashMap<Object, Object> categoryParent = new WeakHashMap<Object, Object>(); 83 84 protected boolean isClassCategory = true; 85 86 /** Empty constructor. */ 87 public CategorisedListenerSet() { 88 89 } 90 91 /** 92 * @param isClassCategory si vrai et que les categorie passé en arguement 93 * lors de l'ajout sont de type Class alors lors du fire on recherche aussi 94 * les peres dans la hierarchie d'heritage de la classe (Super class et 95 * interfaces) 96 */ 97 public CategorisedListenerSet(boolean isClassCategory) { 98 this(); 99 this.isClassCategory = isClassCategory; 100 } 101 102 protected void checkCategory(Object category) { 103 if (ALL.equals(category)) { 104 throw new IllegalArgumentException( 105 "ALL category can't be use to add listener or add Category"); 106 } 107 } 108 109 /** 110 * Ajoute une categorie en indiquant sont pere. Une categorie ne peut 111 * avoir qu'un seul pere, si la nouvelle categorie existait deja 112 * alors l'appel a cette methode change son pere. 113 * 114 * @param parent le pere de la categorie, null si on ne souhaite pas de pere 115 * @param newCategory la nouvelle caterogie 116 */ 117 public void addCategory(Object parent, Object newCategory) { 118 checkCategory(parent); 119 checkCategory(newCategory); 120 categoryParent.put(newCategory, parent); 121 } 122 123 /** 124 * Ajoute un listener sur une certaine categorie, si la categorie n'existe 125 * alors on la crée en ne lui affectant pas de père 126 * 127 * @param category la categorie dans lequel il faut ajouter le listener 128 * @param l le listener a ajouter 129 */ 130 public void add(Object category, L l) { 131 checkCategory(category); 132 ListenerSet<L> listeners = getListeners(category); 133 listeners.add(l); 134 } 135 136 /** 137 * Supprime un listener d'une categorie, si la categorie ou le listener 138 * n'existe pas, rien ne se passe. 139 * 140 * @param category la categorie dans lequel il faut supprimer le listener 141 * @param l le listener a supprimer 142 */ 143 public void remove(Object category, L l) { 144 ListenerSet<L> listeners = getListeners(category); 145 listeners.remove(l); 146 } 147 148 /** 149 * Permet de lancer un event dans une categorie, l'event est aussi propagé 150 * sur les ancètres de la categorie 151 * 152 * @param category la categorie a partir duquel il faut lancer l'evenement 153 * @param methodName le nom de la méthode de la classe listener 154 * @param event l'objet event a passer en paramètre de la methode du 155 * listener 156 * @throws Exception if event can't be fired 157 */ 158 public void fire(Object category, String methodName, Object event) 159 throws Exception { 160 if (log.isTraceEnabled()) { 161 log.trace("fire category: " + category + " method: " + methodName); 162 } 163 ListenerSet<L> ls = getAllListeners(category); 164 ls.fire(methodName, event); 165 } 166 167 /** 168 * Permet de lancer un event dans une categorie, l'event est aussi propagé 169 * sur les ancètres de la categorie, si un meme objet etait listener 170 * dans plusieurs categories alors il ne recevra qu'une seul notification 171 * 172 * @param category la categorie a partir duquel il faut lancer l'evenement 173 * @param methodName le nom de la méthode de la classe listener 174 * @throws Exception if event can't be fired 175 */ 176 public void fire(Object category, String methodName) throws Exception { 177 for (L l : getAllListeners(category)) { 178 Statement stm = new Statement(l, methodName, null); 179 stm.execute(); 180 } 181 } 182 183 /** 184 * Retourne un Iterator sur tous les listeners qu'il faut prevenir si on 185 * souhaite prevenir une certaine categorie. Ceci inclue les ancetre de la 186 * categorie 187 * 188 * @param category category to get iterator on 189 * @return iterator 190 */ 191 public Iterator<L> iterator(Object category) { 192 return getAllListeners(category).iterator(); 193 } 194 195 /** 196 * @param category categorie demandee 197 * @return un ListenerSet contenant tous les listeners c'est à dire les 198 * listener de la categorie demandé mais aussi les listeners des ancetres 199 */ 200 protected ListenerSet<L> getAllListeners(Object category) { 201 ListenerSet<L> result = new ListenerSet<L>(); 202 if (ALL.equals(category)) { 203 for (ListenerSet<L> ls : listeners.values()) { 204 result.addAll(ls); 205 } 206 } else { 207 Object parentCategory = category; 208 while (parentCategory != null) { 209 result.addAll(getListeners(parentCategory)); 210 if (isClassCategory && parentCategory instanceof Class) { 211 result.addAll(getListenersClass((Class<?>) parentCategory)); 212 } 213 parentCategory = categoryParent.get(parentCategory); 214 } 215 } 216 if (log.isTraceEnabled()) { 217 log.trace("getAllListeners category: " + category + " result: " 218 + result); 219 } 220 return result; 221 } 222 223 protected ListenerSet<L> getListenersClass(Class<?> category) { 224 ListenerSet<L> result = new ListenerSet<L>(); 225 Class<?> superClass = category.getSuperclass(); 226 if (superClass != null) { 227 result.addAll(getAllListeners(superClass)); 228 } 229 for (Class<?> c : category.getInterfaces()) { 230 result.addAll(getAllListeners(c)); 231 } 232 return result; 233 } 234 235 /** 236 * @param category categorie demandee 237 * @return un ListenerSet contenant seulement les listener de la categorie 238 * demandé. Si la categorie n'existe pas alors elle est créé. 239 */ 240 protected ListenerSet<L> getListeners(Object category) { 241 ListenerSet<L> result = listeners.get(category); 242 if (result == null) { 243 listeners.put(category, result = new ListenerSet<L>()); 244 } 245 if (log != null && log.isTraceEnabled()) { 246 log.trace("getListeners category: " + category + " result: " 247 + result); 248 } 249 return result; 250 } 251 252 public String toString() { 253 return "Listeners Category: " + categoryParent + "\nListener: " 254 + listeners; 255 } 256 257 } // CategorisedListenerSet 258