History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/java/beans/EventHandler.java (1 of 18352 files). Show all

Unified · Split

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