1. /*
  2. * @(#)EventHandler.java 1.14 04/05/05
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.beans;
  8. import java.lang.reflect.InvocationHandler;
  9. import java.lang.reflect.InvocationTargetException;
  10. import java.lang.reflect.Proxy;
  11. import java.lang.reflect.Method;
  12. import java.util.EventObject;
  13. /**
  14. * The <code>EventHandler</code> class provides
  15. * support for dynamically generating event listeners whose methods
  16. * execute a simple statement involving an incoming event object
  17. * and a target object.
  18. * <p>
  19. * The <code>EventHandler</code> class is intended to be used by interactive tools, such as
  20. * application builders, that allow developers to make connections between
  21. * beans. Typically connections are made from a user interface bean
  22. * (the event <em>source</em>)
  23. * to an application logic bean (the <em>target</em>). The most effective
  24. * connections of this kind isolate the application logic from the user
  25. * interface. For example, the <code>EventHandler</code> for a
  26. * connection from a <code>JCheckBox</code> to a method
  27. * that accepts a boolean value can deal with extracting the state
  28. * of the check box and passing it directly to the method so that
  29. * the method is isolated from the user interface layer.
  30. * <p>
  31. * Inner classes are another, more general way to handle events from
  32. * user interfaces. The <code>EventHandler</code> class
  33. * handles only a subset of what is possible using inner
  34. * classes. However, <code>EventHandler</code> works better
  35. * with the long-term persistence scheme than inner classes.
  36. * Also, using <code>EventHandler</code> in large applications in
  37. * which the same interface is implemented many times can
  38. * reduce the disk and memory footprint of the application.
  39. * <p>
  40. * The reason that listeners created with <code>EventHandler</code>
  41. * have such a small
  42. * footprint is that the <code>Proxy</code> class, on which
  43. * the <code>EventHandler</code> relies, shares implementations
  44. * of identical
  45. * interfaces. For example, if you use
  46. * the <code>EventHandler</code> <code>create</code> methods to make
  47. * all the <code>ActionListener</code>s in an application,
  48. * all the action listeners will be instances of a single class
  49. * (one created by the <code>Proxy</code> class).
  50. * In general, listeners based on
  51. * the <code>Proxy</code> class require one listener class
  52. * to be created per <em>listener type</em> (interface),
  53. * whereas the inner class
  54. * approach requires one class to be created per <em>listener</em>
  55. * (object that implements the interface).
  56. *
  57. * <p>
  58. * You don't generally deal directly with <code>EventHandler</code>
  59. * instances.
  60. * Instead, you use one of the <code>EventHandler</code>
  61. * <code>create</code> methods to create
  62. * an object that implements a given listener interface.
  63. * This listener object uses an <code>EventHandler</code> object
  64. * behind the scenes to encapsulate information about the
  65. * event, the object to be sent a message when the event occurs,
  66. * the message (method) to be sent, and any argument
  67. * to the method.
  68. * The following section gives examples of how to create listener
  69. * objects using the <code>create</code> methods.
  70. *
  71. * <h2>Examples of Using EventHandler</h2>
  72. *
  73. * The simplest use of <code>EventHandler</code> is to install
  74. * a listener that calls a method on the target object with no arguments.
  75. * In the following example we create an <code>ActionListener</code>
  76. * that invokes the <code>toFront</code> method on an instance
  77. * of <code>javax.swing.JFrame</code>.
  78. *
  79. * <blockquote>
  80. *<pre>
  81. *myButton.addActionListener(
  82. * (ActionListener)EventHandler.create(ActionListener.class, frame, "toFront"));
  83. *</pre>
  84. * </blockquote>
  85. *
  86. * When <code>myButton</code> is pressed, the statement
  87. * <code>frame.toFront()</code> will be executed. One could get
  88. * the same effect, with some additional compile-time type safety,
  89. * by defining a new implementation of the <code>ActionListener</code>
  90. * interface and adding an instance of it to the button:
  91. *
  92. * <blockquote>
  93. *<pre>
  94. //Equivalent code using an inner class instead of EventHandler.
  95. *myButton.addActionListener(new ActionListener() {
  96. * public void actionPerformed(ActionEvent e) {
  97. * frame.toFront();
  98. * }
  99. *});
  100. *</pre>
  101. * </blockquote>
  102. *
  103. * The next simplest use of <code>EventHandler</code> is
  104. * to extract a property value from the first argument
  105. * of the method in the listener interface (typically an event object)
  106. * and use it to set the value of a property in the target object.
  107. * In the following example we create an <code>ActionListener</code> that
  108. * sets the <code>nextFocusableComponent</code> property of the target
  109. * object to the value of the "source" property of the event.
  110. *
  111. * <blockquote>
  112. *<pre>
  113. *EventHandler.create(ActionListener.class, target, "nextFocusableComponent", "source")
  114. *</pre>
  115. * </blockquote>
  116. *
  117. * This would correspond to the following inner class implementation:
  118. *
  119. * <blockquote>
  120. *<pre>
  121. //Equivalent code using an inner class instead of EventHandler.
  122. *new ActionListener() {
  123. * public void actionPerformed(ActionEvent e) {
  124. * button.setNextFocusableComponent((Component)e.getSource());
  125. * }
  126. *}
  127. *</pre>
  128. * </blockquote>
  129. *
  130. * Probably the most common use of <code>EventHandler</code>
  131. * is to extract a property value from the
  132. * <em>source</em> of the event object and set this value as
  133. * the value of a property of the target object.
  134. * In the following example we create an <code>ActionListener</code> that
  135. * sets the "label" property of the target
  136. * object to the value of the "text" property of the
  137. * source (the value of the "source" property) of the event.
  138. *
  139. * <blockquote>
  140. *<pre>
  141. *EventHandler.create(ActionListener.class, button, "label", "source.text")
  142. *</pre>
  143. * </blockquote>
  144. *
  145. * This would correspond to the following inner class implementation:
  146. *
  147. * <blockquote>
  148. *<pre>
  149. //Equivalent code using an inner class instead of EventHandler.
  150. *new ActionListener {
  151. * public void actionPerformed(ActionEvent e) {
  152. * button.setLabel(((JTextField)e.getSource()).getText());
  153. * }
  154. *}
  155. *</pre>
  156. * </blockquote>
  157. *
  158. * The event property may be be "qualified" with an arbitrary number
  159. * of property prefixes delimited with the "." character. The "qualifying"
  160. * names that appear before the "." characters are taken as the names of
  161. * properties that should be applied, left-most first, to
  162. * the event object.
  163. * <p>
  164. * For example, the following action listener
  165. *
  166. * <blockquote>
  167. *<pre>
  168. *EventHandler.create(ActionListener.class, target, "a", "b.c.d")
  169. *</pre>
  170. * </blockquote>
  171. *
  172. * might be written as the following inner class
  173. * (assuming all the properties had canonical getter methods and
  174. * returned the appropriate types):
  175. *
  176. * <blockquote>
  177. *<pre>
  178. //Equivalent code using an inner class instead of EventHandler.
  179. *new ActionListener {
  180. * public void actionPerformed(ActionEvent e) {
  181. * target.setA(e.getB().getC().isD());
  182. * }
  183. *}
  184. *</pre>
  185. * </blockquote>
  186. *
  187. * @see java.lang.reflect.Proxy
  188. * @see java.util.EventObject
  189. *
  190. * @since 1.4
  191. *
  192. * @author Mark Davidson
  193. * @author Philip Milne
  194. * @author Hans Muller
  195. *
  196. * @version 1.2 10/24/00
  197. */
  198. public class EventHandler implements InvocationHandler {
  199. private static Object[] empty = new Object[]{};
  200. private Object target;
  201. private Method targetMethod;
  202. private String action;
  203. private String eventPropertyName;
  204. private String listenerMethodName;
  205. /**
  206. * Creates a new <code>EventHandler</code> object;
  207. * you generally use one of the <code>create</code> methods
  208. * instead of invoking this constructor directly.
  209. *
  210. * @param target the object that will perform the action
  211. * @param action the (possibly qualified) name of a writable property or method on the target
  212. * @param eventPropertyName the (possibly qualified) name of a readable property of the incoming event
  213. * @param listenerMethodName the name of the method in the listener interface that should trigger the action
  214. *
  215. * @see EventHandler
  216. * @see #create(Class, Object, String, String, String)
  217. * @see #getTarget
  218. * @see #getAction
  219. * @see #getEventPropertyName
  220. * @see #getListenerMethodName
  221. */
  222. public EventHandler(Object target, String action, String eventPropertyName, String listenerMethodName) {
  223. this.target = target;
  224. this.action = action;
  225. this.eventPropertyName = eventPropertyName;
  226. this.listenerMethodName = listenerMethodName;
  227. }
  228. /**
  229. * Returns the object to which this event handler will send a message.
  230. *
  231. * @return the target of this event handler
  232. * @see #EventHandler(Object, String, String, String)
  233. */
  234. public Object getTarget() {
  235. return target;
  236. }
  237. /**
  238. * Returns the name of the target's writable property
  239. * that this event handler will set,
  240. * or the name of the method that this event handler
  241. * will invoke on the target.
  242. *
  243. * @return the action of this event handler
  244. * @see #EventHandler(Object, String, String, String)
  245. */
  246. public String getAction() {
  247. return action;
  248. }
  249. /**
  250. * Returns the property of the event that should be
  251. * used in the action applied to the target.
  252. *
  253. * @return the property of the event
  254. *
  255. * @see #EventHandler(Object, String, String, String)
  256. */
  257. public String getEventPropertyName() {
  258. return eventPropertyName;
  259. }
  260. /**
  261. * Returns the name of the method that will trigger the action.
  262. * A return value of <code>null</code> signifies that all methods in the
  263. * listener interface trigger the action.
  264. *
  265. * @return the name of the method that will trigger the action
  266. *
  267. * @see #EventHandler(Object, String, String, String)
  268. */
  269. public String getListenerMethodName() {
  270. return listenerMethodName;
  271. }
  272. private Object applyGetters(Object target, String getters) {
  273. if (getters == null || getters.equals("")) {
  274. return target;
  275. }
  276. int firstDot = getters.indexOf('.');
  277. if (firstDot == -1) {
  278. firstDot = getters.length();
  279. }
  280. String first = getters.substring(0, firstDot);
  281. String rest = getters.substring(Math.min(firstDot + 1, getters.length()));
  282. try {
  283. Method getter = ReflectionUtils.getMethod(target.getClass(),
  284. "get" + NameGenerator.capitalize(first),
  285. new Class[]{});
  286. if (getter == null) {
  287. getter = ReflectionUtils.getMethod(target.getClass(),
  288. "is" + NameGenerator.capitalize(first),
  289. new Class[]{});
  290. }
  291. if (getter == null) {
  292. getter = ReflectionUtils.getMethod(target.getClass(), first, new Class[]{});
  293. }
  294. if (getter == null) {
  295. throw new RuntimeException("No method called: " + first +
  296. " defined on " + target);
  297. }
  298. Object newTarget = getter.invoke(target, new Object[]{});
  299. return applyGetters(newTarget, rest);
  300. }
  301. catch (Throwable e) {
  302. throw new RuntimeException("Failed to call method: " + first +
  303. " on " + target, e);
  304. }
  305. }
  306. /**
  307. * Extract the appropriate property value from the event and
  308. * pass it to the action associated with
  309. * this <code>EventHandler</code>.
  310. *
  311. * @param proxy the proxy object
  312. * @param method the method in the listener interface
  313. * @return the result of applying the action to the target
  314. *
  315. * @see EventHandler
  316. */
  317. public Object invoke(Object proxy, Method method, Object[] arguments) {
  318. String methodName = method.getName();
  319. if (method.getDeclaringClass() == Object.class) {
  320. // Handle the Object public methods.
  321. if (methodName.equals("hashCode")) {
  322. return new Integer(System.identityHashCode(proxy));
  323. } else if (methodName.equals("equals")) {
  324. return (proxy == arguments[0] ? Boolean.TRUE : Boolean.FALSE);
  325. } else if (methodName.equals("toString")) {
  326. return proxy.getClass().getName() + '@' + Integer.toHexString(proxy.hashCode());
  327. }
  328. }
  329. if (listenerMethodName == null || listenerMethodName.equals(methodName)) {
  330. Class[] argTypes = null;
  331. Object[] newArgs = null;
  332. if (eventPropertyName == null) { // Nullary method.
  333. newArgs = new Object[]{};
  334. argTypes = new Class[]{};
  335. }
  336. else {
  337. Object input = applyGetters(arguments[0], getEventPropertyName());
  338. newArgs = new Object[]{input};
  339. argTypes = new Class[]{input.getClass()};
  340. }
  341. try {
  342. if (targetMethod == null) {
  343. targetMethod = ReflectionUtils.getMethod(target.getClass(),
  344. action, argTypes);
  345. }
  346. if (targetMethod == null) {
  347. targetMethod = ReflectionUtils.getMethod(target.getClass(),
  348. "set" + NameGenerator.capitalize(action), argTypes);
  349. }
  350. if (targetMethod == null) {
  351. throw new RuntimeException("No method called: " +
  352. action + " on class " +
  353. target.getClass() + " with argument "
  354. + argTypes[0]);
  355. }
  356. return targetMethod.invoke(target, newArgs);
  357. }
  358. catch (IllegalAccessException ex) {
  359. throw new RuntimeException(ex);
  360. }
  361. catch (InvocationTargetException ex) {
  362. throw new RuntimeException(ex.getTargetException());
  363. }
  364. }
  365. return null;
  366. }
  367. /**
  368. * Creates an implementation of <code>listenerInterface</code> in which
  369. * <em>all</em> of the methods in the listener interface apply
  370. * the handler's <code>action</code> to the <code>target</code>. This
  371. * method is implemented by calling the other, more general,
  372. * implementation of the <code>create</code> method with both
  373. * the <code>eventPropertyName</code> and the <code>listenerMethodName</code>
  374. * taking the value <code>null</code>.
  375. * <p>
  376. * To create an <code>ActionListener</code> that shows a
  377. * <code>JDialog</code> with <code>dialog.show()</code>,
  378. * one can write:
  379. *
  380. *<blockquote>
  381. *<pre>
  382. *EventHandler.create(ActionListener.class, dialog, "show")
  383. *</pre>
  384. *</blockquote>
  385. *
  386. * <p>
  387. * @param listenerInterface the listener interface to create a proxy for
  388. * @param target the object that will perform the action
  389. * @param action the name of a writable property or method on the target
  390. *
  391. * @return an object that implements <code>listenerInterface</code>
  392. *
  393. * @see #create(Class, Object, String, String)
  394. */
  395. public static <T> T create(Class<T> listenerInterface,
  396. Object target, String action)
  397. {
  398. return create(listenerInterface, target, action, null, null);
  399. }
  400. /**
  401. * Creates an implementation of <code>listenerInterface</code> in which
  402. * <em>all</em> of the methods pass the value of the event
  403. * expression, <code>eventPropertyName</code>, to the final method in the
  404. * statement, <code>action</code>, which is applied to the <code>target</code>.
  405. * This method is implemented by calling the
  406. * more general, implementation of the <code>create</code> method with
  407. * the <code>listenerMethodName</code> taking the value <code>null</code>.
  408. * <p>
  409. * To create an <code>ActionListener</code> that sets the
  410. * the text of a <code>JLabel</code> to the text value of
  411. * the <code>JTextField</code> source of the incoming event,
  412. * you can use the following code:
  413. *
  414. *<blockquote>
  415. *<pre>
  416. *EventHandler.create(ActionListener.class, label, "text", "source.text");
  417. *</pre>
  418. *</blockquote>
  419. *
  420. * This is equivalent to the following code:
  421. *<blockquote>
  422. *<pre>
  423. //Equivalent code using an inner class instead of EventHandler.
  424. *label.setText((JTextField(event.getSource())).getText())
  425. *</pre>
  426. *</blockquote>
  427. *
  428. * @param listenerInterface the listener interface to create a proxy for
  429. * @param target the object that will perform the action
  430. * @param action the name of a writable property or method on the target
  431. * @param eventPropertyName the (possibly qualified) name of a readable property of the incoming event
  432. *
  433. * @return an object that implements <code>listenerInterface</code>
  434. *
  435. * @see #create(Class, Object, String, String, String)
  436. */
  437. public static <T> T create(Class<T> listenerInterface,
  438. Object target, String action,
  439. String eventPropertyName)
  440. {
  441. return create(listenerInterface, target, action, eventPropertyName, null);
  442. }
  443. /**
  444. * Creates an implementation of <code>listenerInterface</code> in which
  445. * the method named <code>listenerMethodName</code>
  446. * passes the value of the event expression, <code>eventPropertyName</code>,
  447. * to the final method in the statement, <code>action</code>, which
  448. * is applied to the <code>target</code>. All of the other listener
  449. * methods do nothing.
  450. * <p>
  451. * If the <code>eventPropertyName</code> is <code>null</code> the
  452. * implementation calls a method with the name specified
  453. * in <code>action</code> that takes an <code>EventObject</code>
  454. * or a no-argument method with the same name if a method
  455. * accepting an <code>EventObject</code> is not defined.
  456. * <p>
  457. * If the <code>listenerMethodName</code> is <code>null</code>
  458. * <em>all</em> methods in the interface trigger the <code>action</code> to be
  459. * executed on the <code>target</code>.
  460. * <p>
  461. * For example, to create a <code>MouseListener</code> that sets the target
  462. * object's <code>origin</code> property to the incoming <code>MouseEvent</code>'s
  463. * location (that's the value of <code>mouseEvent.getPoint()</code>) each
  464. * time a mouse button is pressed, one would write:
  465. *<blockquote>
  466. *<pre>
  467. *EventHandler.create(MouseListener.class, "mousePressed", target, "origin", "point");
  468. *</pre>
  469. *</blockquote>
  470. *
  471. * This is comparable to writing a <code>MouseListener</code> in which all
  472. * of the methods except <code>mousePressed</code> are no-ops:
  473. *
  474. *<blockquote>
  475. *<pre>
  476. //Equivalent code using an inner class instead of EventHandler.
  477. *new MouseAdapter() {
  478. * public void mousePressed(MouseEvent e) {
  479. * target.setOrigin(e.getPoint());
  480. * }
  481. *}
  482. * </pre>
  483. *</blockquote>
  484. *
  485. * @param listenerInterface the listener interface to create a proxy for
  486. * @param target the object that will perform the action
  487. * @param action the name of a writable property or method on the target
  488. * @param eventPropertyName the (possibly qualified) name of a readable property of the incoming event
  489. * @param listenerMethodName the name of the method in the listener interface that should trigger the action
  490. *
  491. * @return an object that implements <code>listenerInterface</code>
  492. *
  493. * @see EventHandler
  494. */
  495. public static <T> T create(Class<T> listenerInterface,
  496. Object target, String action,
  497. String eventPropertyName,
  498. String listenerMethodName)
  499. {
  500. return (T)Proxy.newProxyInstance(target.getClass().getClassLoader(),
  501. new Class[] {listenerInterface},
  502. new EventHandler(target, action,
  503. eventPropertyName,
  504. listenerMethodName));
  505. }
  506. }