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 * ListenerSet.java
25 *
26 * Created: 10 mai 2004
27 *
28 * @author Benjamin Poussin - poussin@codelutin.com
29 * Copyright Code Lutin
30 *
31 *
32 * Mise a jour: $Date$
33 * par : */
34 package org.nuiton.util;
35
36 import java.beans.Statement;
37 import java.lang.ref.Reference;
38 import java.util.HashSet;
39 import java.util.Iterator;
40
41 /**
42 * <p>Cette classe permet de mettre en place facilement le support de listeners.
43 * Elle ne permet d'ajouter qu'une seul fois le meme listener. Si elle est
44 * la derniere à avoir une référence sur le listener, le listener est supprimé
45 * de la liste des listeners.</p>
46 * <p>Si on souhaite avoir une vérification sur le type de listener ajouté
47 * il faut utiliser le constructeur qui prend une classe en paramètre. Dans ce
48 * cas la méthode {@link #add(Object)} vérifie que l'object passé est bien
49 * du type ou un enfant du type donné en paramètre du constructeur
50 * <p>Il y a deux façon de prévenir les listeners d'un event soit par le
51 * mécanisme inclu dans cette classe en utilisant la méthode {@link #fire} soit
52 * en utilisant soit même l'Iterateur sur les listeners encore valide.</p>
53 * <pre>
54 * ListenerSet listeners = new ListenerSet();
55 * ...
56 * listeners.fire("monEvent", MonObjetEvent);
57 * </pre>
58 * ou bien
59 * <pre>
60 * ListenerSet listeners = new ListenerSet();
61 * ...
62 * for(Iterator i=listeners.iterator(); i.hasNext();){
63 * MonListener l = (MonListener)i.next();
64 * l.monEvent(MonObjetEvent);
65 * }
66 * </pre>
67 * Cette deuxième façon de faire est plus sûr car elle n'utilise pas
68 * l'introspection et donc une vérification est faite sur le nom de la méthode
69 * à appeler à la compilation, mais elle est plus verbeuse à écrire.
70 *
71 * @param <L> listeners type
72 * @see CategorisedListenerSet
73 */
74 public class ListenerSet<L> implements Iterable<L> { // ListenerSet
75
76 /** Listeners reference set. */
77 protected HashSet<Reference<L>> listeners = new HashSet<Reference<L>>();
78
79 public int size() {
80 return listeners.size();
81 }
82
83 /**
84 * Ajoute un listener dans la liste des listeners.
85 *
86 * @param l le listener à ajouter. Si l'objet passé est null, rien n'est fait
87 * si l'objet n'est pas du type passé en argument du constructeur
88 * une IllegalArgumentException est levée.
89 */
90 public void add(L l) {
91 if (l == null) {
92 return;
93 }
94
95 TransparenteWeakReference<L> ref = new TransparenteWeakReference<L>(l);
96 listeners.add(ref);
97 }
98
99 /**
100 * ajoute tous les listeners d'un ListenerSet
101 *
102 * @param ls The feature to be added to the All attribute
103 */
104 public void addAll(ListenerSet<L> ls) {
105 listeners.addAll(ls.listeners);
106 }
107
108 /**
109 * Appel la méthode du listener en passant l'objet event en paramètre
110 * Cette méthode echoue si la methode ou l'objet contenant la methode a
111 * appeler n'est pas public
112 *
113 * @param methodName le nom de la methode a appeler
114 * @param event l'event a passer en parametre de la methode a appeler
115 * @throws Exception si un des listeners leve une exception lors de l'appel
116 */
117 public void fire(String methodName, Object event) throws Exception {
118 for (Iterator<L> i = iterator(); i.hasNext(); ) {
119 L o = i.next();
120 Statement stm = new Statement(o, methodName, new Object[]{event});
121 stm.execute();
122 }
123 }
124
125 /**
126 * Appele la méthode du listener sans argument.
127 *
128 * Cette méthode echoue si la methode ou l'objet contenant la methode a
129 * appeler n'est pas public.
130 *
131 * @param methodName le nom de la methode a appeler
132 * @throws Exception si un des listeners leve une exception lors de l'appel
133 */
134 public void fire(String methodName) throws Exception {
135 for (Iterator<L> i = iterator(); i.hasNext(); ) {
136 L o = i.next();
137 Statement stm = new Statement(o, methodName, null);
138 stm.execute();
139 }
140 }
141
142 /**
143 * Get iterator on listener list.
144 *
145 * @return iterator on listener list.
146 */
147 public Iterator<L> iterator() {
148 return new ReferenceIterator<L>(listeners.iterator());
149 }
150
151 /**
152 * Remove listener.
153 *
154 * @param l listener to remove
155 */
156 public void remove(L l) {
157 TransparenteWeakReference<L> ref = new TransparenteWeakReference<L>(l);
158 listeners.remove(ref);
159 }
160
161 @Override
162 public String toString() {
163 return listeners.toString();
164 }
165
166 /** Iterator qui supprime les references vides lors du parcours */
167 static class ReferenceIterator<T> implements Iterator<T> {
168 /** DOCUMENTME Description of the Field */
169 protected Iterator<Reference<T>> iter = null;
170
171 /** DOCUMENTME Description of the Field */
172 protected T nextObject = null;
173
174 /**
175 * Un iterator contenant des References
176 *
177 * @param iter DOCUMENTME Description of the Parameter
178 */
179 public ReferenceIterator(Iterator<Reference<T>> iter) {
180 this.iter = iter;
181 findNext();
182 }
183
184 /** DOCUMENTME Method */
185 protected void findNext() {
186 while (iter.hasNext() && nextObject == null) {
187 Reference<T> ref = iter.next();
188 T o = ref.get();
189 if (o != null) {
190 nextObject = o;
191 } else {
192 iter.remove();
193 }
194 }
195 }
196
197 /**
198 * DOCUMENTME Method
199 *
200 * @return DOCUMENTME Description of the Return Value
201 */
202 public boolean hasNext() {
203 return nextObject != null;
204 }
205
206 /**
207 * DOCUMENTME Method
208 *
209 * @return DOCUMENTME Description of the Return Value
210 */
211 public T next() {
212 T result = nextObject;
213 nextObject = null;
214 findNext();
215 return result;
216 }
217
218 /** DOCUMENTME Method */
219 public void remove() {
220 iter.remove();
221 }
222 }
223
224 } // ListenerSet