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 }