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+ */
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+
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+ */
278+public 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+}