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