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