##### Coverage Report - org.apache.commons.configuration.event.EventSource

Classes in this File | Line Coverage | Branch Coverage | Complexity
EventSource |
96% | 68/71
100% | 9/9
| 1.4285714285714286;1,429

1 |  |
     /*
2 |  |
      * Licensed to the Apache Software Foundation (ASF) under one or more
3 |  |
      * contributor license agreements.  See the NOTICE file distributed with
4 |  |
      * this work for additional information regarding copyright ownership.
5 |  |
      * The ASF licenses this file to You under the Apache License, Version 2.0
6 |  |
      * (the "License"); you may not use this file except in compliance with
7 |  |
      * the License.  You may obtain a copy of the License at
8 |  |
      *
9 |  |
      *     http://www.apache.org/licenses/LICENSE-2.0
10 |  |
      *
11 |  |
      * Unless required by applicable law or agreed to in writing, software
12 |  |
      * distributed under the License is distributed on an "AS IS" BASIS,
13 |  |
      * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 |  |
      * See the License for the specific language governing permissions and
15 |  |
      * limitations under the License.
16 |  |
      */
17 |  |
     package org.apache.commons.configuration.event;
18 |  |
19 |  |
     import java.util.ArrayList;
20 |  |
     import java.util.Collection;
21 |  |
     import java.util.Collections;
22 |  |
     import java.util.Iterator;
23 |  |
     import java.util.LinkedList;
24 |  |
25 |  |
     /**
26 |  |
      * <p>
27 |  |
      * A base class for objects that can generate configuration events.
28 |  |
      * </p>
29 |  |
      * <p>
30 |  |
      * This class implements functionality for managing a set of event listeners
31 |  |
      * that can be notified when an event occurs. It can be extended by
32 |  |
      * configuration classes that support the event machanism. In this case these
33 |  |
      * classes only need to call the <code>fireEvent()</code> method when an event
34 |  |
      * is to be delivered to the registered listeners.
35 |  |
      * </p>
36 |  |
      * <p>
37 |  |
      * Adding and removing event listeners can happen concurrently to manipulations
38 |  |
      * on a configuration that cause events. The operations are synchronized.
39 |  |
      * </p>
40 |  |
      * <p>
41 |  |
      * With the <code>detailEvents</code> property the number of detail events can
42 |  |
      * be controlled. Some methods in configuration classes are implemented in a way
43 |  |
      * that they call other methods that can generate their own events. One example
44 |  |
      * is the <code>setProperty()</code> method that can be implemented as a
45 |  |
      * combination of <code>clearProperty()</code> and <code>addProperty()</code>.
46 |  |
      * With <code>detailEvents</code> set to <b>true</b>, all involved methods
47 |  |
      * will generate events (i.e. listeners will receive property set events,
48 |  |
      * property clear events, and property add events). If this mode is turned off
49 |  |
      * (which is the default), detail events are suppressed, so only property set
50 |  |
      * events will be received. Note that the number of received detail events may
51 |  |
      * differ for different configuration implementations.
52 |  |
      * <code>{@link org.apache.commons.configuration.HierarchicalConfiguration HierarchicalConfiguration}</code>
53 |  |
      * for instance has a custom implementation of <code>setProperty()</code>,
54 |  |
      * which does not generate any detail events.
55 |  |
      * </p>
56 |  |
      * <p>
57 |  |
      * In addition to &quot;normal&quot; events, error events are supported. Such
58 |  |
      * events signal an internal problem that occurred during access of properties.
59 |  |
      * For them a special listener interface exists:
60 |  |
      * <code>{@link ConfigurationErrorListener}</code>. There is another set of
61 |  |
      * methods dealing with event listeners of this type. The
62 |  |
      * <code>fireError()</code> method can be used by derived classes to send
63 |  |
      * notifications about errors to registered observers.
64 |  |
      * </p>
65 |  |
      *
66 |  |
      * @author <a href="http://commons.apache.org/configuration/team-list.html">Commons Configuration team</a>
67 |  |
      * @version $Id: EventSource.java 561230 2007-07-31 04:17:09Z rahul $
68 |  |
      * @since 1.3
69 |  |
      */
70 |  |
     public class EventSource
71 |  |
     {
72 |  |
         /** A collection for the registered event listeners. */
73 |  |
         private Collection listeners;
74 |  |
75 |  |
         /** A collection for the registered error listeners.*/
76 |  |
         private Collection errorListeners;
77 |  |
78 |  |
         /** A counter for the detail events. */
79 |  |
         private int detailEvents;
80 |  |
81 |  |
         /**
82 |  |
          * Creates a new instance of <code>EventSource</code>.
83 |  |
          */
84 |  |
         public EventSource()
85 | 3335 |
         {
86 | 3335 |
             initListeners();
87 | 3335 |
         }
88 |  |
89 |  |
         /**
90 |  |
          * Adds a configuration listener to this object.
91 |  |
          *
92 |  |
          * @param l the listener to add
93 |  |
          */
94 |  |
         public void addConfigurationListener(ConfigurationListener l)
95 |  |
         {
96 | 2088 |
             doAddListener(listeners, l);
97 | 2087 |
         }
98 |  |
99 |  |
         /**
100 |  |
          * Removes the specified event listener so that it does not receive any
101 |  |
          * further events caused by this object.
102 |  |
          *
103 |  |
          * @param l the listener to be removed
104 |  |
          * @return a flag whether the event listener was found
105 |  |
          */
106 |  |
         public boolean removeConfigurationListener(ConfigurationListener l)
107 |  |
         {
108 | 451 |
             return doRemoveListener(listeners, l);
109 |  |
         }
110 |  |
111 |  |
         /**
112 |  |
          * Returns a collection with all configuration event listeners that are
113 |  |
          * currently registered at this object.
114 |  |
          *
115 |  |
          * @return a collection with the registered
116 |  |
          * <code>ConfigurationListener</code>s (this collection is a snapshot
117 |  |
          * of the currently registered listeners; manipulating it has no effect
118 |  |
          * on this event source object)
119 |  |
          */
120 |  |
         public Collection getConfigurationListeners()
121 |  |
         {
122 | 28 |
             return doGetListeners(listeners);
123 |  |
         }
124 |  |
125 |  |
         /**
126 |  |
          * Removes all registered configuration listeners.
127 |  |
          */
128 |  |
         public void clearConfigurationListeners()
129 |  |
         {
130 | 5 |
             doClearListeners(listeners);
131 | 5 |
         }
132 |  |
133 |  |
         /**
134 |  |
          * Returns a flag whether detail events are enabled.
135 |  |
          *
136 |  |
          * @return a flag if detail events are generated
137 |  |
          */
138 |  |
         public boolean isDetailEvents()
139 |  |
         {
140 | 4 |
             synchronized (listeners)
141 |  |
             {
142 | 4 |
                 return detailEvents > 0;
143 | 0 |
             }
144 |  |
         }
145 |  |
146 |  |
         /**
147 |  |
          * Determines whether detail events should be generated. If enabled, some
148 |  |
          * methods can generate multiple update events. Note that this method
149 |  |
          * records the number of calls, i.e. if for instance
150 |  |
          * <code>setDetailEvents(false)</code> was called three times, you will
151 |  |
          * have to invoke the method as often to enable the details.
152 |  |
          *
153 |  |
          * @param enable a flag if detail events should be enabled or disabled
154 |  |
          */
155 |  |
         public void setDetailEvents(boolean enable)
156 |  |
         {
157 | 15355 |
             synchronized (listeners)
158 |  |
             {
159 | 15355 |
                 if (enable)
160 |  |
                 {
161 | 7690 |
                     detailEvents++;
162 | 7690 |
                 }
163 |  |
                 else
164 |  |
                 {
165 | 7665 |
                     detailEvents--;
166 |  |
                 }
167 | 15355 |
             }
168 | 15355 |
         }
169 |  |
170 |  |
         /**
171 |  |
          * Adds a new configuration error listener to this object. This listener
172 |  |
          * will then be notified about internal problems.
173 |  |
          *
174 |  |
          * @param l the listener to register (must not be <b>null</b>)
175 |  |
          * @since 1.4
176 |  |
          */
177 |  |
         public void addErrorListener(ConfigurationErrorListener l)
178 |  |
         {
179 | 1399 |
             doAddListener(errorListeners, l);
180 | 1398 |
         }
181 |  |
182 |  |
         /**
183 |  |
          * Removes the specified error listener so that it does not receive any
184 |  |
          * further events caused by this object.
185 |  |
          *
186 |  |
          * @param l the listener to remove
187 |  |
          * @return a flag whether the listener could be found and removed
188 |  |
          * @since 1.4
189 |  |
          */
190 |  |
         public boolean removeErrorListener(ConfigurationErrorListener l)
191 |  |
         {
192 | 15 |
             return doRemoveListener(errorListeners, l);
193 |  |
         }
194 |  |
195 |  |
         /**
196 |  |
          * Removes all registered error listeners.
197 |  |
          *
198 |  |
          * @since 1.4
199 |  |
          */
200 |  |
         public void clearErrorListeners()
201 |  |
         {
202 | 35 |
             doClearListeners(errorListeners);
203 | 35 |
         }
204 |  |
205 |  |
         /**
206 |  |
          * Returns a collection with all configuration error listeners that are
207 |  |
          * currently registered at this object.
208 |  |
          *
209 |  |
          * @return a collection with the registered
210 |  |
          * <code>ConfigurationErrorListener</code>s (this collection is a
211 |  |
          * snapshot of the currently registered listeners; it cannot be manipulated)
212 |  |
          * @since 1.4
213 |  |
          */
214 |  |
         public Collection getErrorListeners()
215 |  |
         {
216 | 21 |
             return doGetListeners(errorListeners);
217 |  |
         }
218 |  |
219 |  |
         /**
220 |  |
          * Creates an event object and delivers it to all registered event
221 |  |
          * listeners. The method will check first if sending an event is allowed
222 |  |
          * (making use of the <code>detailEvents</code> property), and if
223 |  |
          * listeners are registered.
224 |  |
          *
225 |  |
          * @param type the event's type
226 |  |
          * @param propName the name of the affected property (can be <b>null</b>)
227 |  |
          * @param propValue the value of the affected property (can be <b>null</b>)
228 |  |
          * @param before the before update flag
229 |  |
          */
230 |  |
         protected void fireEvent(int type, String propName, Object propValue, boolean before)
231 |  |
         {
232 | 78450 |
             Collection listenersToCall = null;
233 |  |
234 | 78450 |
             synchronized (listeners)
235 |  |
             {
236 | 78450 |
                 if (detailEvents >= 0 && listeners.size() > 0)
237 |  |
                 {
238 |  |
                     // Copy listeners to another collection so that manipulating
239 |  |
                     // the listener list during event delivery won't cause problems
240 | 916 |
                     listenersToCall = new ArrayList(listeners);
241 |  |
                 }
242 | 78450 |
             }
243 |  |
244 | 78450 |
             if (listenersToCall != null)
245 |  |
             {
246 | 916 |
                 ConfigurationEvent event = createEvent(type, propName, propValue, before);
247 | 916 |
                 for (Iterator it = listenersToCall.iterator(); it.hasNext();)
248 |  |
                 {
249 | 969 |
                     ((ConfigurationListener) it.next()).configurationChanged(event);
250 | 969 |
                 }
251 |  |
             }
252 | 78450 |
         }
253 |  |
254 |  |
         /**
255 |  |
          * Creates a <code>ConfigurationEvent</code> object based on the passed in
256 |  |
          * parameters. This is called by <code>fireEvent()</code> if it decides
257 |  |
          * that an event needs to be generated.
258 |  |
          *
259 |  |
          * @param type the event's type
260 |  |
          * @param propName the name of the affected property (can be <b>null</b>)
261 |  |
          * @param propValue the value of the affected property (can be <b>null</b>)
262 |  |
          * @param before the before update flag
263 |  |
          * @return the newly created event object
264 |  |
          */
265 |  |
         protected ConfigurationEvent createEvent(int type, String propName, Object propValue, boolean before)
266 |  |
         {
267 | 916 |
             return new ConfigurationEvent(this, type, propName, propValue, before);
268 |  |
         }
269 |  |
270 |  |
         /**
271 |  |
          * Creates an error event object and delivers it to all registered error
272 |  |
          * listeners.
273 |  |
          *
274 |  |
          * @param type the event's type
275 |  |
          * @param propName the name of the affected property (can be <b>null</b>)
276 |  |
          * @param propValue the value of the affected property (can be <b>null</b>)
277 |  |
          * @param ex the <code>Throwable</code> object that caused this error event
278 |  |
          * @since 1.4
279 |  |
          */
280 |  |
         protected void fireError(int type, String propName, Object propValue, Throwable ex)
281 |  |
         {
282 | 28 |
             Collection listenersToCall = null;
283 |  |
284 | 28 |
             synchronized (errorListeners)
285 |  |
             {
286 | 28 |
                 if (errorListeners.size() > 0)
287 |  |
                 {
288 |  |
                     // Copy listeners to another collection so that manipulating
289 |  |
                     // the listener list during event delivery won't cause problems
290 | 15 |
                     listenersToCall = new ArrayList(errorListeners);
291 |  |
                 }
292 | 28 |
             }
293 |  |
294 | 28 |
             if (listenersToCall != null)
295 |  |
             {
296 | 15 |
                 ConfigurationErrorEvent event = createErrorEvent(type, propName, propValue, ex);
297 | 15 |
                 for (Iterator it = listenersToCall.iterator(); it.hasNext();)
298 |  |
                 {
299 | 15 |
                     ((ConfigurationErrorListener) it.next()).configurationError(event);
300 | 14 |
                 }
301 |  |
             }
302 | 27 |
         }
303 |  |
304 |  |
         /**
305 |  |
          * Creates a <code>ConfigurationErrorEvent</code> object based on the
306 |  |
          * passed in parameters. This is called by <code>fireError()</code> if it
307 |  |
          * decides that an event needs to be generated.
308 |  |
          *
309 |  |
          * @param type the event's type
310 |  |
          * @param propName the name of the affected property (can be <b>null</b>)
311 |  |
          * @param propValue the value of the affected property (can be <b>null</b>)
312 |  |
          * @param ex the <code>Throwable</code> object that caused this error
313 |  |
          * event
314 |  |
          * @return the event object
315 |  |
          * @since 1.4
316 |  |
          */
317 |  |
         protected ConfigurationErrorEvent createErrorEvent(int type, String propName, Object propValue, Throwable ex)
318 |  |
         {
319 | 15 |
             return new ConfigurationErrorEvent(this, type, propName, propValue, ex);
320 |  |
         }
321 |  |
322 |  |
         /**
323 |  |
          * Overrides the <code>clone()</code> method to correctly handle so far
324 |  |
          * registered event listeners. This implementation ensures that the clone
325 |  |
          * will have empty event listener lists, i.e. the listeners registered at an
326 |  |
          * <code>EventSource</code> object will not be copied.
327 |  |
          *
328 |  |
          * @return the cloned object
329 |  |
          * @throws CloneNotSupportedException if cloning is not allowed
330 |  |
          * @since 1.4
331 |  |
          */
332 |  |
         protected Object clone() throws CloneNotSupportedException
333 |  |
         {
334 | 27 |
             EventSource copy = (EventSource) super.clone();
335 | 27 |
             copy.initListeners();
336 | 27 |
             return copy;
337 |  |
         }
338 |  |
339 |  |
         /**
340 |  |
          * Adds a new listener object to a listener collection. This is done in a
341 |  |
          * synchronized block. The listener must not be <b>null</b>.
342 |  |
          *
343 |  |
          * @param listeners the collection with the listeners
344 |  |
          * @param l the listener object
345 |  |
          */
346 |  |
         private static void doAddListener(Collection listeners, Object l)
347 |  |
         {
348 | 3487 |
             if (l == null)
349 |  |
             {
350 | 2 |
                 throw new IllegalArgumentException("Listener must not be null!");
351 |  |
             }
352 | 3485 |
             synchronized (listeners)
353 |  |
             {
354 | 3485 |
                 listeners.add(l);
355 | 3485 |
             }
356 | 3485 |
         }
357 |  |
358 |  |
         /**
359 |  |
          * Removes an event listener from a listener collection. This is done in a
360 |  |
          * synchronized block.
361 |  |
          *
362 |  |
          * @param listeners the collection with the listeners
363 |  |
          * @param l the listener object
364 |  |
          * @return a flag whether the listener could be found and removed
365 |  |
          */
366 |  |
         private static boolean doRemoveListener(Collection listeners, Object l)
367 |  |
         {
368 | 466 |
             synchronized (listeners)
369 |  |
             {
370 | 466 |
                 return listeners.remove(l);
371 | 0 |
             }
372 |  |
         }
373 |  |
374 |  |
         /**
375 |  |
          * Removes all entries from the given list of event listeners.
376 |  |
          *
377 |  |
          * @param listeners the collection with the listeners
378 |  |
          */
379 |  |
         private static void doClearListeners(Collection listeners)
380 |  |
         {
381 | 40 |
             synchronized (listeners)
382 |  |
             {
383 | 40 |
                 listeners.clear();
384 | 40 |
             }
385 | 40 |
         }
386 |  |
387 |  |
         /**
388 |  |
          * Returns an unmodifiable snapshot of the given event listener collection.
389 |  |
          *
390 |  |
          * @param listeners the collection with the listeners
391 |  |
          * @return a snapshot of the listeners collection
392 |  |
          */
393 |  |
         private static Collection doGetListeners(Collection listeners)
394 |  |
         {
395 | 49 |
             synchronized (listeners)
396 |  |
             {
397 | 49 |
                 return Collections.unmodifiableCollection(new ArrayList(listeners));
398 | 0 |
             }
399 |  |
         }
400 |  |
401 |  |
         /**
402 |  |
          * Initializes the collections for storing registered event listeners.
403 |  |
          */
404 |  |
         private void initListeners()
405 |  |
         {
406 | 3362 |
             listeners = new LinkedList();
407 | 3362 |
             errorListeners = new LinkedList();
408 | 3362 |
         }
409 |  |
     }