1 /*
2 * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any 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 java.util.EventObject;
36 import sun.reflect.misc.MethodUtil;
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 *}
204 *</pre>
205 * </blockquote>
206 *
207 * The event property may be "qualified" with an arbitrary number
208 * of property prefixes delimited with the "." character. The "qualifying"
209 * names that appear before the "." characters are taken as the names of
210 * properties that should be applied, left-most first, to
211 * the event object.
212 * <p>
213 * For example, the following action listener
214 *
215 * <blockquote>
216 *<pre>
217 *EventHandler.create(ActionListener.class, target, "a", "b.c.d")
218 *</pre>
219 * </blockquote>
220 *
221 * might be written as the following inner class
222 * (assuming all the properties had canonical getter methods and
223 * returned the appropriate types):
224 *
225 * <blockquote>
226 *<pre>
227 //Equivalent code using an inner class instead of EventHandler.
228 *new ActionListener {
229 * public void actionPerformed(ActionEvent e) {
230 * target.setA(e.getB().getC().isD());
231 * }
232 *}
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 String eventPropertyName;
283 private String listenerMethodName;
284 private AccessControlContext acc;
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.acc = AccessController.getContext();
314 this.target = target;
315 this.action = action;
316 if (target == null) {
317 throw new NullPointerException("target must be non-null");
318 }
319 if (action == null) {
320 throw new NullPointerException("action must be non-null");
321 }
322 this.eventPropertyName = eventPropertyName;
323 this.listenerMethodName = listenerMethodName;
324 }
325
326 /**
327 * Returns the object to which this event handler will send a message.
328 *
329 * @return the target of this event handler
330 * @see #EventHandler(Object, String, String, String)
331 */
332 public Object getTarget() {
333 return target;
334 }
335
336 /**
337 * Returns the name of the target's writable property
338 * that this event handler will set,
339 * or the name of the method that this event handler
340 * will invoke on the target.
341 *
342 * @return the action of this event handler
343 * @see #EventHandler(Object, String, String, String)
344 */
345 public String getAction() {
346 return action;
347 }
348
349 /**
350 * Returns the property of the event that should be
351 * used in the action applied to the target.
352 *
353 * @return the property of the event
354 *
355 * @see #EventHandler(Object, String, String, String)
356 */
357 public String getEventPropertyName() {
358 return eventPropertyName;
359 }
360
361 /**
362 * Returns the name of the method that will trigger the action.
363 * A return value of <code>null</code> signifies that all methods in the
364 * listener interface trigger the action.
365 *
366 * @return the name of the method that will trigger the action
367 *
368 * @see #EventHandler(Object, String, String, String)
369 */
370 public String getListenerMethodName() {
371 return listenerMethodName;
372 }
373
374 private Object applyGetters(Object target, String getters) {
375 if (getters == null || getters.equals("")) {
376 return target;
377 }
378 int firstDot = getters.indexOf('.');
379 if (firstDot == -1) {
380 firstDot = getters.length();
381 }
382 String first = getters.substring(0, firstDot);
383 String rest = getters.substring(Math.min(firstDot + 1, getters.length()));
384
385 try {
386 Method getter = null;
387 if (target != null) {
388 getter = ReflectionUtils.getMethod(target.getClass(),
389 "get" + NameGenerator.capitalize(first),
390 new Class[]{});
391 if (getter == null) {
392 getter = ReflectionUtils.getMethod(target.getClass(),
393 "is" + NameGenerator.capitalize(first),
394 new Class[]{});
395 }
396 if (getter == null) {
397 getter = ReflectionUtils.getMethod(target.getClass(), first, new Class[]{});
398 }
399 }
400 if (getter == null) {
401 throw new RuntimeException("No method called: " + first +
402 " defined on " + target);
403 }
404 Object newTarget = MethodUtil.invoke(getter, target, new Object[]{});
405 return applyGetters(newTarget, rest);
406 }
407 catch (Throwable e) {
408 throw new RuntimeException("Failed to call method: " + first +
409 " on " + target, e);
410 }
411 }
412
413 /**
414 * Extract the appropriate property value from the event and
415 * pass it to the action associated with
416 * this <code>EventHandler</code>.
417 *
418 * @param proxy the proxy object
419 * @param method the method in the listener interface
420 * @return the result of applying the action to the target
421 *
422 * @see EventHandler
423 */
424 public Object invoke(final Object proxy, final Method method, final Object[] arguments) {
425 return AccessController.doPrivileged(new PrivilegedAction() {
426 public Object run() {
427 return invokeInternal(proxy, method, arguments);
428 }
429 }, acc);
430 }
431
432 private Object invokeInternal(Object proxy, Method method, Object[] arguments) {
433 String methodName = method.getName();
434 if (method.getDeclaringClass() == Object.class) {
435 // Handle the Object public methods.
436 if (methodName.equals("hashCode")) {
437 return new Integer(System.identityHashCode(proxy));
438 } else if (methodName.equals("equals")) {
439 return (proxy == arguments[0] ? Boolean.TRUE : Boolean.FALSE);
440 } else if (methodName.equals("toString")) {
441 return proxy.getClass().getName() + '@' + Integer.toHexString(proxy.hashCode());
442 }
443 }
444
445 if (listenerMethodName == null || listenerMethodName.equals(methodName)) {
446 Class[] argTypes = null;
447 Object[] newArgs = null;
448
449 if (eventPropertyName == null) { // Nullary method.
450 newArgs = new Object[]{};
451 argTypes = new Class[]{};
452 }
453 else {
454 Object input = applyGetters(arguments[0], getEventPropertyName());
455 newArgs = new Object[]{input};
456 argTypes = new Class[]{input == null ? null :
457 input.getClass()};
458 }
459 try {
460 int lastDot = action.lastIndexOf('.');
461 if (lastDot != -1) {
462 target = applyGetters(target, action.substring(0, lastDot));
463 action = action.substring(lastDot + 1);
464 }
465 Method targetMethod = ReflectionUtils.getMethod(
466 target.getClass(), action, argTypes);
467 if (targetMethod == null) {
468 targetMethod = ReflectionUtils.getMethod(target.getClass(),
469 "set" + NameGenerator.capitalize(action), argTypes);
470 }
471 if (targetMethod == null) {
472 String argTypeString = (argTypes.length == 0)
473 ? " with no arguments"
474 : " with argument " + argTypes[0];
475 throw new RuntimeException(
476 "No method called " + action + " on " +
477 target.getClass() + argTypeString);
478 }
479 return MethodUtil.invoke(targetMethod, target, newArgs);
480 }
481 catch (IllegalAccessException ex) {
482 throw new RuntimeException(ex);
483 }
484 catch (InvocationTargetException ex) {
485 throw new RuntimeException(ex.getTargetException());
486 }
487 }
488 return null;
489 }
490
491 /**
492 * Creates an implementation of <code>listenerInterface</code> in which
493 * <em>all</em> of the methods in the listener interface apply
494 * the handler's <code>action</code> to the <code>target</code>. This
495 * method is implemented by calling the other, more general,
496 * implementation of the <code>create</code> method with both
497 * the <code>eventPropertyName</code> and the <code>listenerMethodName</code>
498 * taking the value <code>null</code>. Refer to
499 * {@link java.beans.EventHandler#create(Class, Object, String, String)
500 * the general version of create} for a complete description of
501 * the <code>action</code> parameter.
502 * <p>
503 * To create an <code>ActionListener</code> that shows a
504 * <code>JDialog</code> with <code>dialog.show()</code>,
505 * one can write:
506 *
507 *<blockquote>
508 *<pre>
509 *EventHandler.create(ActionListener.class, dialog, "show")
510 *</pre>
511 *</blockquote>
512 *
513 * @param listenerInterface the listener interface to create a proxy for
514 * @param target the object that will perform the action
515 * @param action the name of a (possibly qualified) property or method on
516 * the target
517 * @return an object that implements <code>listenerInterface</code>
518 *
519 * @throws NullPointerException if <code>listenerInterface</code> is null
520 * @throws NullPointerException if <code>target</code> is null
521 * @throws NullPointerException if <code>action</code> is null
522 *
523 * @see #create(Class, Object, String, String)
524 */
525 public static <T> T create(Class<T> listenerInterface,
526 Object target, String action)
527 {
528 return create(listenerInterface, target, action, null, null);
529 }
530
531 /**
532 /**
533 * Creates an implementation of <code>listenerInterface</code> in which
534 * <em>all</em> of the methods pass the value of the event
535 * expression, <code>eventPropertyName</code>, to the final method in the
536 * statement, <code>action</code>, which is applied to the <code>target</code>.
537 * This method is implemented by calling the
538 * more general, implementation of the <code>create</code> method with
539 * the <code>listenerMethodName</code> taking the value <code>null</code>.
540 * Refer to
541 * {@link java.beans.EventHandler#create(Class, Object, String, String)
542 * the general version of create} for a complete description of
543 * the <code>action</code> and <code>eventPropertyName</code> parameters.
544 * <p>
545 * To create an <code>ActionListener</code> that sets the
546 * the text of a <code>JLabel</code> to the text value of
547 * the <code>JTextField</code> source of the incoming event,
548 * you can use the following code:
549 *
550 *<blockquote>
551 *<pre>
552 *EventHandler.create(ActionListener.class, label, "text", "source.text");
553 *</pre>
554 *</blockquote>
555 *
556 * This is equivalent to the following code:
557 *<blockquote>
558 *<pre>
559 //Equivalent code using an inner class instead of EventHandler.
560 *new ActionListener() {
561 * public void actionPerformed(ActionEvent event) {
562 * label.setText(((JTextField)(event.getSource())).getText());
563 * }
564 *};
565 *</pre>
566 *</blockquote>
567 *
568 * @param listenerInterface the listener interface to create a proxy for
569 * @param target the object that will perform the action
570 * @param action the name of a (possibly qualified) property or method on
571 * the target
572 * @param eventPropertyName the (possibly qualified) name of a readable property of the incoming event
573 *
574 * @return an object that implements <code>listenerInterface</code>
575 *
576 * @throws NullPointerException if <code>listenerInterface</code> is null
577 * @throws NullPointerException if <code>target</code> is null
578 * @throws NullPointerException if <code>action</code> is null
579 *
580 * @see #create(Class, Object, String, String, String)
581 */
582 public static <T> T create(Class<T> listenerInterface,
583 Object target, String action,
584 String eventPropertyName)
585 {
586 return create(listenerInterface, target, action, eventPropertyName, null);
587 }
588
589 /**
590 * Creates an implementation of <code>listenerInterface</code> in which
591 * the method named <code>listenerMethodName</code>
592 * passes the value of the event expression, <code>eventPropertyName</code>,
593 * to the final method in the statement, <code>action</code>, which
594 * is applied to the <code>target</code>. All of the other listener
595 * methods do nothing.
596 * <p>
597 * The <code>eventPropertyName</code> string is used to extract a value
598 * from the incoming event object that is passed to the target
599 * method. The common case is the target method takes no arguments, in
600 * which case a value of null should be used for the
601 * <code>eventPropertyName</code>. Alternatively if you want
602 * the incoming event object passed directly to the target method use
603 * the empty string.
604 * The format of the <code>eventPropertyName</code> string is a sequence of
605 * methods or properties where each method or
606 * property is applied to the value returned by the preceeding method
607 * starting from the incoming event object.
608 * The syntax is: <code>propertyName{.propertyName}*</code>
609 * where <code>propertyName</code> matches a method or
610 * property. For example, to extract the <code>point</code>
611 * property from a <code>MouseEvent</code>, you could use either
612 * <code>"point"</code> or <code>"getPoint"</code> as the
613 * <code>eventPropertyName</code>. To extract the "text" property from
614 * a <code>MouseEvent</code> with a <code>JLabel</code> source use any
615 * of the following as <code>eventPropertyName</code>:
616 * <code>"source.text"</code>,
617 * <code>"getSource.text"</code> <code>"getSource.getText"</code> or
618 * <code>"source.getText"</code>. If a method can not be found, or an
619 * exception is generated as part of invoking a method a
620 * <code>RuntimeException</code> will be thrown at dispatch time. For
621 * example, if the incoming event object is null, and
622 * <code>eventPropertyName</code> is non-null and not empty, a
623 * <code>RuntimeException</code> will be thrown.
624 * <p>
625 * The <code>action</code> argument is of the same format as the
626 * <code>eventPropertyName</code> argument where the last property name
627 * identifies either a method name or writable property.
628 * <p>
629 * If the <code>listenerMethodName</code> is <code>null</code>
630 * <em>all</em> methods in the interface trigger the <code>action</code> to be
631 * executed on the <code>target</code>.
632 * <p>
633 * For example, to create a <code>MouseListener</code> that sets the target
634 * object's <code>origin</code> property to the incoming <code>MouseEvent</code>'s
635 * location (that's the value of <code>mouseEvent.getPoint()</code>) each
636 * time a mouse button is pressed, one would write:
637 *<blockquote>
638 *<pre>
639 *EventHandler.create(MouseListener.class, "mousePressed", target, "origin", "point");
640 *</pre>
641 *</blockquote>
642 *
643 * This is comparable to writing a <code>MouseListener</code> in which all
644 * of the methods except <code>mousePressed</code> are no-ops:
645 *
646 *<blockquote>
647 *<pre>
648 //Equivalent code using an inner class instead of EventHandler.
649 *new MouseAdapter() {
650 * public void mousePressed(MouseEvent e) {
651 * target.setOrigin(e.getPoint());
652 * }
653 *};
654 * </pre>
655 *</blockquote>
656 *
657 * @param listenerInterface the listener interface to create a proxy for
658 * @param target the object that will perform the action
659 * @param action the name of a (possibly qualified) property or method on
660 * the target
661 * @param eventPropertyName the (possibly qualified) name of a readable property of the incoming event
662 * @param listenerMethodName the name of the method in the listener interface that should trigger the action
663 *
664 * @return an object that implements <code>listenerInterface</code>
665 *
666 * @throws NullPointerException if <code>listenerInterface</code> is null
667 * @throws NullPointerException if <code>target</code> is null
668 * @throws NullPointerException if <code>action</code> is null
669 *
670 * @see EventHandler
671 */
672 public static <T> T create(Class<T> listenerInterface,
673 Object target, String action,
674 String eventPropertyName,
675 String listenerMethodName)
676 {
677 // Create this first to verify target/action are non-null
678 EventHandler eventHandler = new EventHandler(target, action,
679 eventPropertyName,
680 listenerMethodName);
681 if (listenerInterface == null) {
682 throw new NullPointerException(
683 "listenerInterface must be non-null");
684 }
685 return (T)Proxy.newProxyInstance(target.getClass().getClassLoader(),
686 new Class[] {listenerInterface},
687 eventHandler);
688 }
689 }