[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