View Javadoc

1   /*
2    * Copyright 2006-2016 The JGUIraffe Team.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License")
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package net.sf.jguiraffe.gui.builder.event;
17  
18  import java.util.Collections;
19  import java.util.EventListener;
20  import java.util.HashMap;
21  import java.util.Map;
22  import java.util.NoSuchElementException;
23  import java.util.Set;
24  import java.util.concurrent.locks.Lock;
25  import java.util.concurrent.locks.ReentrantLock;
26  
27  import javax.swing.event.EventListenerList;
28  
29  import net.sf.jguiraffe.di.InjectionException;
30  import net.sf.jguiraffe.di.InvocationHelper;
31  import net.sf.jguiraffe.gui.forms.ComponentHandler;
32  import net.sf.jguiraffe.gui.forms.ComponentStore;
33  
34  import org.apache.commons.logging.Log;
35  import org.apache.commons.logging.LogFactory;
36  
37  /**
38   * <p>
39   * The main class for event handling in the form framework.
40   * </p>
41   * <p>
42   * When programming against a concrete GUI framework like Swing or AWT, a
43   * developer usually registers event listeners directly at the components of
44   * interest. In this generic GUI framework the same task is done using this
45   * class.
46   * </p>
47   * <p>
48   * This class is used by clients of the form framework to register event
49   * listeners at components of a form. It provides an
50   * <code>addXXXListener()</code> method for each supported event listener type.
51   * Each of these methods comes in two overloaded versions: One version allows to
52   * specify the name of a concrete component. The listener will then only be
53   * registered at that component. The other version registers the listeners for
54   * all components; so it will receive all occurring events of the corresponding
55   * type. Corresponding <code>removeXXXListener()</code> methods for
56   * unregistering listeners are also available.
57   * </p>
58   * <p>
59   * Registering and unregistering event listeners can be done from arbitrary
60   * threads and will not conflict with the firing of events. This class
61   * implements proper synchronization. It is also possible to add or remove
62   * listeners in an invoked event handler.
63   * </p>
64   * <p>
65   * This event manager class provides an additional level of abstraction over the
66   * platform (i.e. GUI library) specific event manager class (the concrete
67   * implementation of the {@link PlatformEventManager} interface).
68   * It is possible to use this implementation directly to register event
69   * listeners. The reason for this additional level is that it provides a simple
70   * way for registering broadcast event listeners, i.e. listeners that are
71   * capable of receiving all events of a specific type, caused by all involved
72   * components. In addition it provides an easier interface for the listener
73   * registration process.
74   * </p>
75   * <p>
76   * The event manager class needs access to the component handlers of the
77   * available components. For this purpose it maintains a reference to a
78   * {@link ComponentStore} instance. This instance must have been
79   * initialized before the manager can be used.
80   * </p>
81   *
82   * @author Oliver Heger
83   * @version $Id: FormEventManager.java 205 2012-01-29 18:29:57Z oheger $
84   */
85  public class FormEventManager
86  {
87      /** Constant for the name pattern for an add event listener method. */
88      private static final String METH_ADD_LISTENER = "add%sListener";
89  
90      /** Constant for the name pattern for a remove event listener method. */
91      private static final String METH_REMOVE_LISTENER = "remove%sListener";
92  
93      /**
94       * Constant for the argument types of an event listener registration method.
95       */
96      private static final Class<?>[] LISTENER_REG_TYPES = {
97          null
98      };
99  
100     /** The logger used by this class. */
101     private final Log log = LogFactory.getLog(getClass());
102 
103     /** Stores a reference to the platform event manager. */
104     private final PlatformEventManager platformEventManager;
105 
106     /** Holds a reference to the component store storing the actual components. */
107     private ComponentStore componentStore;
108 
109     /** A map with event listeners that are registered at specific components. */
110     private final Map<String, EventListenerList> namedListeners;
111 
112     /** Stores then listeners that are registered for all components. */
113     private final EventListenerList allListeners;
114 
115     /** A helper object for invoking a method using reflection. */
116     private final InvocationHelper invocationHelper;
117 
118     /** A lock for protecting adding and removing event listeners. */
119     private final Lock lockListeners;
120 
121     /**
122      * Creates a new instance of <code>FormEventManager</code> and sets the
123      * platform specific event manager.
124      *
125      * @param platformEventManager the platform event manager
126      */
127     public FormEventManager(PlatformEventManager platformEventManager)
128     {
129         this.platformEventManager = platformEventManager;
130         namedListeners = new HashMap<String, EventListenerList>();
131         allListeners = new EventListenerList();
132         lockListeners = new ReentrantLock();
133         invocationHelper = new InvocationHelper();
134     }
135 
136     /**
137      * Returns the platform specific event manager.
138      *
139      * @return the platform event manager
140      */
141     public PlatformEventManager getPlatformEventManager()
142     {
143         return platformEventManager;
144     }
145 
146     /**
147      * Returns the component store used by this event manager instance.
148      *
149      * @return the underlying component store
150      */
151     public ComponentStore getComponentStore()
152     {
153         return componentStore;
154     }
155 
156     /**
157      * Sets the component store to be used. From this store the event manager
158      * will fetch the <code>ComponentHandler</code> objects of the components,
159      * for which event listeners are to be registered.
160      *
161      * @param componentStore the component store to be used
162      */
163     public void setComponentStore(ComponentStore componentStore)
164     {
165         this.componentStore = componentStore;
166     }
167 
168     /**
169      * Returns the component handler with the given name. If this name is
170      * undefined, <b>null </b> is returned.
171      *
172      * @param name the name of the desired component
173      * @return the component handler for the component with this name
174      * @throws IllegalStateException if no component store has been set
175      */
176     public ComponentHandler<?> getComponentHandler(String name)
177     {
178         if (getComponentStore() == null)
179         {
180             throw new IllegalStateException("ComponentStore must be set!");
181         }
182         return getComponentStore().findComponentHandler(name);
183     }
184 
185     /**
186      * Adds an action listener for the specified component.
187      *
188      * @param name the component's name
189      * @param l the listener to add (can be <b>null</b>, then this operation
190      *        will have no effect)
191      * @throws NoSuchElementException if no component with this names exists
192      */
193     public void addActionListener(String name, FormActionListener l)
194     {
195         addListener(FormListenerType.ACTION, name, l);
196     }
197 
198     /**
199      * Adds an action listener that will be notified about all occurring action
200      * events.
201      *
202      * @param l the listener to add (can be <b>null</b>, then this operation
203      *        will have no effect)
204      */
205     public void addActionListener(FormActionListener l)
206     {
207         addActionListener(null, l);
208     }
209 
210     /**
211      * Removes the specified action listener for the specified component.
212      *
213      * @param name the component's name
214      * @param l the listener to be removed (if this listener is not registered
215      *        at this component, this operation will have no effect)
216      */
217     public void removeActionListener(String name, FormActionListener l)
218     {
219         removeListener(FormListenerType.ACTION, name, l);
220     }
221 
222     /**
223      * Removes an action listener that is registered for all components.
224      *
225      * @param l the action listener to be removed (if this listener is not
226      *        registered for all components, this operation will have no effect)
227      */
228     public void removeActionListener(FormActionListener l)
229     {
230         removeActionListener(null, l);
231     }
232 
233     /**
234      * Adds a change listener for the specified component.
235      *
236      * @param name the component's name
237      * @param l the listener to add
238      * @throws NoSuchElementException if no component with this names exists
239      */
240     public void addChangeListener(String name, FormChangeListener l)
241     {
242         addListener(FormListenerType.CHANGE, name, l);
243     }
244 
245     /**
246      * Adds a change listener that will be notified about all occurring change
247      * events.
248      *
249      * @param l the listener to add
250      */
251     public void addChangeListener(FormChangeListener l)
252     {
253         addChangeListener(null, l);
254     }
255 
256     /**
257      * Removes the specified change listener for the specified component.
258      *
259      * @param name the component's name
260      * @param l the listener to be removed (if this listener is not registered
261      *        at this component, this operation will have no effect)
262      */
263     public void removeChangeListener(String name, FormChangeListener l)
264     {
265         removeListener(FormListenerType.CHANGE, name, l);
266     }
267 
268     /**
269      * Removes a change listener that is registered for all components.
270      *
271      * @param l the change listener to be removed (if this listener is not
272      *        registered for all components, this operation will have no effect)
273      */
274     public void removeChangeListener(FormChangeListener l)
275     {
276         removeChangeListener(null, l);
277     }
278 
279     /**
280      * Adds a focus listener for the specified component.
281      *
282      * @param name the component's name
283      * @param l the listener to add
284      * @throws NoSuchElementException if no component with this names exists
285      */
286     public void addFocusListener(String name, FormFocusListener l)
287     {
288         addListener(FormListenerType.FOCUS, name, l);
289     }
290 
291     /**
292      * Adds a focus listener that will be notified about all occurring focus
293      * events.
294      *
295      * @param l the listener to add
296      */
297     public void addFocusListener(FormFocusListener l)
298     {
299         addFocusListener(null, l);
300     }
301 
302     /**
303      * Removes the specified focus listener for the specified component.
304      *
305      * @param name the component's name
306      * @param l the listener to be removed (if this listener is not registered
307      *        at this component, this operation will have no effect)
308      */
309     public void removeFocusListener(String name, FormFocusListener l)
310     {
311         removeListener(FormListenerType.FOCUS, name, l);
312     }
313 
314     /**
315      * Removes a focus listener that is registered for all components.
316      *
317      * @param l the focus listener to be removed (if this listener is not
318      *        registered for all components, this operation will have no effect)
319      */
320     public void removeFocusListener(FormFocusListener l)
321     {
322         removeFocusListener(null, l);
323     }
324 
325     /**
326      * Adds a mouse listener to the specified component.
327      *
328      * @param name the name of the component
329      * @param l the listener to add
330      * @throws NoSuchElementException if no component with this name exists
331      */
332     public void addMouseListener(String name, FormMouseListener l)
333     {
334         addListener(FormListenerType.MOUSE, name, l);
335     }
336 
337     /**
338      * Adds a mouse listener that will be notified about all mouse events
339      * generated for the components in the current form.
340      *
341      * @param l the listener to add
342      */
343     public void addMouseListener(FormMouseListener l)
344     {
345         addMouseListener(null, l);
346     }
347 
348     /**
349      * Removes the specified mouse listener from the component with the given
350      * name. If this listener is not registered at this component, this
351      * operation has no effect.
352      *
353      * @param name the name of the component
354      * @param l the listener to be removed
355      */
356     public void removeMouseListener(String name, FormMouseListener l)
357     {
358         removeListener(FormListenerType.MOUSE, name, l);
359     }
360 
361     /**
362      * Removes a mouse listener that is registered for all components. If the
363      * listener is unknown, this operation has no effect.
364      *
365      * @param l the listener to be removed
366      */
367     public void removeMouseListener(FormMouseListener l)
368     {
369         removeMouseListener(null, l);
370     }
371 
372     /**
373      * Sends the specified event to all registered listeners. This
374      * implementation relies on the given event object to be properly
375      * initialized, especially the component handler and name fields must be
376      * correctly filled. From the event the name of the affected component is
377      * extracted. All event listeners of the specified type that are registered
378      * for this specific component are notified first. Then the unspecific event
379      * listeners are invoked.
380      *
381      * @param event the event
382      * @param type the event listener type
383      */
384     public void fireEvent(FormEvent event, FormListenerType type)
385     {
386         lock(type);
387         try
388         {
389             if (event.getName() != null)
390             {
391                 EventListenerList listeners = fetchListenersForComponent(event
392                         .getName(), false);
393                 if (listeners != null)
394                 {
395                     type.callListeners(listeners, event);
396                 }
397             }
398 
399             type.callListeners(allListeners, event);
400         }
401         finally
402         {
403             unlock(type);
404         }
405     }
406 
407     /**
408      * Adds a generic event listener to a component. This is the most generic
409      * way of adding an event listener. While there are specific methods for
410      * adding default event listeners (e.g. {@code addActionListener()} or
411      * {@code addFocusListener()}), using this method arbitrary listeners can be
412      * added to components - also for non-standard events. The type of the
413      * listener is specified as a string. If this string refers to a standard
414      * event type (i.e. the string contains the name of one of the constants
415      * defined by the {@link FormListenerType} enumeration class ignoring case),
416      * the behavior of this method is exactly the same as if the corresponding
417      * specific {@code add()} method was called. Otherwise, the method uses
418      * reflection to find a corresponding method for adding the listener to a
419      * {@code ComponentHandler} based on naming conventions. The following
420      * convention is used: If the string <em>Foo</em> is passed, a method with
421      * the name <em>addFooListener()</em> is searched. This method is then
422      * invoked passing in the specified event listener. The type of the listener
423      * must be compatible with the listener type expected by the {@code
424      * addXXXListener()} method. For instance, the {@code TreeHandler}
425      * interface, an extension of {@code ComponentHandler} defines an {@code
426      * addExpansionListener()} method for adding specialized listeners for tree
427      * events. By passing in the string <em>Expansion</em> it is possible to
428      * register listeners of this type. If a component name is passed in, the
429      * listener is only registered at this component. Otherwise all {@code
430      * ComponentHandler} objects currently known are searched for corresponding
431      * methods for adding event listeners. The return value of this method
432      * determines the number of components, for which the event listener was
433      * added. A return value of 0 typically indicates that something went wrong:
434      * maybe there was a typo in the string representing the event listener
435      * type.
436      *
437      * @param componentName the name of the component, for which the listener
438      *        should be added; can be <b>null</b>, then all fitting components
439      *        are processed
440      * @param listenerType a string determining the listener type
441      * @param l the listener to be added (can be <b>null</b>, then this method
442      *        has no effect)
443      * @return the number of components the event listener was registered at
444      */
445     public int addEventListener(String componentName, String listenerType,
446             EventListener l)
447     {
448         return processEventListener(componentName, listenerType, l, false);
449     }
450 
451     /**
452      * Removes an arbitrary event listener from a component. This method is the
453      * counterpart of the
454      * {@link #addEventListener(String, String, EventListener)} method. It works
455      * analogously to remove event listeners. The return value indicates the
456      * number of components for which the remove method for the event listener
457      * was called. This does not necessarily mean that the listener was actually
458      * registered at this components and was removed.
459      *
460      * @param componentName the name of the component, for which the listener
461      *        should be added; can be <b>null</b>, then all fitting components
462      *        are processed
463      * @param listenerType a string determining the listener type
464      * @param l the listener to be removed (can be <b>null</b>, then this method
465      *        has no effect)
466      * @return the number of components from which the event listener was
467      *         removed
468      */
469     public int removeEventListener(String componentName, String listenerType,
470             EventListener l)
471     {
472         return processEventListener(componentName, listenerType, l, true);
473     }
474 
475     /**
476      * Adds a generic event listener to the specified object. This method works
477      * like {@link #addEventListener(String, String, EventListener)}, however,
478      * the target object can be specified directly. The listener is registered
479      * using reflection as described in the comment for {@code
480      * addEventListener()}.
481      *
482      * @param target the target object to which the listener is to be registered
483      * @param listenerType a string determining the listener type
484      * @param l the listener to be added (can be <b>null</b>, then this method
485      *        has no effect)
486      * @return a flag whether the listener could be added successfully
487      */
488     public boolean addEventListenerToObject(Object target, String listenerType,
489             EventListener l)
490     {
491         return processNonStdEventListenerTargetObject(target, listenerType, l,
492                 false);
493     }
494 
495     /**
496      * Removes an arbitrary event listener from the specified object. This is
497      * the counterpart of
498      * {@link #addEventListenerToObject(Object, String, EventListener)}. It
499      * works analogously to
500      * {@link #removeEventListener(String, String, EventListener)}, but the
501      * object affected is directly passed.
502      *
503      * @param target the object from which the listener is to be removed
504      * @param listenerType a string determining the listener type
505      * @param l the listener to be removed (can be <b>null</b>, then this method
506      *        has no effect)
507      * @return a flag whether the listener could be removed without errors
508      */
509     public boolean removeEventListenerFromObject(Object target,
510             String listenerType, EventListener l)
511     {
512         return processNonStdEventListenerTargetObject(target, listenerType, l,
513                 true);
514     }
515 
516     /**
517      * Performs the actual adding of an event listener. This method is called by
518      * the various <code>addXXXListener()</code> methods.
519      *
520      * @param type the type of the listener to be added
521      * @param name the name of the component (<b>null</b> for all listeners)
522      * @param l the affected event listener
523      */
524     protected void addListener(FormListenerType type, String name,
525             FormEventListener l)
526     {
527         if (l != null)
528         {
529             if (name == null)
530             {
531                 addAllListener(type, l);
532             }
533             else
534             {
535                 addNamedListener(type, name, l);
536             }
537         }
538     }
539 
540     /**
541      * Performs the actual removal of an event listener. This method is called
542      * by the various {@code removeXXXListener()} methods.
543      *
544      * @param type the type of the listener to be removed
545      * @param name the name of the component (<b>null</b> for all listeners)
546      * @param l the affected event listener
547      * @return a flag whether the listener could be removed
548      */
549     protected boolean removeListener(FormListenerType type, String name,
550             FormEventListener l)
551     {
552         if (name == null)
553         {
554             return removeAllListener(type, l);
555         }
556         else
557         {
558             return removeNamedListener(type, name, l);
559         }
560     }
561 
562     /**
563      * Helper method for registering an event listener at a specific component.
564      *
565      * @param type the listener type
566      * @param name the component's name
567      * @param l the listener to register
568      * @throws NoSuchElementException if no component with this names exists
569      */
570     private void addNamedListener(FormListenerType type, String name,
571             FormEventListener l) throws NoSuchElementException
572     {
573         ComponentHandler<?> ch = getComponentHandler(name);
574         if (ch == null)
575         {
576             throw new NoSuchElementException("No component with the name "
577                     + name);
578         }
579 
580         lock(type);
581         try
582         {
583             if (fetchAllListenerCount(type) == 0
584                     && fetchNamedListenerCount(name, type) == 0)
585             {
586                 if (log.isInfoEnabled())
587                 {
588                     log.info("Registering listener of type " + type.name()
589                             + " for component " + name);
590                     getPlatformEventManager().registerListener(name, ch, this,
591                             type);
592                 }
593             }
594             registerListener(type, l, fetchListenersForComponent(name, true));
595         }
596         finally
597         {
598             unlock(type);
599         }
600     }
601 
602     /**
603      * Helper method for registering an event listener of a specific type for
604      * all components. This method registers this event manager as listener for
605      * the specified type for all components that have not yet been registered.
606      *
607      * @param type the listener type
608      * @param l the listener to register
609      */
610     private void addAllListener(FormListenerType type, FormEventListener l)
611     {
612         lock(type);
613         try
614         {
615             if (fetchAllListenerCount(type) < 1)
616             {
617                 for (String compName : getComponentStore()
618                         .getComponentHandlerNames())
619                 {
620                     if (fetchNamedListenerCount(compName, type) < 1)
621                     {
622                         if (log.isInfoEnabled())
623                         {
624                             log.info("Registering listener of type "
625                                     + type.name() + " for component "
626                                     + compName);
627                         }
628                         getPlatformEventManager().registerListener(compName,
629                                 getComponentHandler(compName), this, type);
630                     }
631                 }
632             }
633 
634             registerListener(type, l, allListeners);
635         }
636         finally
637         {
638             unlock(type);
639         }
640     }
641 
642     /**
643      * Removes a listener for a specific component.
644      *
645      * @param type the event listener type
646      * @param name the name of the component
647      * @param l the listener to be removed
648      * @return a flag whether the listener could be removed
649      */
650     private boolean removeNamedListener(FormListenerType type, String name,
651             FormEventListener l)
652     {
653         lock(type);
654         try
655         {
656             int countBefore = fetchNamedListenerCount(name, type);
657             if (countBefore > 0)
658             {
659                 unregisterListener(type, l, fetchListenersForComponent(name,
660                         false));
661 
662                 int countAfter = fetchNamedListenerCount(name, type);
663                 if (countAfter == 0 && fetchAllListenerCount(type) == 0)
664                 {
665                     getPlatformEventManager().unregisterListener(name,
666                             getComponentHandler(name), this, type);
667                 }
668 
669                 return countBefore > countAfter;
670             }
671         }
672         finally
673         {
674             unlock(type);
675         }
676 
677         return false;
678     }
679 
680     /**
681      * Removes the specified all listener from all components.
682      *
683      * @param type the event listener type
684      * @param l the listener to be removed
685      * @return a flag whether the listener could be removed
686      */
687     private boolean removeAllListener(FormListenerType type, FormEventListener l)
688     {
689         lock(type);
690         try
691         {
692             int countBefore = fetchAllListenerCount(type);
693             if (countBefore > 0)
694             {
695                 unregisterListener(type, l, allListeners);
696 
697                 int countAfter = fetchAllListenerCount(type);
698                 if (countAfter == 0)
699                 {
700                     for (String name : getComponentStore()
701                             .getComponentHandlerNames())
702                     {
703                         if (fetchNamedListenerCount(name, type) < 1)
704                         {
705                             getPlatformEventManager().unregisterListener(name,
706                                     getComponentHandler(name), this, type);
707                         }
708                     }
709                 }
710 
711                 return countBefore > countAfter;
712             }
713         }
714         finally
715         {
716             unlock(type);
717         }
718 
719         return false;
720     }
721 
722     /**
723      * Obtains the lock for the specified listener type. Adding or removing
724      * event listeners of the same type is not possible concurrently, so locks
725      * must be used. However in theory listeners of different types can be
726      * processed simultaneously. However practice has shown that this is
727      * problematic (especially on machines with a dual core processor). So we
728      * perform a strict locking here.
729      *
730      * @param type the listener type to be locked
731      */
732     private void lock(FormListenerType type)
733     {
734         lockListeners.lock();
735     }
736 
737     /**
738      * Unlocks the specified listener type again.
739      *
740      * @param type the listener type to be unlocked
741      * @see #lock(FormListenerType)
742      */
743     private void unlock(FormListenerType type)
744     {
745         lockListeners.unlock();
746     }
747 
748     /**
749      * Fetches the list with the event listeners registered for the specified
750      * component. This list will be created lazily on the first write access.
751      * The <code>create</code> parameter determines whether the list should be
752      * created if it does not exist yet. If it is <b>false</b>, the return value
753      * may be <b>null</b>.
754      *
755      * @param name the name of the component
756      * @param create the create flag
757      * @return the list with the event listeners registered at this component
758      */
759     private EventListenerList fetchListenersForComponent(String name,
760             boolean create)
761     {
762         synchronized (namedListeners)
763         {
764             EventListenerList result = namedListeners.get(name);
765             if (create && result == null)
766             {
767                 result = new EventListenerList();
768                 namedListeners.put(name, result);
769             }
770             return result;
771         }
772     }
773 
774     /**
775      * Determines the number of event listeners of the given type that are
776      * registered at the component with the given name.
777      *
778      * @param name the name of the component
779      * @param type the event listener type
780      * @return the number of registered listeners of that type
781      */
782     private int fetchNamedListenerCount(String name, FormListenerType type)
783     {
784         EventListenerList lst = fetchListenersForComponent(name, false);
785         return (lst != null) ? lst.getListenerCount(type.getListenerClass())
786                 : 0;
787     }
788 
789     /**
790      * Returns the number of event listeners of the specified type that are
791      * registered at all components.
792      *
793      * @param type the event listener type
794      * @return the number of all event listeners of that type
795      */
796     private int fetchAllListenerCount(FormListenerType type)
797     {
798         return allListeners.getListenerCount(type.getListenerClass());
799     }
800 
801     /**
802      * Adds an event listener of the specified type to the given event listener
803      * list.
804      *
805      * @param type the type
806      * @param l the listener
807      * @param listeners the list with the listeners
808      */
809     @SuppressWarnings("unchecked")
810     private void registerListener(FormListenerType type, FormEventListener l,
811             EventListenerList listeners)
812     {
813         listeners.add((Class<FormEventListener>) type.getListenerClass(), l);
814     }
815 
816     /**
817      * Removes a listener of the specified type from the given event listener
818      * list.
819      *
820      * @param type the type
821      * @param l the listener
822      * @param listeners the list with the listeners
823      */
824     @SuppressWarnings("unchecked")
825     private void unregisterListener(FormListenerType type, FormEventListener l,
826             EventListenerList listeners)
827     {
828         listeners.remove((Class<FormEventListener>) type.getListenerClass(), l);
829     }
830 
831     /**
832      * Helper method for adding or removing an event listener. This method is
833      * called by both {@code addEventListener()}, and {@code
834      * removeEventListener()} to actually handle the registration stuff.
835      *
836      * @param componentName the name of the component
837      * @param listenerType the type of the listener
838      * @param l the affected listener
839      * @param remove flag for remove (<b>true</b>) or add (<b>false</b>) the
840      *        listener
841      * @return the number of components affected by this operation
842      */
843     private int processEventListener(String componentName, String listenerType,
844             EventListener l, boolean remove)
845     {
846         if (l == null)
847         {
848             return 0;
849         }
850 
851         FormListenerType type = FormListenerType.fromString(listenerType);
852         if (type != null && type.getListenerClass().isInstance(l))
853         {
854             return processStdEventListener(componentName, type,
855                     (FormEventListener) l, remove);
856         }
857 
858         else
859         {
860             Set<String> compNames = (componentName != null) ? Collections
861                     .singleton(componentName) : getComponentStore()
862                     .getComponentHandlerNames();
863             return processNonStdEventListener(compNames, listenerType, l,
864                     remove);
865         }
866     }
867 
868     /**
869      * Helper method for adding or removing a standard event listener. This
870      * method is called by {@code processEventListener()} if the listener type
871      * can be resolved to a standard type. It delegates either to {@code
872      * addListener()} or {@code removeListener()}.
873      *
874      * @param componentName the name of the component
875      * @param type the type of the event listener
876      * @param l the affected listener
877      * @param remove the remove flag
878      * @return the number of components affected by this operation
879      */
880     private int processStdEventListener(String componentName,
881             FormListenerType type, FormEventListener l, boolean remove)
882     {
883         boolean success;
884 
885         try
886         {
887             if (remove)
888             {
889                 success = removeListener(type, componentName, l);
890             }
891             else
892             {
893                 addListener(type, componentName, l);
894                 success = true;
895             }
896         }
897         catch (NoSuchElementException nsex)
898         {
899             success = false;
900         }
901 
902         if (!success)
903         {
904             return 0;
905         }
906         else
907         {
908             return (componentName != null) ? 1 : getComponentStore()
909                     .getComponentHandlerNames().size();
910         }
911     }
912 
913     /**
914      * Helper method for adding or removing a non-standard event listener. This
915      * method is called by {@code processEventListener()} if the listener type
916      * does not refer to a standard type. It tries to invoke listener
917      * registration methods on the known {@code ComponentHandler} objects
918      * through reflection.
919      *
920      * @param compNames a set with the names of the {@code ComponentHandler}
921      *        objects to be processed
922      * @param listenerType the type of the event listener
923      * @param l the affected event listener
924      * @param remove the remove flag
925      * @return the number of {@code ComponentHandler} objects that could be
926      *         processed
927      */
928     private int processNonStdEventListener(Set<String> compNames,
929             String listenerType, EventListener l, boolean remove)
930     {
931         int successCount = 0;
932 
933         for (String compName : compNames)
934         {
935             ComponentHandler<?> handler = getComponentHandler(compName);
936             if (handler != null
937                     && doProcessNonStdEventListener(handler, listenerType, l,
938                             remove))
939             {
940                 successCount++;
941             }
942         }
943 
944         return successCount;
945     }
946 
947     /**
948      * A helper method that implements the major part of the functionality
949      * required by {@code addEventListenerToObject()} and {@code
950      * removeEventListenerFromObject()}. It performs some validity checks and
951      * then delegates to {@code doProcessNonStdEventListener()}.
952      *
953      * @param target the target object
954      * @param listenerType the type of the event listener
955      * @param l the affected event listener
956      * @param remove the remove flag
957      * @return a flag whether the operation was successful
958      */
959     private boolean processNonStdEventListenerTargetObject(
960             Object target, String listenerType, EventListener l, boolean remove)
961     {
962         return (target == null || l == null) ? false
963                 : doProcessNonStdEventListener(target, listenerType, l, remove);
964     }
965 
966     /**
967      * Helper method that uses reflection for adding or removing an event
968      * handler from an arbitrary object. The return value indicates, whether the
969      * operation was successful. Exceptions are caught.
970      *
971      * @param target the target object
972      * @param listenerType the type of the event listener
973      * @param l the affected event listener
974      * @param remove the remove flag
975      * @return a flag whether the operation was successful
976      */
977     private boolean doProcessNonStdEventListener(Object target,
978             String listenerType, EventListener l, boolean remove)
979     {
980         String methodName =
981                 String.format(
982                         remove ? METH_REMOVE_LISTENER : METH_ADD_LISTENER,
983                         listenerType);
984 
985         try
986         {
987             invocationHelper.invokeInstanceMethod(target, methodName,
988                     LISTENER_REG_TYPES, new Object[] {
989                         l
990                     });
991             return true;
992         }
993         catch (InjectionException iex)
994         {
995             log.info("Could not register listener of type " + listenerType
996                     + " at target object " + target, iex);
997             return false;
998         }
999     }
1000 }