From 554fd8c5195424bdbcabf5de30fdc183aba391bd Mon Sep 17 00:00:00 2001 From: upstream source tree Date: Sun, 15 Mar 2015 20:14:05 -0400 Subject: obtained gcc-4.6.4.tar.bz2 from upstream website; verified gcc-4.6.4.tar.bz2.sig; imported gcc-4.6.4 source tree from verified upstream tarball. downloading a git-generated archive based on the 'upstream' tag should provide you with a source tree that is binary identical to the one extracted from the above tarball. if you have obtained the source via the command 'git clone', however, do note that line-endings of files in your working directory might differ from line-endings of the respective files in the upstream repository. --- libjava/classpath/java/beans/EventHandler.java | 604 +++++++++++++++++++++++++ 1 file changed, 604 insertions(+) create mode 100644 libjava/classpath/java/beans/EventHandler.java (limited to 'libjava/classpath/java/beans/EventHandler.java') diff --git a/libjava/classpath/java/beans/EventHandler.java b/libjava/classpath/java/beans/EventHandler.java new file mode 100644 index 000000000..967ba8294 --- /dev/null +++ b/libjava/classpath/java/beans/EventHandler.java @@ -0,0 +1,604 @@ +/* java.beans.EventHandler + Copyright (C) 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 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.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +/** + *

EventHandler forms a bridge between dynamically created listeners and + * arbitrary properties and methods.

+ * + *

You can use this class to easily create listener implementations for + * some basic interactions between an event source and its target. Using + * the three static methods named create you can create + * these listener implementations.

+ * + *

See the documentation of each method for usage examples.

+ * + * @author Jerry Quinn (jlquinn@optonline.net) + * @author Robert Schuster (thebohemian@gmx.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; + + // The target objects Class. + private Class targetClass; + + // String class doesn't already have a capitalize routine. + private String capitalize(String s) + { + return s.substring(0, 1).toUpperCase() + s.substring(1); + } + + /** + * Creates a new EventHandler instance. + * + *

Typical creation is done with the create method, not by knewing 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.

+ * + *

Throws a NullPointerException if the target + * argument is null. + * + * @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; + + // Retrieving the class is done for two reasons: + // 1) The class object is needed very frequently in the invoke() method. + // 2) The constructor should throw a NullPointerException if target is null. + targetClass = target.getClass(); + + this.action = action; // Turn this into a method or do we wait till + // runtime + property = eventPropertyName; + listenerMethod = listenerMethodName; + } + + /** + * Returns the event property name. + */ + public String getEventPropertyName() + { + return property; + } + + /** + * Returns the listener's method name. + */ + public String getListenerMethodName() + { + return listenerMethod; + } + + /** + * Returns the target object. + */ + public Object getTarget() + { + return target; + } + + /** + * Returns 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) + { + // 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)); + } + catch (NoSuchMethodException nsme1) + { + try { + // Look for regular property getter getProperty + getter = o.getClass().getMethod("get" + capitalize(prop)); + } catch(NoSuchMethodException nsme2) { + try { + // Finally look for a method of the name prop + getter = o.getClass().getMethod(prop); + } catch(NoSuchMethodException nsme3) { + // Ok, give up with an intelligent hint for the user. + throw new RuntimeException("Method not called: Could not find a property or method '" + prop + + "' in " + o.getClass() + " while following the property argument '" + property + "'."); + } + } + } + try { + Object val = getter.invoke(o); + + if (rest != null) + return getProperty(val, rest); + + return new Object[] {val, getter.getReturnType()}; + } catch(InvocationTargetException ite) { + throw new RuntimeException("Method not called: Property or method '" + prop + "' has thrown an exception.", ite); + } catch(IllegalAccessException iae) { + // This cannot happen because we looked up method with Class.getMethod() + // which returns public methods only. + throw (InternalError) new InternalError("Non-public method was invoked.").initCause(iae); + } + } + + /** + * Invokes the EventHandler. + * + *

This method is normally called by the listener's proxy implementation.

+ * + * @param proxy The listener interface that is implemented using + * the proxy mechanism. + * @param method The method that was called on the proxy instance. + * @param arguments The arguments which where given to the method. + * @throws Throwable NoSuchMethodException is thrown when the EventHandler's + * action method or property cannot be found. + */ + public Object invoke(Object proxy, Method method, Object[] arguments) + { + try { + // The method instance of the target object. We have to find out which + // one we have to invoke. + Method actionMethod = null; + + // 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; + + // If a property is defined we definitely need a valid object at + // arguments[0] that can be used to retrieve a value to which the + // property of the target gets set. + if(property != null) { + // Extracts the argument. We will let it fail with a NullPointerException + // the caller used a listener method that has no arguments. + Object event = arguments[0]; + + // Obtains 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[] args = new Object[] { v[0] }; + + // Changes the class array that controls which method signature we are going + // to look up in the target object. + Class[] argTypes = new Class[] { initClass((Class) v[1]) }; + + // Tries to find a setter method to which we can apply the + while(argTypes[0] != null) { + try + { + // Look for a property setter for action. + actionMethod = targetClass.getMethod("set" + capitalize(action), argTypes); + + return actionMethod.invoke(target, args); + } + catch (NoSuchMethodException e) + { + // If action as property didn't work, try as method later. + } + + argTypes[0] = nextClass(argTypes[0]); + } + + // We could not find a suitable setter method. Now we try again interpreting + // action as the method name itself. + // Since we probably have changed the block local argTypes array + // we need to rebuild it. + argTypes = new Class[] { initClass((Class) v[1]) }; + + // Tries to find a setter method to which we can apply the + while(argTypes[0] != null) { + try + { + actionMethod = targetClass.getMethod(action, argTypes); + + return actionMethod.invoke(target, args); + } + catch (NoSuchMethodException e) + { + } + + argTypes[0] = nextClass(argTypes[0]); + } + + throw new RuntimeException("Method not called: Could not find a public method named '" + + action + "' in target " + targetClass + " which takes a '" + + v[1] + "' argument or a property of this type."); + } + + // If property was null we will search for a no-argument method here. + // Note: The ordering of method lookups is important because we want to prefer no-argument + // calls like the JDK does. This means if we have actionMethod() and actionMethod(Event) we will + // call the first *EVEN* if we have a valid argument for the second method. This is behavior compliant + // to the JDK. + // If actionMethod() is not available but there is a actionMethod(Event) we take this. That makes us + // more specification compliant than the JDK itself because this one will fail in such a case. + try + { + actionMethod = targetClass.getMethod(action); + } + catch(NoSuchMethodException nsme) + { + // Note: If we want to be really strict the specification says that a no-argument method should + // accept an EventObject (or subclass I guess). However since the official implementation is broken + // anyways, it's more flexible without the EventObject restriction and we are compatible on everything + // else this can stay this way. + if(arguments != null && arguments.length >= 1/* && arguments[0] instanceof EventObject*/) { + Class[] targetArgTypes = new Class[] { initClass(arguments[0].getClass()) }; + + while(targetArgTypes[0] != null) { + try + { + // If no property exists we expect the first element of the arguments to be + // an EventObject which is then applied to the target method. + + actionMethod = targetClass.getMethod(action, targetArgTypes); + + return actionMethod.invoke(target, new Object[] { arguments[0] }); + } + catch(NoSuchMethodException nsme2) + { + + } + + targetArgTypes[0] = nextClass(targetArgTypes[0]); + } + + } + } + + // If we do not have a Method instance at this point this means that all our tries + // failed. The JDK throws an ArrayIndexOutOfBoundsException in this case. + if(actionMethod == null) + throw new ArrayIndexOutOfBoundsException(0); + + // Invoke target.action(property) + return actionMethod.invoke(target); + } catch(InvocationTargetException ite) { + throw new RuntimeException(ite.getCause()); + } catch(IllegalAccessException iae) { + // Cannot happen because we always use getMethod() which returns public + // methods only. Otherwise there is something seriously broken in + // GNU Classpath. + throw (InternalError) new InternalError("Non-public method was invoked.").initCause(iae); + } + } + + /** + *

Returns the primitive type for every wrapper class or the + * class itself if it is no wrapper class.

+ * + *

This is needed because to be able to find both kinds of methods: + * One that takes a wrapper class as the first argument and one that + * accepts a primitive instead.

+ */ + private Class initClass(Class klass) { + if(klass == Boolean.class) { + return Boolean.TYPE; + } else if(klass == Byte.class) { + return Byte.TYPE; + } else if(klass == Short.class) { + return Short.TYPE; + } else if(klass == Integer.class) { + return Integer.TYPE; + } else if(klass == Long.class) { + return Long.TYPE; + } else if(klass == Float.class) { + return Float.TYPE; + } else if(klass == Double.class) { + return Double.TYPE; + } else { + return klass; + } + } + + /** + * + * + * @param klass + * @return + */ + private Class nextClass(Class klass) { + if(klass == Boolean.TYPE) { + return Boolean.class; + } else if(klass == Byte.TYPE) { + return Byte.class; + } else if(klass == Short.TYPE) { + return Short.class; + } else if(klass == Integer.TYPE) { + return Integer.class; + } else if(klass == Long.TYPE) { + return Long.class; + } else if(klass == Float.TYPE) { + return Float.class; + } else if(klass == Double.TYPE) { + return Double.class; + } else { + return klass.getSuperclass(); + } + } + + /** + *

Constructs an implementation of listenerInterface + * to dispatch events.

+ * + *

You can use such an implementation to simply call a public + * no-argument method of an arbitrary target object or to forward + * the first argument of the listener method to the target method.

+ * + *

Call this method like:

+ * + * button.addActionListener((ActionListener) + * EventHandler.create(ActionListener.class, target, "dispose")); + * + * + *

to achieve the following behavior:

+ * + * button.addActionListener(new ActionListener() { + * public void actionPerformed(ActionEvent ae) { + * target.dispose(); + * } + * }); + * + * + *

That means if you need a listener implementation that simply calls a + * a no-argument method on a given instance for each + * method of the listener interface.

+ * + *

Note: The action is interpreted as a method name. If your target object + * has no no-argument method of the given name the EventHandler tries to find + * a method with the same name but which can accept the first argument of the + * listener method. Usually this will be an event object but any other object + * will be forwarded, too. Keep in mind that using a property name instead of a + * real method here is wrong and will throw an ArrayIndexOutOfBoundsException + * whenever one of the listener methods is called.

+ * + *

The EventHandler will automatically convert primitives + * to their wrapper class and vice versa. Furthermore it will call + * a target method if it accepts a superclass of the type of the + * first argument of the listener method.

+ * + *

In case that the method of the target object throws an exception + * it will be wrapped in a RuntimeException and thrown out + * of the listener method.

+ * + *

In case that the method of the target object cannot be found an + * ArrayIndexOutOfBoundsException will be thrown when the + * listener method is invoked.

+ * + *

A call to this method is equivalent to: + * create(listenerInterface, target, action, null, null)

+ * + * @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 T create(Class listenerInterface, Object target, + String action) + { + return create(listenerInterface, target, action, null, null); + } + + /** + *

Constructs an implementation of listenerInterface + * to dispatch events.

+ * + *

Use this method if you want to create an implementation that retrieves + * a property value from the first argument of the listener method + * and applies it to the target's property or method. This first argument + * of the listener is usually an event object but any other object is + * valid, too.

+ * + *

You can set the value of eventPropertyName to "prop" + * to denote the retrieval of a property named "prop" from the event + * object. In case that no such property exists the EventHandler + * will try to find a method with that name.

+ * + *

If you set eventPropertyName to a value like this "a.b.c" + * EventHandler will recursively evaluate the properties "a", "b" + * and "c". Again if no property can be found the EventHandler + * tries a method name instead. This allows mixing the names, too: "a.toString" + * will retrieve the property "a" from the event object and will then call + * the method "toString" on it.

+ * + *

An exception thrown in any of these methods will provoke a + * RuntimeException to be thrown which contains an + * InvocationTargetException containing the triggering exception.

+ * + *

If you set eventPropertyName to a non-null value the + * action parameter will be interpreted as a property name + * or a method name of the target object.

+ * + *

Any object retrieved from the event object and applied to the + * target will converted from primitives to their wrapper class or + * vice versa or applied to a method that accepts a superclass + * of the object.

+ * + *

Examples:

+ *

The following code:

+ * button.addActionListener( + * new ActionListener() { + * public void actionPerformed(ActionEvent ae) { + * Object o = ae.getSource().getClass().getName(); + * textField.setText((String) o); + * } + * }); + * + * + *

Can be expressed using the EventHandler like this:

+ *

+ * button.addActionListener((ActionListener) + * EventHandler.create(ActionListener.class, textField, "text", "source.class.name"); + * + *

+ * + *

As said above you can specify the target as a method, too:

+ *

+ * button.addActionListener((ActionListener) + * EventHandler.create(ActionListener.class, textField, "setText", "source.class.name"); + * + *

+ * + *

Furthermore you can use method names in the property:

+ *

+ * button.addActionListener((ActionListener) + * EventHandler.create(ActionListener.class, textField, "setText", "getSource.getClass.getName"); + * + *

+ * + *

Finally you can mix names:

+ *

+ * button.addActionListener((ActionListener) + * EventHandler.create(ActionListener.class, textField, "setText", "source.getClass.name"); + * + *

+ * + *

A call to this method is equivalent to: + * create(listenerInterface, target, action, null, null) + *

+ * + * @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 T create(Class listenerInterface, Object target, + String action, String eventPropertyName) + { + return create(listenerInterface, target, action, eventPropertyName, null); + } + + /** + *

Constructs an implementation of listenerInterface + * to dispatch events.

+ * + *

Besides the functionality described for {@link create(Class, Object, String)} + * and {@link create(Class, Object, String, String)} this method allows you + * to filter the listener method that should have an effect. Look at these + * method's documentation for more information about the EventHandler's + * usage.

+ * + *

If you want to call dispose on a JFrame instance + * when the WindowListener.windowClosing() method was invoked use + * the following code:

+ *

+ * + * EventHandler.create(WindowListener.class, jframeInstance, "dispose", null, "windowClosing"); + * + *

+ * + *

A NullPointerException is thrown if the listenerInterface + * or target argument are null. + * + * @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 T 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 (T) proxy; + } +} -- cgit v1.2.3