19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 package java.beans;
26
27 import java.lang.reflect.InvocationHandler;
28 import java.lang.reflect.InvocationTargetException;
29 import java.lang.reflect.Proxy;
30 import java.lang.reflect.Method;
31 import java.security.AccessControlContext;
32 import java.security.AccessController;
33 import java.security.PrivilegedAction;
34
35 import sun.reflect.misc.MethodUtil;
36 import sun.reflect.misc.ReflectUtil;
37
38 /**
39 * The <code>EventHandler</code> class provides
40 * support for dynamically generating event listeners whose methods
41 * execute a simple statement involving an incoming event object
42 * and a target object.
43 * <p>
44 * The <code>EventHandler</code> class is intended to be used by interactive tools, such as
45 * application builders, that allow developers to make connections between
46 * beans. Typically connections are made from a user interface bean
47 * (the event <em>source</em>)
48 * to an application logic bean (the <em>target</em>). The most effective
49 * connections of this kind isolate the application logic from the user
50 * interface. For example, the <code>EventHandler</code> for a
51 * connection from a <code>JCheckBox</code> to a method
52 * that accepts a boolean value can deal with extracting the state
53 * of the check box and passing it directly to the method so that
54 * the method is isolated from the user interface layer.
55 * <p>
56 * Inner classes are another, more general way to handle events from
57 * user interfaces. The <code>EventHandler</code> class
58 * handles only a subset of what is possible using inner
59 * classes. However, <code>EventHandler</code> works better
60 * with the long-term persistence scheme than inner classes.
61 * Also, using <code>EventHandler</code> in large applications in
62 * which the same interface is implemented many times can
63 * reduce the disk and memory footprint of the application.
64 * <p>
65 * The reason that listeners created with <code>EventHandler</code>
66 * have such a small
67 * footprint is that the <code>Proxy</code> class, on which
68 * the <code>EventHandler</code> relies, shares implementations
69 * of identical
70 * interfaces. For example, if you use
71 * the <code>EventHandler</code> <code>create</code> methods to make
72 * all the <code>ActionListener</code>s in an application,
73 * all the action listeners will be instances of a single class
74 * (one created by the <code>Proxy</code> class).
75 * In general, listeners based on
76 * the <code>Proxy</code> class require one listener class
77 * to be created per <em>listener type</em> (interface),
78 * whereas the inner class
79 * approach requires one class to be created per <em>listener</em>
80 * (object that implements the interface).
81 *
82 * <p>
83 * You don't generally deal directly with <code>EventHandler</code>
84 * instances.
85 * Instead, you use one of the <code>EventHandler</code>
86 * <code>create</code> methods to create
87 * an object that implements a given listener interface.
88 * This listener object uses an <code>EventHandler</code> object
89 * behind the scenes to encapsulate information about the
90 * event, the object to be sent a message when the event occurs,
91 * the message (method) to be sent, and any argument
92 * to the method.
93 * The following section gives examples of how to create listener
94 * objects using the <code>create</code> methods.
95 *
96 * <h2>Examples of Using EventHandler</h2>
97 *
98 * The simplest use of <code>EventHandler</code> is to install
99 * a listener that calls a method on the target object with no arguments.
100 * In the following example we create an <code>ActionListener</code>
101 * that invokes the <code>toFront</code> method on an instance
102 * of <code>javax.swing.JFrame</code>.
103 *
104 * <blockquote>
105 *<pre>
106 *myButton.addActionListener(
107 * (ActionListener)EventHandler.create(ActionListener.class, frame, "toFront"));
108 *</pre>
109 * </blockquote>
110 *
111 * When <code>myButton</code> is pressed, the statement
112 * <code>frame.toFront()</code> will be executed. One could get
113 * the same effect, with some additional compile-time type safety,
114 * by defining a new implementation of the <code>ActionListener</code>
115 * interface and adding an instance of it to the button:
116 *
117 * <blockquote>
118 *<pre>
119 //Equivalent code using an inner class instead of EventHandler.
120 *myButton.addActionListener(new ActionListener() {
121 * public void actionPerformed(ActionEvent e) {
122 * frame.toFront();
123 * }
124 *});
125 *</pre>
126 * </blockquote>
127 *
128 * The next simplest use of <code>EventHandler</code> is
129 * to extract a property value from the first argument
130 * of the method in the listener interface (typically an event object)
131 * and use it to set the value of a property in the target object.
132 * In the following example we create an <code>ActionListener</code> that
133 * sets the <code>nextFocusableComponent</code> property of the target
134 * (myButton) object to the value of the "source" property of the event.
135 *
136 * <blockquote>
137 *<pre>
138 *EventHandler.create(ActionListener.class, myButton, "nextFocusableComponent", "source")
139 *</pre>
140 * </blockquote>
141 *
142 * This would correspond to the following inner class implementation:
143 *
144 * <blockquote>
145 *<pre>
146 //Equivalent code using an inner class instead of EventHandler.
147 *new ActionListener() {
148 * public void actionPerformed(ActionEvent e) {
149 * myButton.setNextFocusableComponent((Component)e.getSource());
150 * }
151 *}
152 *</pre>
153 * </blockquote>
154 *
155 * It's also possible to create an <code>EventHandler</code> that
156 * just passes the incoming event object to the target's action.
157 * If the fourth <code>EventHandler.create</code> argument is
158 * an empty string, then the event is just passed along:
159 *
160 * <blockquote>
161 *<pre>
162 *EventHandler.create(ActionListener.class, target, "doActionEvent", "")
163 *</pre>
164 * </blockquote>
165 *
166 * This would correspond to the following inner class implementation:
167 *
168 * <blockquote>
169 *<pre>
170 //Equivalent code using an inner class instead of EventHandler.
171 *new ActionListener() {
172 * public void actionPerformed(ActionEvent e) {
173 * target.doActionEvent(e);
174 * }
175 *}
176 *</pre>
177 * </blockquote>
178 *
179 * Probably the most common use of <code>EventHandler</code>
180 * is to extract a property value from the
181 * <em>source</em> of the event object and set this value as
182 * the value of a property of the target object.
183 * In the following example we create an <code>ActionListener</code> that
184 * sets the "label" property of the target
185 * object to the value of the "text" property of the
186 * source (the value of the "source" property) of the event.
187 *
188 * <blockquote>
189 *<pre>
190 *EventHandler.create(ActionListener.class, myButton, "label", "source.text")
191 *</pre>
192 * </blockquote>
193 *
194 * This would correspond to the following inner class implementation:
195 *
196 * <blockquote>
197 *<pre>
198 //Equivalent code using an inner class instead of EventHandler.
199 *new ActionListener {
200 * public void actionPerformed(ActionEvent e) {
201 * myButton.setLabel(((JTextField)e.getSource()).getText());
202 * }
203 *}
233 *</pre>
234 * </blockquote>
235 * The target property may also be "qualified" with an arbitrary number
236 * of property prefixs delimited with the "." character. For example, the
237 * following action listener:
238 * <pre>
239 * EventHandler.create(ActionListener.class, target, "a.b", "c.d")
240 * </pre>
241 * might be written as the following inner class
242 * (assuming all the properties had canonical getter methods and
243 * returned the appropriate types):
244 * <pre>
245 * //Equivalent code using an inner class instead of EventHandler.
246 * new ActionListener {
247 * public void actionPerformed(ActionEvent e) {
248 * target.getA().setB(e.getC().isD());
249 * }
250 *}
251 *</pre>
252 * <p>
253 * As <code>EventHandler</code> ultimately relies on reflection to invoke
254 * a method we recommend against targeting an overloaded method. For example,
255 * if the target is an instance of the class <code>MyTarget</code> which is
256 * defined as:
257 * <pre>
258 * public class MyTarget {
259 * public void doIt(String);
260 * public void doIt(Object);
261 * }
262 * </pre>
263 * Then the method <code>doIt</code> is overloaded. EventHandler will invoke
264 * the method that is appropriate based on the source. If the source is
265 * null, then either method is appropriate and the one that is invoked is
266 * undefined. For that reason we recommend against targeting overloaded
267 * methods.
268 *
269 * @see java.lang.reflect.Proxy
270 * @see java.util.EventObject
271 *
272 * @since 1.4
273 *
274 * @author Mark Davidson
275 * @author Philip Milne
276 * @author Hans Muller
277 *
278 */
279 public class EventHandler implements InvocationHandler {
280 private Object target;
281 private String action;
282 private final String eventPropertyName;
283 private final String listenerMethodName;
284 private final AccessControlContext acc = AccessController.getContext();
285
286 /**
287 * Creates a new <code>EventHandler</code> object;
288 * you generally use one of the <code>create</code> methods
289 * instead of invoking this constructor directly. Refer to
290 * {@link java.beans.EventHandler#create(Class, Object, String, String)
291 * the general version of create} for a complete description of
292 * the <code>eventPropertyName</code> and <code>listenerMethodName</code>
293 * parameter.
294 *
295 * @param target the object that will perform the action
296 * @param action the name of a (possibly qualified) property or method on
297 * the target
298 * @param eventPropertyName the (possibly qualified) name of a readable property of the incoming event
299 * @param listenerMethodName the name of the method in the listener interface that should trigger the action
300 *
301 * @throws NullPointerException if <code>target</code> is null
302 * @throws NullPointerException if <code>action</code> is null
303 *
304 * @see EventHandler
305 * @see #create(Class, Object, String, String, String)
306 * @see #getTarget
307 * @see #getAction
308 * @see #getEventPropertyName
309 * @see #getListenerMethodName
310 */
311 @ConstructorProperties({"target", "action", "eventPropertyName", "listenerMethodName"})
312 public EventHandler(Object target, String action, String eventPropertyName, String listenerMethodName) {
313 this.target = target;
314 this.action = action;
315 if (target == null) {
316 throw new NullPointerException("target must be non-null");
317 }
318 if (action == null) {
319 throw new NullPointerException("action must be non-null");
320 }
321 this.eventPropertyName = eventPropertyName;
322 this.listenerMethodName = listenerMethodName;
342 * @see #EventHandler(Object, String, String, String)
343 */
344 public String getAction() {
345 return action;
346 }
347
348 /**
349 * Returns the property of the event that should be
350 * used in the action applied to the target.
351 *
352 * @return the property of the event
353 *
354 * @see #EventHandler(Object, String, String, String)
355 */
356 public String getEventPropertyName() {
357 return eventPropertyName;
358 }
359
360 /**
361 * Returns the name of the method that will trigger the action.
362 * A return value of <code>null</code> signifies that all methods in the
363 * listener interface trigger the action.
364 *
365 * @return the name of the method that will trigger the action
366 *
367 * @see #EventHandler(Object, String, String, String)
368 */
369 public String getListenerMethodName() {
370 return listenerMethodName;
371 }
372
373 private Object applyGetters(Object target, String getters) {
374 if (getters == null || getters.equals("")) {
375 return target;
376 }
377 int firstDot = getters.indexOf('.');
378 if (firstDot == -1) {
379 firstDot = getters.length();
380 }
381 String first = getters.substring(0, firstDot);
382 String rest = getters.substring(Math.min(firstDot + 1, getters.length()));
395 if (getter == null) {
396 getter = Statement.getMethod(target.getClass(), first, new Class<?>[]{});
397 }
398 }
399 if (getter == null) {
400 throw new RuntimeException("No method called: " + first +
401 " defined on " + target);
402 }
403 Object newTarget = MethodUtil.invoke(getter, target, new Object[]{});
404 return applyGetters(newTarget, rest);
405 }
406 catch (Exception e) {
407 throw new RuntimeException("Failed to call method: " + first +
408 " on " + target, e);
409 }
410 }
411
412 /**
413 * Extract the appropriate property value from the event and
414 * pass it to the action associated with
415 * this <code>EventHandler</code>.
416 *
417 * @param proxy the proxy object
418 * @param method the method in the listener interface
419 * @return the result of applying the action to the target
420 *
421 * @see EventHandler
422 */
423 public Object invoke(final Object proxy, final Method method, final Object[] arguments) {
424 AccessControlContext acc = this.acc;
425 if ((acc == null) && (System.getSecurityManager() != null)) {
426 throw new SecurityException("AccessControlContext is not set");
427 }
428 return AccessController.doPrivileged(new PrivilegedAction<Object>() {
429 public Object run() {
430 return invokeInternal(proxy, method, arguments);
431 }
432 }, acc);
433 }
434
435 private Object invokeInternal(Object proxy, Method method, Object[] arguments) {
478 throw new RuntimeException(
479 "No method called " + action + " on " +
480 target.getClass() + argTypeString);
481 }
482 return MethodUtil.invoke(targetMethod, target, newArgs);
483 }
484 catch (IllegalAccessException ex) {
485 throw new RuntimeException(ex);
486 }
487 catch (InvocationTargetException ex) {
488 Throwable th = ex.getTargetException();
489 throw (th instanceof RuntimeException)
490 ? (RuntimeException) th
491 : new RuntimeException(th);
492 }
493 }
494 return null;
495 }
496
497 /**
498 * Creates an implementation of <code>listenerInterface</code> in which
499 * <em>all</em> of the methods in the listener interface apply
500 * the handler's <code>action</code> to the <code>target</code>. This
501 * method is implemented by calling the other, more general,
502 * implementation of the <code>create</code> method with both
503 * the <code>eventPropertyName</code> and the <code>listenerMethodName</code>
504 * taking the value <code>null</code>. Refer to
505 * {@link java.beans.EventHandler#create(Class, Object, String, String)
506 * the general version of create} for a complete description of
507 * the <code>action</code> parameter.
508 * <p>
509 * To create an <code>ActionListener</code> that shows a
510 * <code>JDialog</code> with <code>dialog.show()</code>,
511 * one can write:
512 *
513 *<blockquote>
514 *<pre>
515 *EventHandler.create(ActionListener.class, dialog, "show")
516 *</pre>
517 *</blockquote>
518 *
519 * @param <T> the type to create
520 * @param listenerInterface the listener interface to create a proxy for
521 * @param target the object that will perform the action
522 * @param action the name of a (possibly qualified) property or method on
523 * the target
524 * @return an object that implements <code>listenerInterface</code>
525 *
526 * @throws NullPointerException if <code>listenerInterface</code> is null
527 * @throws NullPointerException if <code>target</code> is null
528 * @throws NullPointerException if <code>action</code> is null
529 *
530 * @see #create(Class, Object, String, String)
531 */
532 public static <T> T create(Class<T> listenerInterface,
533 Object target, String action)
534 {
535 return create(listenerInterface, target, action, null, null);
536 }
537
538 /**
539 /**
540 * Creates an implementation of <code>listenerInterface</code> in which
541 * <em>all</em> of the methods pass the value of the event
542 * expression, <code>eventPropertyName</code>, to the final method in the
543 * statement, <code>action</code>, which is applied to the <code>target</code>.
544 * This method is implemented by calling the
545 * more general, implementation of the <code>create</code> method with
546 * the <code>listenerMethodName</code> taking the value <code>null</code>.
547 * Refer to
548 * {@link java.beans.EventHandler#create(Class, Object, String, String)
549 * the general version of create} for a complete description of
550 * the <code>action</code> and <code>eventPropertyName</code> parameters.
551 * <p>
552 * To create an <code>ActionListener</code> that sets the
553 * the text of a <code>JLabel</code> to the text value of
554 * the <code>JTextField</code> source of the incoming event,
555 * you can use the following code:
556 *
557 *<blockquote>
558 *<pre>
559 *EventHandler.create(ActionListener.class, label, "text", "source.text");
560 *</pre>
561 *</blockquote>
562 *
563 * This is equivalent to the following code:
564 *<blockquote>
565 *<pre>
566 //Equivalent code using an inner class instead of EventHandler.
567 *new ActionListener() {
568 * public void actionPerformed(ActionEvent event) {
569 * label.setText(((JTextField)(event.getSource())).getText());
570 * }
571 *};
572 *</pre>
573 *</blockquote>
574 *
575 * @param <T> the type to create
576 * @param listenerInterface the listener interface to create a proxy for
577 * @param target the object that will perform the action
578 * @param action the name of a (possibly qualified) property or method on
579 * the target
580 * @param eventPropertyName the (possibly qualified) name of a readable property of the incoming event
581 *
582 * @return an object that implements <code>listenerInterface</code>
583 *
584 * @throws NullPointerException if <code>listenerInterface</code> is null
585 * @throws NullPointerException if <code>target</code> is null
586 * @throws NullPointerException if <code>action</code> is null
587 *
588 * @see #create(Class, Object, String, String, String)
589 */
590 public static <T> T create(Class<T> listenerInterface,
591 Object target, String action,
592 String eventPropertyName)
593 {
594 return create(listenerInterface, target, action, eventPropertyName, null);
595 }
596
597 /**
598 * Creates an implementation of <code>listenerInterface</code> in which
599 * the method named <code>listenerMethodName</code>
600 * passes the value of the event expression, <code>eventPropertyName</code>,
601 * to the final method in the statement, <code>action</code>, which
602 * is applied to the <code>target</code>. All of the other listener
603 * methods do nothing.
604 * <p>
605 * The <code>eventPropertyName</code> string is used to extract a value
606 * from the incoming event object that is passed to the target
607 * method. The common case is the target method takes no arguments, in
608 * which case a value of null should be used for the
609 * <code>eventPropertyName</code>. Alternatively if you want
610 * the incoming event object passed directly to the target method use
611 * the empty string.
612 * The format of the <code>eventPropertyName</code> string is a sequence of
613 * methods or properties where each method or
614 * property is applied to the value returned by the preceding method
615 * starting from the incoming event object.
616 * The syntax is: <code>propertyName{.propertyName}*</code>
617 * where <code>propertyName</code> matches a method or
618 * property. For example, to extract the <code>point</code>
619 * property from a <code>MouseEvent</code>, you could use either
620 * <code>"point"</code> or <code>"getPoint"</code> as the
621 * <code>eventPropertyName</code>. To extract the "text" property from
622 * a <code>MouseEvent</code> with a <code>JLabel</code> source use any
623 * of the following as <code>eventPropertyName</code>:
624 * <code>"source.text"</code>,
625 * <code>"getSource.text"</code> <code>"getSource.getText"</code> or
626 * <code>"source.getText"</code>. If a method can not be found, or an
627 * exception is generated as part of invoking a method a
628 * <code>RuntimeException</code> will be thrown at dispatch time. For
629 * example, if the incoming event object is null, and
630 * <code>eventPropertyName</code> is non-null and not empty, a
631 * <code>RuntimeException</code> will be thrown.
632 * <p>
633 * The <code>action</code> argument is of the same format as the
634 * <code>eventPropertyName</code> argument where the last property name
635 * identifies either a method name or writable property.
636 * <p>
637 * If the <code>listenerMethodName</code> is <code>null</code>
638 * <em>all</em> methods in the interface trigger the <code>action</code> to be
639 * executed on the <code>target</code>.
640 * <p>
641 * For example, to create a <code>MouseListener</code> that sets the target
642 * object's <code>origin</code> property to the incoming <code>MouseEvent</code>'s
643 * location (that's the value of <code>mouseEvent.getPoint()</code>) each
644 * time a mouse button is pressed, one would write:
645 *<blockquote>
646 *<pre>
647 *EventHandler.create(MouseListener.class, target, "origin", "point", "mousePressed");
648 *</pre>
649 *</blockquote>
650 *
651 * This is comparable to writing a <code>MouseListener</code> in which all
652 * of the methods except <code>mousePressed</code> are no-ops:
653 *
654 *<blockquote>
655 *<pre>
656 //Equivalent code using an inner class instead of EventHandler.
657 *new MouseAdapter() {
658 * public void mousePressed(MouseEvent e) {
659 * target.setOrigin(e.getPoint());
660 * }
661 *};
662 * </pre>
663 *</blockquote>
664 *
665 * @param <T> the type to create
666 * @param listenerInterface the listener interface to create a proxy for
667 * @param target the object that will perform the action
668 * @param action the name of a (possibly qualified) property or method on
669 * the target
670 * @param eventPropertyName the (possibly qualified) name of a readable property of the incoming event
671 * @param listenerMethodName the name of the method in the listener interface that should trigger the action
672 *
673 * @return an object that implements <code>listenerInterface</code>
674 *
675 * @throws NullPointerException if <code>listenerInterface</code> is null
676 * @throws NullPointerException if <code>target</code> is null
677 * @throws NullPointerException if <code>action</code> is null
678 *
679 * @see EventHandler
680 */
681 public static <T> T create(Class<T> listenerInterface,
682 Object target, String action,
683 String eventPropertyName,
684 String listenerMethodName)
685 {
686 // Create this first to verify target/action are non-null
687 final EventHandler handler = new EventHandler(target, action,
688 eventPropertyName,
689 listenerMethodName);
690 if (listenerInterface == null) {
691 throw new NullPointerException(
692 "listenerInterface must be non-null");
693 }
694 final ClassLoader loader = getClassLoader(listenerInterface);
695 final Class<?>[] interfaces = {listenerInterface};
696 return AccessController.doPrivileged(new PrivilegedAction<T>() {
697 @SuppressWarnings("unchecked")
|
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 package java.beans;
26
27 import java.lang.reflect.InvocationHandler;
28 import java.lang.reflect.InvocationTargetException;
29 import java.lang.reflect.Proxy;
30 import java.lang.reflect.Method;
31 import java.security.AccessControlContext;
32 import java.security.AccessController;
33 import java.security.PrivilegedAction;
34
35 import sun.reflect.misc.MethodUtil;
36 import sun.reflect.misc.ReflectUtil;
37
38 /**
39 * The {@code EventHandler} class provides
40 * support for dynamically generating event listeners whose methods
41 * execute a simple statement involving an incoming event object
42 * and a target object.
43 * <p>
44 * The {@code EventHandler} class is intended to be used by interactive tools, such as
45 * application builders, that allow developers to make connections between
46 * beans. Typically connections are made from a user interface bean
47 * (the event <em>source</em>)
48 * to an application logic bean (the <em>target</em>). The most effective
49 * connections of this kind isolate the application logic from the user
50 * interface. For example, the {@code EventHandler} for a
51 * connection from a {@code JCheckBox} to a method
52 * that accepts a boolean value can deal with extracting the state
53 * of the check box and passing it directly to the method so that
54 * the method is isolated from the user interface layer.
55 * <p>
56 * Inner classes are another, more general way to handle events from
57 * user interfaces. The {@code EventHandler} class
58 * handles only a subset of what is possible using inner
59 * classes. However, {@code EventHandler} works better
60 * with the long-term persistence scheme than inner classes.
61 * Also, using {@code EventHandler} in large applications in
62 * which the same interface is implemented many times can
63 * reduce the disk and memory footprint of the application.
64 * <p>
65 * The reason that listeners created with {@code EventHandler}
66 * have such a small
67 * footprint is that the {@code Proxy} class, on which
68 * the {@code EventHandler} relies, shares implementations
69 * of identical
70 * interfaces. For example, if you use
71 * the {@code EventHandler create} methods to make
72 * all the {@code ActionListener}s in an application,
73 * all the action listeners will be instances of a single class
74 * (one created by the {@code Proxy} class).
75 * In general, listeners based on
76 * the {@code Proxy} class require one listener class
77 * to be created per <em>listener type</em> (interface),
78 * whereas the inner class
79 * approach requires one class to be created per <em>listener</em>
80 * (object that implements the interface).
81 *
82 * <p>
83 * You don't generally deal directly with {@code EventHandler}
84 * instances.
85 * Instead, you use one of the {@code EventHandler}
86 * {@code create} methods to create
87 * an object that implements a given listener interface.
88 * This listener object uses an {@code EventHandler} object
89 * behind the scenes to encapsulate information about the
90 * event, the object to be sent a message when the event occurs,
91 * the message (method) to be sent, and any argument
92 * to the method.
93 * The following section gives examples of how to create listener
94 * objects using the {@code create} methods.
95 *
96 * <h2>Examples of Using EventHandler</h2>
97 *
98 * The simplest use of {@code EventHandler} is to install
99 * a listener that calls a method on the target object with no arguments.
100 * In the following example we create an {@code ActionListener}
101 * that invokes the {@code toFront} method on an instance
102 * of {@code javax.swing.JFrame}.
103 *
104 * <blockquote>
105 *<pre>
106 *myButton.addActionListener(
107 * (ActionListener)EventHandler.create(ActionListener.class, frame, "toFront"));
108 *</pre>
109 * </blockquote>
110 *
111 * When {@code myButton} is pressed, the statement
112 * {@code frame.toFront()} will be executed. One could get
113 * the same effect, with some additional compile-time type safety,
114 * by defining a new implementation of the {@code ActionListener}
115 * interface and adding an instance of it to the button:
116 *
117 * <blockquote>
118 *<pre>
119 //Equivalent code using an inner class instead of EventHandler.
120 *myButton.addActionListener(new ActionListener() {
121 * public void actionPerformed(ActionEvent e) {
122 * frame.toFront();
123 * }
124 *});
125 *</pre>
126 * </blockquote>
127 *
128 * The next simplest use of {@code EventHandler} is
129 * to extract a property value from the first argument
130 * of the method in the listener interface (typically an event object)
131 * and use it to set the value of a property in the target object.
132 * In the following example we create an {@code ActionListener} that
133 * sets the {@code nextFocusableComponent} property of the target
134 * (myButton) object to the value of the "source" property of the event.
135 *
136 * <blockquote>
137 *<pre>
138 *EventHandler.create(ActionListener.class, myButton, "nextFocusableComponent", "source")
139 *</pre>
140 * </blockquote>
141 *
142 * This would correspond to the following inner class implementation:
143 *
144 * <blockquote>
145 *<pre>
146 //Equivalent code using an inner class instead of EventHandler.
147 *new ActionListener() {
148 * public void actionPerformed(ActionEvent e) {
149 * myButton.setNextFocusableComponent((Component)e.getSource());
150 * }
151 *}
152 *</pre>
153 * </blockquote>
154 *
155 * It's also possible to create an {@code EventHandler} that
156 * just passes the incoming event object to the target's action.
157 * If the fourth {@code EventHandler.create} argument is
158 * an empty string, then the event is just passed along:
159 *
160 * <blockquote>
161 *<pre>
162 *EventHandler.create(ActionListener.class, target, "doActionEvent", "")
163 *</pre>
164 * </blockquote>
165 *
166 * This would correspond to the following inner class implementation:
167 *
168 * <blockquote>
169 *<pre>
170 //Equivalent code using an inner class instead of EventHandler.
171 *new ActionListener() {
172 * public void actionPerformed(ActionEvent e) {
173 * target.doActionEvent(e);
174 * }
175 *}
176 *</pre>
177 * </blockquote>
178 *
179 * Probably the most common use of {@code EventHandler}
180 * is to extract a property value from the
181 * <em>source</em> of the event object and set this value as
182 * the value of a property of the target object.
183 * In the following example we create an {@code ActionListener} that
184 * sets the "label" property of the target
185 * object to the value of the "text" property of the
186 * source (the value of the "source" property) of the event.
187 *
188 * <blockquote>
189 *<pre>
190 *EventHandler.create(ActionListener.class, myButton, "label", "source.text")
191 *</pre>
192 * </blockquote>
193 *
194 * This would correspond to the following inner class implementation:
195 *
196 * <blockquote>
197 *<pre>
198 //Equivalent code using an inner class instead of EventHandler.
199 *new ActionListener {
200 * public void actionPerformed(ActionEvent e) {
201 * myButton.setLabel(((JTextField)e.getSource()).getText());
202 * }
203 *}
233 *</pre>
234 * </blockquote>
235 * The target property may also be "qualified" with an arbitrary number
236 * of property prefixs delimited with the "." character. For example, the
237 * following action listener:
238 * <pre>
239 * EventHandler.create(ActionListener.class, target, "a.b", "c.d")
240 * </pre>
241 * might be written as the following inner class
242 * (assuming all the properties had canonical getter methods and
243 * returned the appropriate types):
244 * <pre>
245 * //Equivalent code using an inner class instead of EventHandler.
246 * new ActionListener {
247 * public void actionPerformed(ActionEvent e) {
248 * target.getA().setB(e.getC().isD());
249 * }
250 *}
251 *</pre>
252 * <p>
253 * As {@code EventHandler} ultimately relies on reflection to invoke
254 * a method we recommend against targeting an overloaded method. For example,
255 * if the target is an instance of the class {@code MyTarget} which is
256 * defined as:
257 * <pre>
258 * public class MyTarget {
259 * public void doIt(String);
260 * public void doIt(Object);
261 * }
262 * </pre>
263 * Then the method {@code doIt} is overloaded. EventHandler will invoke
264 * the method that is appropriate based on the source. If the source is
265 * null, then either method is appropriate and the one that is invoked is
266 * undefined. For that reason we recommend against targeting overloaded
267 * methods.
268 *
269 * @see java.lang.reflect.Proxy
270 * @see java.util.EventObject
271 *
272 * @since 1.4
273 *
274 * @author Mark Davidson
275 * @author Philip Milne
276 * @author Hans Muller
277 *
278 */
279 public class EventHandler implements InvocationHandler {
280 private Object target;
281 private String action;
282 private final String eventPropertyName;
283 private final String listenerMethodName;
284 private final AccessControlContext acc = AccessController.getContext();
285
286 /**
287 * Creates a new {@code EventHandler} object;
288 * you generally use one of the {@code create} methods
289 * instead of invoking this constructor directly. Refer to
290 * {@link java.beans.EventHandler#create(Class, Object, String, String)
291 * the general version of create} for a complete description of
292 * the {@code eventPropertyName} and {@code listenerMethodName}
293 * parameter.
294 *
295 * @param target the object that will perform the action
296 * @param action the name of a (possibly qualified) property or method on
297 * the target
298 * @param eventPropertyName the (possibly qualified) name of a readable property of the incoming event
299 * @param listenerMethodName the name of the method in the listener interface that should trigger the action
300 *
301 * @throws NullPointerException if {@code target} is null
302 * @throws NullPointerException if {@code action} is null
303 *
304 * @see EventHandler
305 * @see #create(Class, Object, String, String, String)
306 * @see #getTarget
307 * @see #getAction
308 * @see #getEventPropertyName
309 * @see #getListenerMethodName
310 */
311 @ConstructorProperties({"target", "action", "eventPropertyName", "listenerMethodName"})
312 public EventHandler(Object target, String action, String eventPropertyName, String listenerMethodName) {
313 this.target = target;
314 this.action = action;
315 if (target == null) {
316 throw new NullPointerException("target must be non-null");
317 }
318 if (action == null) {
319 throw new NullPointerException("action must be non-null");
320 }
321 this.eventPropertyName = eventPropertyName;
322 this.listenerMethodName = listenerMethodName;
342 * @see #EventHandler(Object, String, String, String)
343 */
344 public String getAction() {
345 return action;
346 }
347
348 /**
349 * Returns the property of the event that should be
350 * used in the action applied to the target.
351 *
352 * @return the property of the event
353 *
354 * @see #EventHandler(Object, String, String, String)
355 */
356 public String getEventPropertyName() {
357 return eventPropertyName;
358 }
359
360 /**
361 * Returns the name of the method that will trigger the action.
362 * A return value of {@code null} signifies that all methods in the
363 * listener interface trigger the action.
364 *
365 * @return the name of the method that will trigger the action
366 *
367 * @see #EventHandler(Object, String, String, String)
368 */
369 public String getListenerMethodName() {
370 return listenerMethodName;
371 }
372
373 private Object applyGetters(Object target, String getters) {
374 if (getters == null || getters.equals("")) {
375 return target;
376 }
377 int firstDot = getters.indexOf('.');
378 if (firstDot == -1) {
379 firstDot = getters.length();
380 }
381 String first = getters.substring(0, firstDot);
382 String rest = getters.substring(Math.min(firstDot + 1, getters.length()));
395 if (getter == null) {
396 getter = Statement.getMethod(target.getClass(), first, new Class<?>[]{});
397 }
398 }
399 if (getter == null) {
400 throw new RuntimeException("No method called: " + first +
401 " defined on " + target);
402 }
403 Object newTarget = MethodUtil.invoke(getter, target, new Object[]{});
404 return applyGetters(newTarget, rest);
405 }
406 catch (Exception e) {
407 throw new RuntimeException("Failed to call method: " + first +
408 " on " + target, e);
409 }
410 }
411
412 /**
413 * Extract the appropriate property value from the event and
414 * pass it to the action associated with
415 * this {@code EventHandler}.
416 *
417 * @param proxy the proxy object
418 * @param method the method in the listener interface
419 * @return the result of applying the action to the target
420 *
421 * @see EventHandler
422 */
423 public Object invoke(final Object proxy, final Method method, final Object[] arguments) {
424 AccessControlContext acc = this.acc;
425 if ((acc == null) && (System.getSecurityManager() != null)) {
426 throw new SecurityException("AccessControlContext is not set");
427 }
428 return AccessController.doPrivileged(new PrivilegedAction<Object>() {
429 public Object run() {
430 return invokeInternal(proxy, method, arguments);
431 }
432 }, acc);
433 }
434
435 private Object invokeInternal(Object proxy, Method method, Object[] arguments) {
478 throw new RuntimeException(
479 "No method called " + action + " on " +
480 target.getClass() + argTypeString);
481 }
482 return MethodUtil.invoke(targetMethod, target, newArgs);
483 }
484 catch (IllegalAccessException ex) {
485 throw new RuntimeException(ex);
486 }
487 catch (InvocationTargetException ex) {
488 Throwable th = ex.getTargetException();
489 throw (th instanceof RuntimeException)
490 ? (RuntimeException) th
491 : new RuntimeException(th);
492 }
493 }
494 return null;
495 }
496
497 /**
498 * Creates an implementation of {@code listenerInterface} in which
499 * <em>all</em> of the methods in the listener interface apply
500 * the handler's {@code action} to the {@code target}. This
501 * method is implemented by calling the other, more general,
502 * implementation of the {@code create} method with both
503 * the {@code eventPropertyName} and the {@code listenerMethodName}
504 * taking the value {@code null}. Refer to
505 * {@link java.beans.EventHandler#create(Class, Object, String, String)
506 * the general version of create} for a complete description of
507 * the {@code action} parameter.
508 * <p>
509 * To create an {@code ActionListener} that shows a
510 * {@code JDialog} with {@code dialog.show()},
511 * one can write:
512 *
513 *<blockquote>
514 *<pre>
515 *EventHandler.create(ActionListener.class, dialog, "show")
516 *</pre>
517 *</blockquote>
518 *
519 * @param <T> the type to create
520 * @param listenerInterface the listener interface to create a proxy for
521 * @param target the object that will perform the action
522 * @param action the name of a (possibly qualified) property or method on
523 * the target
524 * @return an object that implements {@code listenerInterface}
525 *
526 * @throws NullPointerException if {@code listenerInterface} is null
527 * @throws NullPointerException if {@code target} is null
528 * @throws NullPointerException if {@code action} is null
529 *
530 * @see #create(Class, Object, String, String)
531 */
532 public static <T> T create(Class<T> listenerInterface,
533 Object target, String action)
534 {
535 return create(listenerInterface, target, action, null, null);
536 }
537
538 /**
539 /**
540 * Creates an implementation of {@code listenerInterface} in which
541 * <em>all</em> of the methods pass the value of the event
542 * expression, {@code eventPropertyName}, to the final method in the
543 * statement, {@code action}, which is applied to the {@code target}.
544 * This method is implemented by calling the
545 * more general, implementation of the {@code create} method with
546 * the {@code listenerMethodName} taking the value {@code null}.
547 * Refer to
548 * {@link java.beans.EventHandler#create(Class, Object, String, String)
549 * the general version of create} for a complete description of
550 * the {@code action} and {@code eventPropertyName} parameters.
551 * <p>
552 * To create an {@code ActionListener} that sets the
553 * the text of a {@code JLabel} to the text value of
554 * the {@code JTextField} source of the incoming event,
555 * you can use the following code:
556 *
557 *<blockquote>
558 *<pre>
559 *EventHandler.create(ActionListener.class, label, "text", "source.text");
560 *</pre>
561 *</blockquote>
562 *
563 * This is equivalent to the following code:
564 *<blockquote>
565 *<pre>
566 //Equivalent code using an inner class instead of EventHandler.
567 *new ActionListener() {
568 * public void actionPerformed(ActionEvent event) {
569 * label.setText(((JTextField)(event.getSource())).getText());
570 * }
571 *};
572 *</pre>
573 *</blockquote>
574 *
575 * @param <T> the type to create
576 * @param listenerInterface the listener interface to create a proxy for
577 * @param target the object that will perform the action
578 * @param action the name of a (possibly qualified) property or method on
579 * the target
580 * @param eventPropertyName the (possibly qualified) name of a readable property of the incoming event
581 *
582 * @return an object that implements {@code listenerInterface}
583 *
584 * @throws NullPointerException if {@code listenerInterface} is null
585 * @throws NullPointerException if {@code target} is null
586 * @throws NullPointerException if {@code action} is null
587 *
588 * @see #create(Class, Object, String, String, String)
589 */
590 public static <T> T create(Class<T> listenerInterface,
591 Object target, String action,
592 String eventPropertyName)
593 {
594 return create(listenerInterface, target, action, eventPropertyName, null);
595 }
596
597 /**
598 * Creates an implementation of {@code listenerInterface} in which
599 * the method named {@code listenerMethodName}
600 * passes the value of the event expression, {@code eventPropertyName},
601 * to the final method in the statement, {@code action}, which
602 * is applied to the {@code target}. All of the other listener
603 * methods do nothing.
604 * <p>
605 * The {@code eventPropertyName} string is used to extract a value
606 * from the incoming event object that is passed to the target
607 * method. The common case is the target method takes no arguments, in
608 * which case a value of null should be used for the
609 * {@code eventPropertyName}. Alternatively if you want
610 * the incoming event object passed directly to the target method use
611 * the empty string.
612 * The format of the {@code eventPropertyName} string is a sequence of
613 * methods or properties where each method or
614 * property is applied to the value returned by the preceding method
615 * starting from the incoming event object.
616 * The syntax is: {@code propertyName{.propertyName}*}
617 * where {@code propertyName} matches a method or
618 * property. For example, to extract the {@code point}
619 * property from a {@code MouseEvent}, you could use either
620 * {@code "point"} or {@code "getPoint"} as the
621 * {@code eventPropertyName}. To extract the "text" property from
622 * a {@code MouseEvent} with a {@code JLabel} source use any
623 * of the following as {@code eventPropertyName}:
624 * {@code "source.text"},
625 * {@code "getSource.text" "getSource.getText"} or
626 * {@code "source.getText"}. If a method can not be found, or an
627 * exception is generated as part of invoking a method a
628 * {@code RuntimeException} will be thrown at dispatch time. For
629 * example, if the incoming event object is null, and
630 * {@code eventPropertyName} is non-null and not empty, a
631 * {@code RuntimeException} will be thrown.
632 * <p>
633 * The {@code action} argument is of the same format as the
634 * {@code eventPropertyName} argument where the last property name
635 * identifies either a method name or writable property.
636 * <p>
637 * If the {@code listenerMethodName} is {@code null}
638 * <em>all</em> methods in the interface trigger the {@code action} to be
639 * executed on the {@code target}.
640 * <p>
641 * For example, to create a {@code MouseListener} that sets the target
642 * object's {@code origin} property to the incoming {@code MouseEvent}'s
643 * location (that's the value of {@code mouseEvent.getPoint()}) each
644 * time a mouse button is pressed, one would write:
645 *<blockquote>
646 *<pre>
647 *EventHandler.create(MouseListener.class, target, "origin", "point", "mousePressed");
648 *</pre>
649 *</blockquote>
650 *
651 * This is comparable to writing a {@code MouseListener} in which all
652 * of the methods except {@code mousePressed} are no-ops:
653 *
654 *<blockquote>
655 *<pre>
656 //Equivalent code using an inner class instead of EventHandler.
657 *new MouseAdapter() {
658 * public void mousePressed(MouseEvent e) {
659 * target.setOrigin(e.getPoint());
660 * }
661 *};
662 * </pre>
663 *</blockquote>
664 *
665 * @param <T> the type to create
666 * @param listenerInterface the listener interface to create a proxy for
667 * @param target the object that will perform the action
668 * @param action the name of a (possibly qualified) property or method on
669 * the target
670 * @param eventPropertyName the (possibly qualified) name of a readable property of the incoming event
671 * @param listenerMethodName the name of the method in the listener interface that should trigger the action
672 *
673 * @return an object that implements {@code listenerInterface}
674 *
675 * @throws NullPointerException if {@code listenerInterface} is null
676 * @throws NullPointerException if {@code target} is null
677 * @throws NullPointerException if {@code action} is null
678 *
679 * @see EventHandler
680 */
681 public static <T> T create(Class<T> listenerInterface,
682 Object target, String action,
683 String eventPropertyName,
684 String listenerMethodName)
685 {
686 // Create this first to verify target/action are non-null
687 final EventHandler handler = new EventHandler(target, action,
688 eventPropertyName,
689 listenerMethodName);
690 if (listenerInterface == null) {
691 throw new NullPointerException(
692 "listenerInterface must be non-null");
693 }
694 final ClassLoader loader = getClassLoader(listenerInterface);
695 final Class<?>[] interfaces = {listenerInterface};
696 return AccessController.doPrivileged(new PrivilegedAction<T>() {
697 @SuppressWarnings("unchecked")
|