[kaffe] CVS kaffe (robilad): Added missing EventHandler

Kaffe CVS cvs-commits at kaffe.org
Mon Sep 27 08:56:57 PDT 2004


PatchSet 5223 
Date: 2004/09/27 15:52:59
Author: robilad
Branch: HEAD
Tag: (none) 
Log:
Added missing EventHandler

Members: 
	libraries/javalib/java/beans/EventHandler.java:INITIAL->1.1 

===================================================================
Checking out kaffe/libraries/javalib/java/beans/EventHandler.java
RCS:  /home/cvs/kaffe/kaffe/libraries/javalib/java/beans/EventHandler.java,v
VERS: 1.1
***************
--- /dev/null	Sun Aug  4 19:57:58 2002
+++ kaffe/libraries/javalib/java/beans/EventHandler.java	Mon Sep 27 15:56:57 2004
@@ -0,0 +1,390 @@
+/* java.beans.EventHandler
+   Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+ 
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.beans;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+/**
+ * class EventHandler
+ *
+ * EventHandler forms a bridge between dynamically created listeners and
+ * arbitrary properties and methods.  The idea is that a Proxy that implements
+ * a listener class calls the EventHandler when a listener method is called.
+ * The Proxy calls invoke(), which dispatches the event to a method, called
+ * the action, in another object, called the target.
+ *
+ * The event passed to the listener method is used to access a prespecified
+ * property, which in turn is passed to the action method.
+ * 
+ * Normally, call EventHandler.create(), which constructs an EventHandler and
+ * a Proxy for the listener interface.  When the listenerMethod gets called on
+ * the proxy, it in turn calls invoke on the attached EventHandler.  The
+ * invoke call extracts the bean property from the event object and passes it
+ * to the action method of target object.
+ *
+ * TODO: Add examples of using this thing.
+ * 
+ * @author Jerry Quinn (jlquinn at optonline.net)
+ * @since 1.4
+ */
+public class EventHandler implements InvocationHandler
+{
+  // The name of the method that will be implemented.  If null, any method.
+  private String listenerMethod;
+
+  // The object to call action on.
+  private Object target;
+
+  // The name of the method or property setter in target.
+  private String action;
+
+  // The property to extract from an event passed to listenerMethod.
+  private String property;
+
+  // String class doesn't already have a capitalize routine.
+  final private String capitalize(String s)
+  {
+    return s.substring(0, 1).toUpperCase() + s.substring(1);
+  }
+
+  /**
+   * Creates a new <code>EventHandler</code> instance.
+   *
+   * Typical creation is done with the create method, not by newing an
+   * EventHandler.
+   *
+   * This constructs an EventHandler that will connect the method
+   * listenerMethodName to target.action, extracting eventPropertyName from
+   * the first argument of listenerMethodName. and sending it to action.
+   *
+   *
+   *
+   * @param target Object that will perform the action.
+   * @param action A property or method of the target.
+   * @param eventPropertyName A readable property of the inbound event.
+   * @param listenerMethodName The listener method name triggering the action.
+   */
+  public EventHandler(Object target, String action, String eventPropertyName,
+		      String listenerMethodName)
+  {
+    this.target = target;
+    this.action = action;	// Turn this into a method or do we wait till
+				// runtime
+    property = eventPropertyName;
+    listenerMethod = listenerMethodName;
+  }
+
+  /**
+   * Return the event property name.
+   */
+  public String getEventPropertyName()
+  {
+    return property;
+  }
+
+  /**
+   * Return the listener's method name.
+   */
+  public String getListenerMethodName()
+  {
+    return listenerMethod;
+  }
+
+  /**
+   * Return the target object.
+   */
+  public Object getTarget()
+  {
+    return target;
+  }
+
+  /**
+   * Return the action method name.
+   */
+  public String getAction()
+  {
+    return action;
+  }
+
+  // Fetch a qualified property like a.b.c from object o.  The properties can
+  // be boolean isProp or object getProp properties.
+  //
+  // Returns a length 2 array with the first entry containing the value
+  // extracted from the property, and the second entry contains the class of
+  // the method return type.
+  //
+  // We play this game because if the method returns a native type, the return
+  // value will be a wrapper.  If we then take the type of the wrapper and use
+  // it to locate the action method that takes the native type, it won't match.
+  private Object[] getProperty(Object o, String prop)
+    throws NoSuchMethodException, IllegalAccessException, InvocationTargetException
+  {
+    // Use the event object when the property name to extract is null.
+    if (prop == null)
+      return new Object[] {o, o.getClass()};
+
+    // Isolate the first property name from a.b.c.
+    int pos;
+    String rest = null;
+    if ((pos = prop.indexOf('.')) != -1)
+      {
+	rest = prop.substring(pos + 1);
+	prop = prop.substring(0, pos);
+      }
+
+    // Find a method named getProp.  It could be isProp instead.
+    Method getter;
+    try
+      {
+	// Look for boolean property getter isProperty
+	getter = o.getClass().getMethod("is" + capitalize(prop),
+						 null);
+      }
+    catch (NoSuchMethodException e)
+      {
+	// Look for regular property getter getProperty
+	getter = o.getClass().getMethod("get" + capitalize(prop),
+						 null);
+      }
+    Object val = getter.invoke(o, null);
+
+    if (rest != null)
+      return getProperty(val, rest);
+
+    return new Object[] {val, getter.getReturnType()};
+  }
+
+
+  /**
+   * Invoke the event handler.
+   *
+   * Proxy is the object that was used, method is the method that was invoked
+   * on object, and arguments is the set of arguments passed to this method.
+   * We assume that the first argument is the event to extract a property
+   * from.
+   *
+   * Assuming that method matches the listener method specified when creating
+   * this EventHandler, the desired property is extracted from this argument.
+   * The property is passed to target.setAction(), if possible.  Otherwise
+   * target.action() is called, where action is the string fed to the
+   * constructor.
+   *
+   * For now we punt on indexed properties.  Sun docs are not clear to me
+   * about this.
+   *
+   * @param proxy The proxy object that had method invoked on it.
+   * @param method The method that was invoked.
+   * @param arguments Arguments to method.
+   * @return Result of invoking target.action on the event property
+   */
+  public Object invoke(Object proxy, Method method, Object[] arguments)
+    throws Exception
+  {
+    // Do we actually need the proxy?
+    if (method == null)
+      throw new RuntimeException("Invoking null method");
+
+    // Listener methods that weren't specified are ignored.  If listenerMethod
+    // is null, then all listener methods are processed.
+    if (listenerMethod != null && !method.getName().equals(listenerMethod))
+      return null;
+
+    // Extract the first arg from arguments and do getProperty on arg
+    if (arguments == null || arguments.length == 0)
+      return null;
+    Object event = arguments[0]; // We hope :-)
+
+    // Obtain the property XXX propertyType keeps showing up null - why?
+    // because the object inside getProperty changes, but the ref variable
+    // can't change this way, dolt!  need a better way to get both values out
+    // - need method and object to do the invoke and get return type
+    Object v[] = getProperty(event, property);
+    Object val = v[0];
+    Class propertyType = (Class) v[1];
+
+    // Find the actual method of target to invoke.  We can't do this in the
+    // constructor since we don't know the type of the property we extracted
+    // from the event then.
+    //
+    // action can be either a property or a method.  Sun's docs seem to imply
+    // that action should be treated as a property first, and then a method,
+    // but don't specifically say it.
+    //
+    // XXX check what happens with native type wrappers.  The better thing to
+    // do is look at the return type of the method
+    Method actionMethod;
+    try
+      {
+	// Look for a property setter for action.
+	actionMethod = 
+	  target.getClass().getMethod("set" + capitalize(action),
+				      new Class[] {propertyType});
+      }
+    catch (NoSuchMethodException e)
+      {
+	// If action as property didn't work, try as method.
+	try
+	  {
+	    actionMethod = 
+	      target.getClass().getMethod(action, new Class[] {propertyType});
+	  }
+	catch (NoSuchMethodException e1)
+	  {
+	    // When event property is null, we may call action with no args
+	    if (property == null)
+	      {
+		actionMethod =
+		  target.getClass().getMethod(action, null);
+		return actionMethod.invoke(target, null);
+	      }
+	    else
+	      throw e1;
+	  }
+      }
+
+    // Invoke target.action(property)
+    return actionMethod.invoke(target, new Object[] {val});
+  }
+
+  /**
+   * Construct a new object to dispatch events.
+   *
+   * Equivalent to:
+   * create(listenerInterface, target, action, null, null)
+   *
+   * I.e. all listenerInterface methods are mapped to
+   * target.action(EventObject) or target.action(), if the first doesn't
+   * exist.
+   *
+   * @param listenerInterface Listener interface to implement.
+   * @param target Object to invoke action on.
+   * @param action Target property or method to invoke.
+   * @return A constructed proxy object.
+   */
+  public static Object create(Class listenerInterface, Object target, String action)
+  {
+    return create(listenerInterface, target, action, null, null);
+  }
+
+  /**
+   * Construct a new object to dispatch events.
+   *
+   * Equivalent to:
+   * create(listenerInterface, target, action, eventPropertyName, null)
+   *
+   * I.e. all listenerInterface methods are mapped to
+   * target.action(event.getEventPropertyName)
+   * 
+   *
+   * @param listenerInterface Listener interface to implement.
+   * @param target Object to invoke action on.
+   * @param action Target property or method to invoke.
+   * @param eventPropertyName Name of property to extract from event.
+   * @return A constructed proxy object.
+   */
+  public static Object create(Class listenerInterface, Object target,
+			      String action, String eventPropertyName)
+  {
+    return create(listenerInterface, target, action, eventPropertyName, null);
+  }
+
+
+  /**
+   * Construct a new object to dispatch events.
+   *
+   * This creates an object that acts as a proxy for the method
+   * listenerMethodName in listenerInterface.  When the listener method is
+   * activated, the object extracts eventPropertyName from the event.  Then it
+   * passes the property to the method target.setAction, or target.action if
+   * action is not a property with a setter.
+   *
+   * For example, EventHandler.create(MouseListener.class, test, "pushed",
+   * "button", "mouseClicked") generates a proxy object that implements
+   * MouseListener, at least for the method mouseClicked().  The other methods
+   * of MouseListener are null operations.  When mouseClicked is invoked, the
+   * generated object extracts the button property from the MouseEvent,
+   * i.e. event.getButton(), and calls test.setPushed() with the result.  So under
+   * the covers the following happens:
+   *
+   * <CODE>
+   * object.mouseClicked(MouseEvent e) { test.setPushed(e.getButton()); }
+   * </CODE>
+   *
+   * The Sun spec specifies a hierarchical property naming scheme.  Generally
+   * if the property is a.b.c, this corresponds to event.getA().getB().getC()
+   * or event.getA().getB().isC().  I don't see how you specify an indexed
+   * property, though.  This may be a limitation of the Sun implementation as
+   * well.  The spec doesn't seem to address it.
+   * 
+   * If eventPropertyName is null, EventHandler instead uses the event object
+   * in place of a property, i.e. it calls target.action(EventObject).  If
+   * there is no method named action taking an EventObject argument,
+   * EventHandler looks for a method target.action() taking no arguments.
+   *
+   * If listenerMethodName is null, every method in listenerInterface gets
+   * mapped to target.action, rather than the specified listener method.
+   * 
+   * @param listenerInterface Listener interface to implement.
+   * @param target Object to invoke action on.
+   * @param action Target method name to invoke.
+   * @param eventPropertyName Name of property to extract from event.
+   * @param listenerMethodName Listener method to implement.
+   * @return A constructed proxy object.
+   */
+  public static Object create(Class listenerInterface, Object target,
+			      String action, String eventPropertyName,
+			      String listenerMethodName)
+  {
+    // Create EventHandler instance
+    EventHandler eh = new EventHandler(target, action, eventPropertyName,
+				       listenerMethodName);
+
+    // Create proxy object passing in the event handler
+    Object proxy = Proxy.newProxyInstance(listenerInterface.getClassLoader(),
+					  new Class[] {listenerInterface},
+					  eh);
+
+    return proxy;
+  }
+
+}




More information about the kaffe mailing list