1 /*
   2  * Copyright (c) 2000, 2014, 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</code> class provides
  40  * support for dynamically generating event listeners whose methods
  41  * execute a simple statement involving an incoming event object
  42  * and a target object.
  43  * <p>
  44  * The <code>EventHandler</code> class is intended to be used by interactive tools, such as
  45  * application builders, that allow developers to make connections between
  46  * beans. Typically connections are made from a user interface bean
  47  * (the event <em>source</em>)
  48  * to an application logic bean (the <em>target</em>). The most effective
  49  * connections of this kind isolate the application logic from the user
  50  * interface.  For example, the <code>EventHandler</code> for a
  51  * connection from a <code>JCheckBox</code> to a method
  52  * that accepts a boolean value can deal with extracting the state
  53  * of the check box and passing it directly to the method so that
  54  * the method is isolated from the user interface layer.
  55  * <p>
  56  * Inner classes are another, more general way to handle events from
  57  * user interfaces.  The <code>EventHandler</code> class
  58  * handles only a subset of what is possible using inner
  59  * classes. However, <code>EventHandler</code> works better
  60  * with the long-term persistence scheme than inner classes.
  61  * Also, using <code>EventHandler</code> in large applications in
  62  * which the same interface is implemented many times can
  63  * reduce the disk and memory footprint of the application.
  64  * <p>
  65  * The reason that listeners created with <code>EventHandler</code>
  66  * have such a small
  67  * footprint is that the <code>Proxy</code> class, on which
  68  * the <code>EventHandler</code> relies, shares implementations
  69  * of identical
  70  * interfaces. For example, if you use
  71  * the <code>EventHandler</code> <code>create</code> methods to make
  72  * all the <code>ActionListener</code>s in an application,
  73  * all the action listeners will be instances of a single class
  74  * (one created by the <code>Proxy</code> class).
  75  * In general, listeners based on
  76  * the <code>Proxy</code> class require one listener class
  77  * to be created per <em>listener type</em> (interface),
  78  * whereas the inner class
  79  * approach requires one class to be created per <em>listener</em>
  80  * (object that implements the interface).
  81  *
  82  * <p>
  83  * You don't generally deal directly with <code>EventHandler</code>
  84  * instances.
  85  * Instead, you use one of the <code>EventHandler</code>
  86  * <code>create</code> methods to create
  87  * an object that implements a given listener interface.
  88  * This listener object uses an <code>EventHandler</code> object
  89  * behind the scenes to encapsulate information about the
  90  * event, the object to be sent a message when the event occurs,
  91  * the message (method) to be sent, and any argument
  92  * to the method.
  93  * The following section gives examples of how to create listener
  94  * objects using the <code>create</code> methods.
  95  *
  96  * <h2>Examples of Using EventHandler</h2>
  97  *
  98  * The simplest use of <code>EventHandler</code> is to install
  99  * a listener that calls a method on the target object with no arguments.
 100  * In the following example we create an <code>ActionListener</code>
 101  * that invokes the <code>toFront</code> method on an instance
 102  * of <code>javax.swing.JFrame</code>.
 103  *
 104  * <blockquote>
 105  *<pre>
 106  *myButton.addActionListener(
 107  *    (ActionListener)EventHandler.create(ActionListener.class, frame, "toFront"));
 108  *</pre>
 109  * </blockquote>
 110  *
 111  * When <code>myButton</code> is pressed, the statement
 112  * <code>frame.toFront()</code> will be executed.  One could get
 113  * the same effect, with some additional compile-time type safety,
 114  * by defining a new implementation of the <code>ActionListener</code>
 115  * interface and adding an instance of it to the button:
 116  *
 117  * <blockquote>
 118  *<pre>
 119 //Equivalent code using an inner class instead of EventHandler.
 120  *myButton.addActionListener(new ActionListener() {
 121  *    public void actionPerformed(ActionEvent e) {
 122  *        frame.toFront();
 123  *    }
 124  *});
 125  *</pre>
 126  * </blockquote>
 127  *
 128  * The next simplest use of <code>EventHandler</code> is
 129  * to extract a property value from the first argument
 130  * of the method in the listener interface (typically an event object)
 131  * and use it to set the value of a property in the target object.
 132  * In the following example we create an <code>ActionListener</code> that
 133  * sets the <code>nextFocusableComponent</code> property of the target
 134  * (myButton) object to the value of the "source" property of the event.
 135  *
 136  * <blockquote>
 137  *<pre>
 138  *EventHandler.create(ActionListener.class, myButton, "nextFocusableComponent", "source")
 139  *</pre>
 140  * </blockquote>
 141  *
 142  * This would correspond to the following inner class implementation:
 143  *
 144  * <blockquote>
 145  *<pre>
 146 //Equivalent code using an inner class instead of EventHandler.
 147  *new ActionListener() {
 148  *    public void actionPerformed(ActionEvent e) {
 149  *        myButton.setNextFocusableComponent((Component)e.getSource());
 150  *    }
 151  *}
 152  *</pre>
 153  * </blockquote>
 154  *
 155  * It's also possible to create an <code>EventHandler</code> that
 156  * just passes the incoming event object to the target's action.
 157  * If the fourth <code>EventHandler.create</code> argument is
 158  * an empty string, then the event is just passed along:
 159  *
 160  * <blockquote>
 161  *<pre>
 162  *EventHandler.create(ActionListener.class, target, "doActionEvent", "")
 163  *</pre>
 164  * </blockquote>
 165  *
 166  * This would correspond to the following inner class implementation:
 167  *
 168  * <blockquote>
 169  *<pre>
 170 //Equivalent code using an inner class instead of EventHandler.
 171  *new ActionListener() {
 172  *    public void actionPerformed(ActionEvent e) {
 173  *        target.doActionEvent(e);
 174  *    }
 175  *}
 176  *</pre>
 177  * </blockquote>
 178  *
 179  * Probably the most common use of <code>EventHandler</code>
 180  * is to extract a property value from the
 181  * <em>source</em> of the event object and set this value as
 182  * the value of a property of the target object.
 183  * In the following example we create an <code>ActionListener</code> that
 184  * sets the "label" property of the target
 185  * object to the value of the "text" property of the
 186  * source (the value of the "source" property) of the event.
 187  *
 188  * <blockquote>
 189  *<pre>
 190  *EventHandler.create(ActionListener.class, myButton, "label", "source.text")
 191  *</pre>
 192  * </blockquote>
 193  *
 194  * This would correspond to the following inner class implementation:
 195  *
 196  * <blockquote>
 197  *<pre>
 198 //Equivalent code using an inner class instead of EventHandler.
 199  *new ActionListener {
 200  *    public void actionPerformed(ActionEvent e) {
 201  *        myButton.setLabel(((JTextField)e.getSource()).getText());
 202  *    }
 203  *}
 204  *</pre>
 205  * </blockquote>
 206  *
 207  * The event property may be "qualified" with an arbitrary number
 208  * of property prefixes delimited with the "." character. The "qualifying"
 209  * names that appear before the "." characters are taken as the names of
 210  * properties that should be applied, left-most first, to
 211  * the event object.
 212  * <p>
 213  * For example, the following action listener
 214  *
 215  * <blockquote>
 216  *<pre>
 217  *EventHandler.create(ActionListener.class, target, "a", "b.c.d")
 218  *</pre>
 219  * </blockquote>
 220  *
 221  * might be written as the following inner class
 222  * (assuming all the properties had canonical getter methods and
 223  * returned the appropriate types):
 224  *
 225  * <blockquote>
 226  *<pre>
 227 //Equivalent code using an inner class instead of EventHandler.
 228  *new ActionListener {
 229  *    public void actionPerformed(ActionEvent e) {
 230  *        target.setA(e.getB().getC().isD());
 231  *    }
 232  *}
 233  *</pre>
 234  * </blockquote>
 235  * The target property may also be "qualified" with an arbitrary number
 236  * of property prefixs delimited with the "." character.  For example, the
 237  * following action listener:
 238  * <pre>
 239  *   EventHandler.create(ActionListener.class, target, "a.b", "c.d")
 240  * </pre>
 241  * might be written as the following inner class
 242  * (assuming all the properties had canonical getter methods and
 243  * returned the appropriate types):
 244  * <pre>
 245  *   //Equivalent code using an inner class instead of EventHandler.
 246  *   new ActionListener {
 247  *     public void actionPerformed(ActionEvent e) {
 248  *         target.getA().setB(e.getC().isD());
 249  *    }
 250  *}
 251  *</pre>
 252  * <p>
 253  * As <code>EventHandler</code> ultimately relies on reflection to invoke
 254  * a method we recommend against targeting an overloaded method.  For example,
 255  * if the target is an instance of the class <code>MyTarget</code> which is
 256  * defined as:
 257  * <pre>
 258  *   public class MyTarget {
 259  *     public void doIt(String);
 260  *     public void doIt(Object);
 261  *   }
 262  * </pre>
 263  * Then the method <code>doIt</code> is overloaded.  EventHandler will invoke
 264  * the method that is appropriate based on the source.  If the source is
 265  * null, then either method is appropriate and the one that is invoked is
 266  * undefined.  For that reason we recommend against targeting overloaded
 267  * methods.
 268  *
 269  * @see java.lang.reflect.Proxy
 270  * @see java.util.EventObject
 271  *
 272  * @since 1.4
 273  *
 274  * @author Mark Davidson
 275  * @author Philip Milne
 276  * @author Hans Muller
 277  *
 278  */
 279 public class EventHandler implements InvocationHandler {
 280     private Object target;
 281     private String action;
 282     private final String eventPropertyName;
 283     private final String listenerMethodName;
 284     private final AccessControlContext acc = AccessController.getContext();
 285 
 286     /**
 287      * Creates a new <code>EventHandler</code> object;
 288      * you generally use one of the <code>create</code> methods
 289      * instead of invoking this constructor directly.  Refer to
 290      * {@link java.beans.EventHandler#create(Class, Object, String, String)
 291      * the general version of create} for a complete description of
 292      * the <code>eventPropertyName</code> and <code>listenerMethodName</code>
 293      * parameter.
 294      *
 295      * @param target the object that will perform the action
 296      * @param action the name of a (possibly qualified) property or method on
 297      *        the target
 298      * @param eventPropertyName the (possibly qualified) name of a readable property of the incoming event
 299      * @param listenerMethodName the name of the method in the listener interface that should trigger the action
 300      *
 301      * @throws NullPointerException if <code>target</code> is null
 302      * @throws NullPointerException if <code>action</code> is null
 303      *
 304      * @see EventHandler
 305      * @see #create(Class, Object, String, String, String)
 306      * @see #getTarget
 307      * @see #getAction
 308      * @see #getEventPropertyName
 309      * @see #getListenerMethodName
 310      */
 311     @ConstructorProperties({"target", "action", "eventPropertyName", "listenerMethodName"})
 312     public EventHandler(Object target, String action, String eventPropertyName, String listenerMethodName) {
 313         this.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</code> 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</code>.
 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</code> in which
 499      * <em>all</em> of the methods in the listener interface apply
 500      * the handler's <code>action</code> to the <code>target</code>. This
 501      * method is implemented by calling the other, more general,
 502      * implementation of the <code>create</code> method with both
 503      * the <code>eventPropertyName</code> and the <code>listenerMethodName</code>
 504      * taking the value <code>null</code>. 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</code> parameter.
 508      * <p>
 509      * To create an <code>ActionListener</code> that shows a
 510      * <code>JDialog</code> with <code>dialog.show()</code>,
 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</code>
 525      *
 526      * @throws NullPointerException if <code>listenerInterface</code> is null
 527      * @throws NullPointerException if <code>target</code> is null
 528      * @throws NullPointerException if <code>action</code> is null
 529      *
 530      * @see #create(Class, Object, String, String)
 531      */
 532     public static <T> T create(Class<T> listenerInterface,
 533                                Object target, String action)
 534     {
 535         return create(listenerInterface, target, action, null, null);
 536     }
 537 
 538     /**
 539     /**
 540      * Creates an implementation of <code>listenerInterface</code> in which
 541      * <em>all</em> of the methods pass the value of the event
 542      * expression, <code>eventPropertyName</code>, to the final method in the
 543      * statement, <code>action</code>, which is applied to the <code>target</code>.
 544      * This method is implemented by calling the
 545      * more general, implementation of the <code>create</code> method with
 546      * the <code>listenerMethodName</code> taking the value <code>null</code>.
 547      * Refer to
 548      * {@link java.beans.EventHandler#create(Class, Object, String, String)
 549      * the general version of create} for a complete description of
 550      * the <code>action</code> and <code>eventPropertyName</code> parameters.
 551      * <p>
 552      * To create an <code>ActionListener</code> that sets the
 553      * the text of a <code>JLabel</code> to the text value of
 554      * the <code>JTextField</code> source of the incoming event,
 555      * you can use the following code:
 556      *
 557      *<blockquote>
 558      *<pre>
 559      *EventHandler.create(ActionListener.class, label, "text", "source.text");
 560      *</pre>
 561      *</blockquote>
 562      *
 563      * This is equivalent to the following code:
 564      *<blockquote>
 565      *<pre>
 566 //Equivalent code using an inner class instead of EventHandler.
 567      *new ActionListener() {
 568      *    public void actionPerformed(ActionEvent event) {
 569      *        label.setText(((JTextField)(event.getSource())).getText());
 570      *     }
 571      *};
 572      *</pre>
 573      *</blockquote>
 574      *
 575      * @param <T> the type to create
 576      * @param listenerInterface the listener interface to create a proxy for
 577      * @param target the object that will perform the action
 578      * @param action the name of a (possibly qualified) property or method on
 579      *        the target
 580      * @param eventPropertyName the (possibly qualified) name of a readable property of the incoming event
 581      *
 582      * @return an object that implements <code>listenerInterface</code>
 583      *
 584      * @throws NullPointerException if <code>listenerInterface</code> is null
 585      * @throws NullPointerException if <code>target</code> is null
 586      * @throws NullPointerException if <code>action</code> is null
 587      *
 588      * @see #create(Class, Object, String, String, String)
 589      */
 590     public static <T> T create(Class<T> listenerInterface,
 591                                Object target, String action,
 592                                String eventPropertyName)
 593     {
 594         return create(listenerInterface, target, action, eventPropertyName, null);
 595     }
 596 
 597     /**
 598      * Creates an implementation of <code>listenerInterface</code> in which
 599      * the method named <code>listenerMethodName</code>
 600      * passes the value of the event expression, <code>eventPropertyName</code>,
 601      * to the final method in the statement, <code>action</code>, which
 602      * is applied to the <code>target</code>. All of the other listener
 603      * methods do nothing.
 604      * <p>
 605      * The <code>eventPropertyName</code> string is used to extract a value
 606      * from the incoming event object that is passed to the target
 607      * method.  The common case is the target method takes no arguments, in
 608      * which case a value of null should be used for the
 609      * <code>eventPropertyName</code>.  Alternatively if you want
 610      * the incoming event object passed directly to the target method use
 611      * the empty string.
 612      * The format of the <code>eventPropertyName</code> string is a sequence of
 613      * methods or properties where each method or
 614      * property is applied to the value returned by the preceding method
 615      * starting from the incoming event object.
 616      * The syntax is: <code>propertyName{.propertyName}*</code>
 617      * where <code>propertyName</code> matches a method or
 618      * property.  For example, to extract the <code>point</code>
 619      * property from a <code>MouseEvent</code>, you could use either
 620      * <code>"point"</code> or <code>"getPoint"</code> as the
 621      * <code>eventPropertyName</code>.  To extract the "text" property from
 622      * a <code>MouseEvent</code> with a <code>JLabel</code> source use any
 623      * of the following as <code>eventPropertyName</code>:
 624      * <code>"source.text"</code>,
 625      * <code>"getSource.text"</code> <code>"getSource.getText"</code> or
 626      * <code>"source.getText"</code>.  If a method can not be found, or an
 627      * exception is generated as part of invoking a method a
 628      * <code>RuntimeException</code> will be thrown at dispatch time.  For
 629      * example, if the incoming event object is null, and
 630      * <code>eventPropertyName</code> is non-null and not empty, a
 631      * <code>RuntimeException</code> will be thrown.
 632      * <p>
 633      * The <code>action</code> argument is of the same format as the
 634      * <code>eventPropertyName</code> argument where the last property name
 635      * identifies either a method name or writable property.
 636      * <p>
 637      * If the <code>listenerMethodName</code> is <code>null</code>
 638      * <em>all</em> methods in the interface trigger the <code>action</code> to be
 639      * executed on the <code>target</code>.
 640      * <p>
 641      * For example, to create a <code>MouseListener</code> that sets the target
 642      * object's <code>origin</code> property to the incoming <code>MouseEvent</code>'s
 643      * location (that's the value of <code>mouseEvent.getPoint()</code>) each
 644      * time a mouse button is pressed, one would write:
 645      *<blockquote>
 646      *<pre>
 647      *EventHandler.create(MouseListener.class, target, "origin", "point", "mousePressed");
 648      *</pre>
 649      *</blockquote>
 650      *
 651      * This is comparable to writing a <code>MouseListener</code> in which all
 652      * of the methods except <code>mousePressed</code> are no-ops:
 653      *
 654      *<blockquote>
 655      *<pre>
 656 //Equivalent code using an inner class instead of EventHandler.
 657      *new MouseAdapter() {
 658      *    public void mousePressed(MouseEvent e) {
 659      *        target.setOrigin(e.getPoint());
 660      *    }
 661      *};
 662      * </pre>
 663      *</blockquote>
 664      *
 665      * @param <T> the type to create
 666      * @param listenerInterface the listener interface to create a proxy for
 667      * @param target the object that will perform the action
 668      * @param action the name of a (possibly qualified) property or method on
 669      *        the target
 670      * @param eventPropertyName the (possibly qualified) name of a readable property of the incoming event
 671      * @param listenerMethodName the name of the method in the listener interface that should trigger the action
 672      *
 673      * @return an object that implements <code>listenerInterface</code>
 674      *
 675      * @throws NullPointerException if <code>listenerInterface</code> is null
 676      * @throws NullPointerException if <code>target</code> is null
 677      * @throws NullPointerException if <code>action</code> is null
 678      *
 679      * @see EventHandler
 680      */
 681     public static <T> T create(Class<T> listenerInterface,
 682                                Object target, String action,
 683                                String eventPropertyName,
 684                                String listenerMethodName)
 685     {
 686         // Create this first to verify target/action are non-null
 687         final EventHandler handler = new EventHandler(target, action,
 688                                                      eventPropertyName,
 689                                                      listenerMethodName);
 690         if (listenerInterface == null) {
 691             throw new NullPointerException(
 692                           "listenerInterface must be non-null");
 693         }
 694         final ClassLoader loader = getClassLoader(listenerInterface);
 695         final Class<?>[] interfaces = {listenerInterface};
 696         return AccessController.doPrivileged(new PrivilegedAction<T>() {
 697             @SuppressWarnings("unchecked")
 698             public T run() {
 699                 return (T) Proxy.newProxyInstance(loader, interfaces, handler);
 700             }
 701         });
 702     }
 703 
 704     private static ClassLoader getClassLoader(Class<?> type) {
 705         ReflectUtil.checkPackageAccess(type);
 706         ClassLoader loader = type.getClassLoader();
 707         if (loader == null) {
 708             loader = Thread.currentThread().getContextClassLoader(); // avoid use of BCP
 709             if (loader == null) {
 710                 loader = ClassLoader.getSystemClassLoader();
 711             }
 712         }
 713         return loader;
 714     }
 715 }