KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > java > beans > EventHandler


1 /*
2  * @(#)EventHandler.java 1.16 05/08/09
3  *
4  * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7 package java.beans;
8
9 import java.lang.reflect.InvocationHandler JavaDoc;
10 import java.lang.reflect.InvocationTargetException JavaDoc;
11 import java.lang.reflect.Proxy JavaDoc;
12 import java.lang.reflect.Method JavaDoc;
13 import java.security.AccessControlContext JavaDoc;
14 import java.security.AccessController JavaDoc;
15 import java.security.PrivilegedAction JavaDoc;
16
17 import java.util.EventObject JavaDoc;
18 import sun.reflect.misc.MethodUtil;
19
20 /**
21  * The <code>EventHandler</code> class provides
22  * support for dynamically generating event listeners whose methods
23  * execute a simple statement involving an incoming event object
24  * and a target object.
25  * <p>
26  * The <code>EventHandler</code> class is intended to be used by interactive tools, such as
27  * application builders, that allow developers to make connections between
28  * beans. Typically connections are made from a user interface bean
29  * (the event <em>source</em>)
30  * to an application logic bean (the <em>target</em>). The most effective
31  * connections of this kind isolate the application logic from the user
32  * interface. For example, the <code>EventHandler</code> for a
33  * connection from a <code>JCheckBox</code> to a method
34  * that accepts a boolean value can deal with extracting the state
35  * of the check box and passing it directly to the method so that
36  * the method is isolated from the user interface layer.
37  * <p>
38  * Inner classes are another, more general way to handle events from
39  * user interfaces. The <code>EventHandler</code> class
40  * handles only a subset of what is possible using inner
41  * classes. However, <code>EventHandler</code> works better
42  * with the long-term persistence scheme than inner classes.
43  * Also, using <code>EventHandler</code> in large applications in
44  * which the same interface is implemented many times can
45  * reduce the disk and memory footprint of the application.
46  * <p>
47  * The reason that listeners created with <code>EventHandler</code>
48  * have such a small
49  * footprint is that the <code>Proxy</code> class, on which
50  * the <code>EventHandler</code> relies, shares implementations
51  * of identical
52  * interfaces. For example, if you use
53  * the <code>EventHandler</code> <code>create</code> methods to make
54  * all the <code>ActionListener</code>s in an application,
55  * all the action listeners will be instances of a single class
56  * (one created by the <code>Proxy</code> class).
57  * In general, listeners based on
58  * the <code>Proxy</code> class require one listener class
59  * to be created per <em>listener type</em> (interface),
60  * whereas the inner class
61  * approach requires one class to be created per <em>listener</em>
62  * (object that implements the interface).
63  *
64  * <p>
65  * You don't generally deal directly with <code>EventHandler</code>
66  * instances.
67  * Instead, you use one of the <code>EventHandler</code>
68  * <code>create</code> methods to create
69  * an object that implements a given listener interface.
70  * This listener object uses an <code>EventHandler</code> object
71  * behind the scenes to encapsulate information about the
72  * event, the object to be sent a message when the event occurs,
73  * the message (method) to be sent, and any argument
74  * to the method.
75  * The following section gives examples of how to create listener
76  * objects using the <code>create</code> methods.
77  *
78  * <h2>Examples of Using EventHandler</h2>
79  *
80  * The simplest use of <code>EventHandler</code> is to install
81  * a listener that calls a method on the target object with no arguments.
82  * In the following example we create an <code>ActionListener</code>
83  * that invokes the <code>toFront</code> method on an instance
84  * of <code>javax.swing.JFrame</code>.
85  *
86  * <blockquote>
87  *<pre>
88  *myButton.addActionListener(
89  * (ActionListener)EventHandler.create(ActionListener.class, frame, "toFront"));
90  *</pre>
91  * </blockquote>
92  *
93  * When <code>myButton</code> is pressed, the statement
94  * <code>frame.toFront()</code> will be executed. One could get
95  * the same effect, with some additional compile-time type safety,
96  * by defining a new implementation of the <code>ActionListener</code>
97  * interface and adding an instance of it to the button:
98  *
99  * <blockquote>
100  *<pre>
101 //Equivalent code using an inner class instead of EventHandler.
102  *myButton.addActionListener(new ActionListener() {
103  * public void actionPerformed(ActionEvent e) {
104  * frame.toFront();
105  * }
106  *});
107  *</pre>
108  * </blockquote>
109  *
110  * The next simplest use of <code>EventHandler</code> is
111  * to extract a property value from the first argument
112  * of the method in the listener interface (typically an event object)
113  * and use it to set the value of a property in the target object.
114  * In the following example we create an <code>ActionListener</code> that
115  * sets the <code>nextFocusableComponent</code> property of the target
116  * object to the value of the "source" property of the event.
117  *
118  * <blockquote>
119  *<pre>
120  *EventHandler.create(ActionListener.class, target, "nextFocusableComponent", "source")
121  *</pre>
122  * </blockquote>
123  *
124  * This would correspond to the following inner class implementation:
125  *
126  * <blockquote>
127  *<pre>
128 //Equivalent code using an inner class instead of EventHandler.
129  *new ActionListener() {
130  * public void actionPerformed(ActionEvent e) {
131  * button.setNextFocusableComponent((Component)e.getSource());
132  * }
133  *}
134  *</pre>
135  * </blockquote>
136  *
137  * Probably the most common use of <code>EventHandler</code>
138  * is to extract a property value from the
139  * <em>source</em> of the event object and set this value as
140  * the value of a property of the target object.
141  * In the following example we create an <code>ActionListener</code> that
142  * sets the "label" property of the target
143  * object to the value of the "text" property of the
144  * source (the value of the "source" property) of the event.
145  *
146  * <blockquote>
147  *<pre>
148  *EventHandler.create(ActionListener.class, button, "label", "source.text")
149  *</pre>
150  * </blockquote>
151  *
152  * This would correspond to the following inner class implementation:
153  *
154  * <blockquote>
155  *<pre>
156 //Equivalent code using an inner class instead of EventHandler.
157  *new ActionListener {
158  * public void actionPerformed(ActionEvent e) {
159  * button.setLabel(((JTextField)e.getSource()).getText());
160  * }
161  *}
162  *</pre>
163  * </blockquote>
164  *
165  * The event property may be be "qualified" with an arbitrary number
166  * of property prefixes delimited with the "." character. The "qualifying"
167  * names that appear before the "." characters are taken as the names of
168  * properties that should be applied, left-most first, to
169  * the event object.
170  * <p>
171  * For example, the following action listener
172  *
173  * <blockquote>
174  *<pre>
175  *EventHandler.create(ActionListener.class, target, "a", "b.c.d")
176  *</pre>
177  * </blockquote>
178  *
179  * might be written as the following inner class
180  * (assuming all the properties had canonical getter methods and
181  * returned the appropriate types):
182  *
183  * <blockquote>
184  *<pre>
185 //Equivalent code using an inner class instead of EventHandler.
186  *new ActionListener {
187  * public void actionPerformed(ActionEvent e) {
188  * target.setA(e.getB().getC().isD());
189  * }
190  *}
191  *</pre>
192  * </blockquote>
193  *
194  * @see java.lang.reflect.Proxy
195  * @see java.util.EventObject
196  *
197  * @since 1.4
198  *
199  * @author Mark Davidson
200  * @author Philip Milne
201  * @author Hans Muller
202  *
203  * @version 1.2 10/24/00
204  */

205 public class EventHandler implements InvocationHandler JavaDoc {
206     private static Object JavaDoc[] empty = new Object JavaDoc[]{};
207     
208     private Object JavaDoc target;
209     private Method JavaDoc targetMethod;
210     private String JavaDoc action;
211     private String JavaDoc eventPropertyName;
212     private String JavaDoc listenerMethodName;
213     private AccessControlContext JavaDoc acc;
214     
215     /**
216      * Creates a new <code>EventHandler</code> object;
217      * you generally use one of the <code>create</code> methods
218      * instead of invoking this constructor directly.
219      *
220      * @param target the object that will perform the action
221      * @param action the (possibly qualified) name of a writable property or method on the target
222      * @param eventPropertyName the (possibly qualified) name of a readable property of the incoming event
223      * @param listenerMethodName the name of the method in the listener interface that should trigger the action
224      *
225      * @see EventHandler
226      * @see #create(Class, Object, String, String, String)
227      * @see #getTarget
228      * @see #getAction
229      * @see #getEventPropertyName
230      * @see #getListenerMethodName
231      */

232     public EventHandler(Object JavaDoc target, String JavaDoc action, String JavaDoc eventPropertyName, String JavaDoc listenerMethodName) {
233     this.acc = AccessController.getContext();
234         this.target = target;
235         this.action = action;
236         this.eventPropertyName = eventPropertyName;
237         this.listenerMethodName = listenerMethodName;
238     }
239     
240     /**
241      * Returns the object to which this event handler will send a message.
242      *
243      * @return the target of this event handler
244      * @see #EventHandler(Object, String, String, String)
245      */

246     public Object JavaDoc getTarget() {
247         return target;
248     }
249     
250     /**
251      * Returns the name of the target's writable property
252      * that this event handler will set,
253      * or the name of the method that this event handler
254      * will invoke on the target.
255      *
256      * @return the action of this event handler
257      * @see #EventHandler(Object, String, String, String)
258      */

259     public String JavaDoc getAction() {
260         return action;
261     }
262     
263     /**
264      * Returns the property of the event that should be
265      * used in the action applied to the target.
266      *
267      * @return the property of the event
268      *
269      * @see #EventHandler(Object, String, String, String)
270      */

271     public String JavaDoc getEventPropertyName() {
272         return eventPropertyName;
273     }
274     
275     /**
276      * Returns the name of the method that will trigger the action.
277      * A return value of <code>null</code> signifies that all methods in the
278      * listener interface trigger the action.
279      *
280      * @return the name of the method that will trigger the action
281      *
282      * @see #EventHandler(Object, String, String, String)
283      */

284     public String JavaDoc getListenerMethodName() {
285         return listenerMethodName;
286     }
287     
288     private Object JavaDoc applyGetters(Object JavaDoc target, String JavaDoc getters) {
289         if (getters == null || getters.equals("")) {
290             return target;
291         }
292         int firstDot = getters.indexOf('.');
293         if (firstDot == -1) {
294             firstDot = getters.length();
295         }
296         String JavaDoc first = getters.substring(0, firstDot);
297         String JavaDoc rest = getters.substring(Math.min(firstDot + 1, getters.length()));
298         
299         try {
300             Method JavaDoc getter = ReflectionUtils.getMethod(target.getClass(),
301                       "get" + NameGenerator.capitalize(first),
302                       new Class JavaDoc[]{});
303             if (getter == null) {
304                 getter = ReflectionUtils.getMethod(target.getClass(),
305                    "is" + NameGenerator.capitalize(first),
306                    new Class JavaDoc[]{});
307             }
308             if (getter == null) {
309                 getter = ReflectionUtils.getMethod(target.getClass(), first, new Class JavaDoc[]{});
310             }
311             if (getter == null) {
312         throw new RuntimeException JavaDoc("No method called: " + first +
313                        " defined on " + target);
314             }
315             Object JavaDoc newTarget = MethodUtil.invoke(getter, target, new Object JavaDoc[]{});
316             return applyGetters(newTarget, rest);
317         }
318         catch (Throwable JavaDoc e) {
319             throw new RuntimeException JavaDoc("Failed to call method: " + first +
320                        " on " + target, e);
321         }
322     }
323     
324     /**
325      * Extract the appropriate property value from the event and
326      * pass it to the action associated with
327      * this <code>EventHandler</code>.
328      *
329      * @param proxy the proxy object
330      * @param method the method in the listener interface
331      * @return the result of applying the action to the target
332      *
333      * @see EventHandler
334      */

335     public Object JavaDoc invoke(final Object JavaDoc proxy, final Method JavaDoc method, final Object JavaDoc[] arguments) {
336     return AccessController.doPrivileged(new PrivilegedAction JavaDoc() {
337         public Object JavaDoc run() {
338             return invokeInternal(proxy, method, arguments);
339         }
340     }, acc);
341     }
342
343     private Object JavaDoc invokeInternal(Object JavaDoc proxy, Method JavaDoc method, Object JavaDoc[] arguments) {
344         String JavaDoc methodName = method.getName();
345         if (method.getDeclaringClass() == Object JavaDoc.class) {
346             // Handle the Object public methods.
347
if (methodName.equals("hashCode")) {
348                 return new Integer JavaDoc(System.identityHashCode(proxy));
349             } else if (methodName.equals("equals")) {
350                 return (proxy == arguments[0] ? Boolean.TRUE : Boolean.FALSE);
351             } else if (methodName.equals("toString")) {
352                 return proxy.getClass().getName() + '@' + Integer.toHexString(proxy.hashCode());
353             }
354         }
355
356         if (listenerMethodName == null || listenerMethodName.equals(methodName)) {
357             Class JavaDoc[] argTypes = null;
358             Object JavaDoc[] newArgs = null;
359
360             if (eventPropertyName == null) { // Nullary method.
361
newArgs = new Object JavaDoc[]{};
362                 argTypes = new Class JavaDoc[]{};
363             }
364             else {
365                 Object JavaDoc input = applyGetters(arguments[0], getEventPropertyName());
366                 newArgs = new Object JavaDoc[]{input};
367                 argTypes = new Class JavaDoc[]{input.getClass()};
368             }
369             try {
370                 if (targetMethod == null) {
371                     targetMethod = ReflectionUtils.getMethod(target.getClass(),
372                                  action, argTypes);
373                 }
374                 if (targetMethod == null) {
375                     targetMethod = ReflectionUtils.getMethod(target.getClass(),
376                  "set" + NameGenerator.capitalize(action), argTypes);
377                 }
378                 if (targetMethod == null) {
379             throw new RuntimeException JavaDoc("No method called: " +
380                            action + " on class " +
381                            target.getClass() + " with argument "
382                            + argTypes[0]);
383                 }
384                 return MethodUtil.invoke(targetMethod, target, newArgs);
385             }
386             catch (IllegalAccessException JavaDoc ex) {
387                 throw new RuntimeException JavaDoc(ex);
388             }
389             catch (InvocationTargetException JavaDoc ex) {
390                 throw new RuntimeException JavaDoc(ex.getTargetException());
391         }
392         }
393         return null;
394     }
395
396     /**
397      * Creates an implementation of <code>listenerInterface</code> in which
398      * <em>all</em> of the methods in the listener interface apply
399      * the handler's <code>action</code> to the <code>target</code>. This
400      * method is implemented by calling the other, more general,
401      * implementation of the <code>create</code> method with both
402      * the <code>eventPropertyName</code> and the <code>listenerMethodName</code>
403      * taking the value <code>null</code>.
404      * <p>
405      * To create an <code>ActionListener</code> that shows a
406      * <code>JDialog</code> with <code>dialog.show()</code>,
407      * one can write:
408      *
409      *<blockquote>
410      *<pre>
411      *EventHandler.create(ActionListener.class, dialog, "show")
412      *</pre>
413      *</blockquote>
414      *
415      * <p>
416      * @param listenerInterface the listener interface to create a proxy for
417      * @param target the object that will perform the action
418      * @param action the name of a writable property or method on the target
419      *
420      * @return an object that implements <code>listenerInterface</code>
421      *
422      * @see #create(Class, Object, String, String)
423      */

424     public static <T> T create(Class JavaDoc<T> listenerInterface,
425                    Object JavaDoc target, String JavaDoc action)
426     {
427         return create(listenerInterface, target, action, null, null);
428     }
429
430     /**
431      * Creates an implementation of <code>listenerInterface</code> in which
432      * <em>all</em> of the methods pass the value of the event
433      * expression, <code>eventPropertyName</code>, to the final method in the
434      * statement, <code>action</code>, which is applied to the <code>target</code>.
435      * This method is implemented by calling the
436      * more general, implementation of the <code>create</code> method with
437      * the <code>listenerMethodName</code> taking the value <code>null</code>.
438      * <p>
439      * To create an <code>ActionListener</code> that sets the
440      * the text of a <code>JLabel</code> to the text value of
441      * the <code>JTextField</code> source of the incoming event,
442      * you can use the following code:
443      *
444      *<blockquote>
445      *<pre>
446      *EventHandler.create(ActionListener.class, label, "text", "source.text");
447      *</pre>
448      *</blockquote>
449      *
450      * This is equivalent to the following code:
451      *<blockquote>
452      *<pre>
453 //Equivalent code using an inner class instead of EventHandler.
454      *label.setText((JTextField(event.getSource())).getText())
455      *</pre>
456      *</blockquote>
457      *
458      * @param listenerInterface the listener interface to create a proxy for
459      * @param target the object that will perform the action
460      * @param action the name of a writable property or method on the target
461      * @param eventPropertyName the (possibly qualified) name of a readable property of the incoming event
462      *
463      * @return an object that implements <code>listenerInterface</code>
464      *
465      * @see #create(Class, Object, String, String, String)
466      */

467     public static <T> T create(Class JavaDoc<T> listenerInterface,
468                    Object JavaDoc target, String JavaDoc action,
469                    String JavaDoc eventPropertyName)
470     {
471         return create(listenerInterface, target, action, eventPropertyName, null);
472     }
473
474     /**
475      * Creates an implementation of <code>listenerInterface</code> in which
476      * the method named <code>listenerMethodName</code>
477      * passes the value of the event expression, <code>eventPropertyName</code>,
478      * to the final method in the statement, <code>action</code>, which
479      * is applied to the <code>target</code>. All of the other listener
480      * methods do nothing.
481      * <p>
482      * If the <code>eventPropertyName</code> is <code>null</code> the
483      * implementation calls a method with the name specified
484      * in <code>action</code> that takes an <code>EventObject</code>
485      * or a no-argument method with the same name if a method
486      * accepting an <code>EventObject</code> is not defined.
487      * <p>
488      * If the <code>listenerMethodName</code> is <code>null</code>
489      * <em>all</em> methods in the interface trigger the <code>action</code> to be
490      * executed on the <code>target</code>.
491      * <p>
492      * For example, to create a <code>MouseListener</code> that sets the target
493      * object's <code>origin</code> property to the incoming <code>MouseEvent</code>'s
494      * location (that's the value of <code>mouseEvent.getPoint()</code>) each
495      * time a mouse button is pressed, one would write:
496      *<blockquote>
497      *<pre>
498      *EventHandler.create(MouseListener.class, "mousePressed", target, "origin", "point");
499      *</pre>
500      *</blockquote>
501      *
502      * This is comparable to writing a <code>MouseListener</code> in which all
503      * of the methods except <code>mousePressed</code> are no-ops:
504      *
505      *<blockquote>
506      *<pre>
507 //Equivalent code using an inner class instead of EventHandler.
508      *new MouseAdapter() {
509      * public void mousePressed(MouseEvent e) {
510      * target.setOrigin(e.getPoint());
511      * }
512      *}
513      * </pre>
514      *</blockquote>
515      *
516      * @param listenerInterface the listener interface to create a proxy for
517      * @param target the object that will perform the action
518      * @param action the name of a writable property or method on the target
519      * @param eventPropertyName the (possibly qualified) name of a readable property of the incoming event
520      * @param listenerMethodName the name of the method in the listener interface that should trigger the action
521      *
522      * @return an object that implements <code>listenerInterface</code>
523      *
524      * @see EventHandler
525      */

526     public static <T> T create(Class JavaDoc<T> listenerInterface,
527                    Object JavaDoc target, String JavaDoc action,
528                    String JavaDoc eventPropertyName,
529                    String JavaDoc listenerMethodName)
530     {
531         return (T)Proxy.newProxyInstance(target.getClass().getClassLoader(),
532                      new Class JavaDoc[] {listenerInterface},
533                      new EventHandler JavaDoc(target, action,
534                               eventPropertyName,
535                               listenerMethodName));
536     }
537 }
538
539
540
541
542
543
Popular Tags