1 /*
2  * Copyright (c) 2000, 2016, 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 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  *}
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} 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"})
EventHandler(Object target, String action, String eventPropertyName, String 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;
323     }
324 
325     /**
326      * Returns the object to which this event handler will send a message.
327      *
328      * @return the target of this event handler
329      * @see #EventHandler(Object, String, String, String)
330      */
getTarget()331     public Object getTarget()  {
332         return target;
333     }
334 
335     /**
336      * Returns the name of the target's writable property
337      * that this event handler will set,
338      * or the name of the method that this event handler
339      * will invoke on the target.
340      *
341      * @return the action of this event handler
342      * @see #EventHandler(Object, String, String, String)
343      */
getAction()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      */
getEventPropertyName()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      */
getListenerMethodName()369     public String getListenerMethodName()  {
370         return listenerMethodName;
371     }
372 
applyGetters(Object target, String getters)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()));
383 
384         try {
385             Method getter = null;
386             if (target != null) {
387                 getter = Statement.getMethod(target.getClass(),
388                                       "get" + NameGenerator.capitalize(first),
389                                       new Class<?>[]{});
390                 if (getter == null) {
391                     getter = Statement.getMethod(target.getClass(),
392                                    "is" + NameGenerator.capitalize(first),
393                                    new Class<?>[]{});
394                 }
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      */
invoke(final Object proxy, final Method method, final Object[] arguments)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) {
436         String methodName = method.getName();
437         if (method.getDeclaringClass() == Object.class)  {
438             // Handle the Object public methods.
439             if (methodName.equals("hashCode"))  {
440                 return System.identityHashCode(proxy);
441             } else if (methodName.equals("equals")) {
442                 return (proxy == arguments[0] ? Boolean.TRUE : Boolean.FALSE);
443             } else if (methodName.equals("toString")) {
444                 return proxy.getClass().getName() + '@' + Integer.toHexString(proxy.hashCode());
445             }
446         }
447 
448         if (listenerMethodName == null || listenerMethodName.equals(methodName)) {
449             Class<?>[] argTypes = null;
450             Object[] newArgs = null;
451 
452             if (eventPropertyName == null) {     // Nullary method.
453                 newArgs = new Object[]{};
454                 argTypes = new Class<?>[]{};
455             }
456             else {
457                 Object input = applyGetters(arguments[0], getEventPropertyName());
458                 newArgs = new Object[]{input};
459                 argTypes = new Class<?>[]{input == null ? null :
460                                        input.getClass()};
461             }
462             try {
463                 int lastDot = action.lastIndexOf('.');
464                 if (lastDot != -1) {
465                     target = applyGetters(target, action.substring(0, lastDot));
466                     action = action.substring(lastDot + 1);
467                 }
468                 Method targetMethod = Statement.getMethod(
469                              target.getClass(), action, argTypes);
470                 if (targetMethod == null) {
471                     targetMethod = Statement.getMethod(target.getClass(),
472                              "set" + NameGenerator.capitalize(action), argTypes);
473                 }
474                 if (targetMethod == null) {
475                     String argTypeString = (argTypes.length == 0)
476                         ? " with no arguments"
477                         : " with argument " + argTypes[0];
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      * @throws IllegalArgumentException if creating a Proxy for
530      *         {@code listenerInterface} fails for any of the restrictions
531      *         specified by {@link Proxy#newProxyInstance}
532      * @see #create(Class, Object, String, String)
533      * @see Proxy#newProxyInstance
534      */
535     public static <T> T create(Class<T> listenerInterface,
536                                Object target, String action)
537     {
538         return create(listenerInterface, target, action, null, null);
539     }
540 
541     /**
542     /**
543      * Creates an implementation of {@code listenerInterface} in which
544      * <em>all</em> of the methods pass the value of the event
545      * expression, {@code eventPropertyName}, to the final method in the
546      * statement, {@code action}, which is applied to the {@code target}.
547      * This method is implemented by calling the
548      * more general, implementation of the {@code create} method with
549      * the {@code listenerMethodName} taking the value {@code null}.
550      * Refer to
551      * {@link java.beans.EventHandler#create(Class, Object, String, String)
552      * the general version of create} for a complete description of
553      * the {@code action} and {@code eventPropertyName} parameters.
554      * <p>
555      * To create an {@code ActionListener} that sets the
556      * the text of a {@code JLabel} to the text value of
557      * the {@code JTextField} source of the incoming event,
558      * you can use the following code:
559      *
560      *<blockquote>
561      *<pre>
562      *EventHandler.create(ActionListener.class, label, "text", "source.text");
563      *</pre>
564      *</blockquote>
565      *
566      * This is equivalent to the following code:
567      *<blockquote>
568      *<pre>
569 //Equivalent code using an inner class instead of EventHandler.
570      *new ActionListener() {
571      *    public void actionPerformed(ActionEvent event) {
572      *        label.setText(((JTextField)(event.getSource())).getText());
573      *     }
574      *};
575      *</pre>
576      *</blockquote>
577      *
578      * @param <T> the type to create
579      * @param listenerInterface the listener interface to create a proxy for
580      * @param target the object that will perform the action
581      * @param action the name of a (possibly qualified) property or method on
582      *        the target
583      * @param eventPropertyName the (possibly qualified) name of a readable property of the incoming event
584      *
585      * @return an object that implements {@code listenerInterface}
586      *
587      * @throws NullPointerException if {@code listenerInterface} is null
588      * @throws NullPointerException if {@code target} is null
589      * @throws NullPointerException if {@code action} is null
590      * @throws IllegalArgumentException if creating a Proxy for
591      *         {@code listenerInterface} fails for any of the restrictions
592      *         specified by {@link Proxy#newProxyInstance}
593      * @see #create(Class, Object, String, String, String)
594      * @see Proxy#newProxyInstance
595      */
596     public static <T> T create(Class<T> listenerInterface,
597                                Object target, String action,
598                                String eventPropertyName)
599     {
600         return create(listenerInterface, target, action, eventPropertyName, null);
601     }
602 
603     /**
604      * Creates an implementation of {@code listenerInterface} in which
605      * the method named {@code listenerMethodName}
606      * passes the value of the event expression, {@code eventPropertyName},
607      * to the final method in the statement, {@code action}, which
608      * is applied to the {@code target}. All of the other listener
609      * methods do nothing.
610      * <p>
611      * The {@code eventPropertyName} string is used to extract a value
612      * from the incoming event object that is passed to the target
613      * method.  The common case is the target method takes no arguments, in
614      * which case a value of null should be used for the
615      * {@code eventPropertyName}.  Alternatively if you want
616      * the incoming event object passed directly to the target method use
617      * the empty string.
618      * The format of the {@code eventPropertyName} string is a sequence of
619      * methods or properties where each method or
620      * property is applied to the value returned by the preceding method
621      * starting from the incoming event object.
622      * The syntax is: {@code propertyName{.propertyName}*}
623      * where {@code propertyName} matches a method or
624      * property.  For example, to extract the {@code point}
625      * property from a {@code MouseEvent}, you could use either
626      * {@code "point"} or {@code "getPoint"} as the
627      * {@code eventPropertyName}.  To extract the "text" property from
628      * a {@code MouseEvent} with a {@code JLabel} source use any
629      * of the following as {@code eventPropertyName}:
630      * {@code "source.text"},
631      * {@code "getSource.text" "getSource.getText"} or
632      * {@code "source.getText"}.  If a method can not be found, or an
633      * exception is generated as part of invoking a method a
634      * {@code RuntimeException} will be thrown at dispatch time.  For
635      * example, if the incoming event object is null, and
636      * {@code eventPropertyName} is non-null and not empty, a
637      * {@code RuntimeException} will be thrown.
638      * <p>
639      * The {@code action} argument is of the same format as the
640      * {@code eventPropertyName} argument where the last property name
641      * identifies either a method name or writable property.
642      * <p>
643      * If the {@code listenerMethodName} is {@code null}
644      * <em>all</em> methods in the interface trigger the {@code action} to be
645      * executed on the {@code target}.
646      * <p>
647      * For example, to create a {@code MouseListener} that sets the target
648      * object's {@code origin} property to the incoming {@code MouseEvent}'s
649      * location (that's the value of {@code mouseEvent.getPoint()}) each
650      * time a mouse button is pressed, one would write:
651      *<blockquote>
652      *<pre>
653      *EventHandler.create(MouseListener.class, target, "origin", "point", "mousePressed");
654      *</pre>
655      *</blockquote>
656      *
657      * This is comparable to writing a {@code MouseListener} in which all
658      * of the methods except {@code mousePressed} are no-ops:
659      *
660      *<blockquote>
661      *<pre>
662 //Equivalent code using an inner class instead of EventHandler.
663      *new MouseAdapter() {
664      *    public void mousePressed(MouseEvent e) {
665      *        target.setOrigin(e.getPoint());
666      *    }
667      *};
668      * </pre>
669      *</blockquote>
670      *
671      * @param <T> the type to create
672      * @param listenerInterface the listener interface to create a proxy for
673      * @param target the object that will perform the action
674      * @param action the name of a (possibly qualified) property or method on
675      *        the target
676      * @param eventPropertyName the (possibly qualified) name of a readable property of the incoming event
677      * @param listenerMethodName the name of the method in the listener interface that should trigger the action
678      *
679      * @return an object that implements {@code listenerInterface}
680      *
681      * @throws NullPointerException if {@code listenerInterface} is null
682      * @throws NullPointerException if {@code target} is null
683      * @throws NullPointerException if {@code action} is null
684      * @throws IllegalArgumentException if creating a Proxy for
685      *         {@code listenerInterface} fails for any of the restrictions
686      *         specified by {@link Proxy#newProxyInstance}
687      * @see EventHandler
688      * @see Proxy#newProxyInstance
689      */
690     public static <T> T create(Class<T> listenerInterface,
691                                Object target, String action,
692                                String eventPropertyName,
693                                String listenerMethodName)
694     {
695         // Create this first to verify target/action are non-null
696         final EventHandler handler = new EventHandler(target, action,
697                                                      eventPropertyName,
698                                                      listenerMethodName);
699         if (listenerInterface == null) {
700             throw new NullPointerException(
701                           "listenerInterface must be non-null");
702         }
703         final ClassLoader loader = getClassLoader(listenerInterface);
704         final Class<?>[] interfaces = {listenerInterface};
705         return AccessController.doPrivileged(new PrivilegedAction<T>() {
706             @SuppressWarnings("unchecked")
707             public T run() {
708                 return (T) Proxy.newProxyInstance(loader, interfaces, handler);
709             }
710         });
711     }
712 
713     private static ClassLoader getClassLoader(Class<?> type) {
714         ReflectUtil.checkPackageAccess(type);
715         ClassLoader loader = type.getClassLoader();
716         if (loader == null) {
717             loader = Thread.currentThread().getContextClassLoader(); // avoid use of BCP
718             if (loader == null) {
719                 loader = ClassLoader.getSystemClassLoader();
720             }
721         }
722         return loader;
723     }
724 }
725