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 */
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;
36import 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 */
279public class EventHandler implements InvocationHandler {
280    private Object target;
281    private String action;
282    private final String eventPropertyName;
283    private final String listenerMethodName;
284    private final AccessControlContext acc = AccessController.getContext();
285
286    /**
287     * Creates a new {@code EventHandler} object;
288     * you generally use one of the {@code create} methods
289     * instead of invoking this constructor directly.  Refer to
290     * {@link java.beans.EventHandler#create(Class, Object, String, String)
291     * the general version of create} for a complete description of
292     * the {@code eventPropertyName} and {@code listenerMethodName}
293     * parameter.
294     *
295     * @param target the object that will perform the action
296     * @param action the name of a (possibly qualified) property or method on
297     *        the target
298     * @param eventPropertyName the (possibly qualified) name of a readable property of the incoming event
299     * @param listenerMethodName the name of the method in the listener interface that should trigger the action
300     *
301     * @throws NullPointerException if {@code target} is null
302     * @throws NullPointerException if {@code action} is null
303     *
304     * @see EventHandler
305     * @see #create(Class, Object, String, String, String)
306     * @see #getTarget
307     * @see #getAction
308     * @see #getEventPropertyName
309     * @see #getListenerMethodName
310     */
311    @ConstructorProperties({"target", "action", "eventPropertyName", "listenerMethodName"})
312    public EventHandler(Object target, String action, String eventPropertyName, String listenerMethodName) {
313        this.target = target;
314        this.action = action;
315        if (target == null) {
316            throw new NullPointerException("target must be non-null");
317        }
318        if (action == null) {
319            throw new NullPointerException("action must be non-null");
320        }
321        this.eventPropertyName = eventPropertyName;
322        this.listenerMethodName = listenerMethodName;
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     */
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     */
344    public String getAction()  {
345        return action;
346    }
347
348    /**
349     * Returns the property of the event that should be
350     * used in the action applied to the target.
351     *
352     * @return the property of the event
353     *
354     * @see #EventHandler(Object, String, String, String)
355     */
356    public String getEventPropertyName()  {
357        return eventPropertyName;
358    }
359
360    /**
361     * Returns the name of the method that will trigger the action.
362     * A return value of {@code null} signifies that all methods in the
363     * listener interface trigger the action.
364     *
365     * @return the name of the method that will trigger the action
366     *
367     * @see #EventHandler(Object, String, String, String)
368     */
369    public String getListenerMethodName()  {
370        return listenerMethodName;
371    }
372
373    private Object applyGetters(Object target, String getters) {
374        if (getters == null || getters.equals("")) {
375            return target;
376        }
377        int firstDot = getters.indexOf('.');
378        if (firstDot == -1) {
379            firstDot = getters.length();
380        }
381        String first = getters.substring(0, firstDot);
382        String rest = getters.substring(Math.min(firstDot + 1, getters.length()));
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     */
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