    001    /*
    002     * Licensed to the Apache Software Foundation (ASF) under one or more
    003     * contributor license agreements.  See the NOTICE file distributed with
    004     * this work for additional information regarding copyright ownership.
    005     * The ASF licenses this file to You under the Apache License, Version 2.0
    006     * (the "License"); you may not use this file except in compliance with
    007     * the License.  You may obtain a copy of the License at
    008     *
    009     *      http://www.apache.org/licenses/LICENSE-2.0
    010     *
    011     * Unless required by applicable law or agreed to in writing, software
    012     * distributed under the License is distributed on an "AS IS" BASIS,
    013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014     * See the License for the specific language governing permissions and
    015     * limitations under the License.
    016     */
    017
    018    package org.apache.commons.lang3.event;
    019
    020    import org.apache.commons.lang3.reflect.MethodUtils;
    021
    022    import java.lang.reflect.InvocationHandler;
    023    import java.lang.reflect.InvocationTargetException;
    024    import java.lang.reflect.Method;
    025    import java.lang.reflect.Proxy;
    026    import java.util.Arrays;
    027    import java.util.HashSet;
    028    import java.util.Set;
    029
    030    /**
    031     * Provides some useful event-based utility methods.
    032     *
    033     * @since 3.0
    034     * @version $Id: EventUtils.java 967237 2010-07-23 20:08:57Z mbenson $
    035     */
    036    public class EventUtils
    037    {
    038        /**
    039         * Adds an event listener to the specified source.  This looks for an "add" method corresponding to the event
    040         * type (addActionListener, for example).
    041         * @param eventSource   the event source
    042         * @param listenerType  the event listener type
    043         * @param listener      the listener
    044         * @param <L>           the event listener type
    045         *
    046         * @throws IllegalArgumentException if the object doesn't support the listener type
    047         */
    048        public static <L> void addEventListener(Object eventSource, Class<L> listenerType, L listener)
    049        {
    050            try
    051            {
    052                MethodUtils.invokeMethod(eventSource, "add" + listenerType.getSimpleName(), listener);
    053            }
    054            catch (NoSuchMethodException e)
    055            {
    056                throw new IllegalArgumentException("Class " + eventSource.getClass().getName() + " does not have a public add" + listenerType.getSimpleName() + " method which takes a parameter of type " + listenerType.getName() + ".");
    057            }
    058            catch (IllegalAccessException e)
    059            {
    060                throw new IllegalArgumentException("Class " + eventSource.getClass().getName() + " does not have an accessible add" + listenerType.getSimpleName () + " method which takes a parameter of type " + listenerType.getName() + ".");
    061            }
    062            catch (InvocationTargetException e)
    063            {
    064                throw new RuntimeException("Unable to add listener.", e.getCause());
    065            }
    066        }
    067
    068        /**
    069         * Binds an event listener to a specific method on a specific object.
    070         *
    071         * @param <L>          the event listener type
    072         * @param target       the target object
    073         * @param methodName   the name of the method to be called
    074         * @param eventSource  the object which is generating events (JButton, JList, etc.)
    075         * @param listenerType the listener interface (ActionListener.class, SelectionListener.class, etc.)
    076         * @param eventTypes   the event types (method names) from the listener interface (if none specified, all will be
    077         *                     supported)
    078         */
    079        public static <L> void bindEventsToMethod(Object target, String methodName, Object eventSource, Class<L> listenerType, String... eventTypes)
    080        {
    081            final L listener = listenerType.cast(Proxy.newProxyInstance(target.getClass().getClassLoader(), new Class[] { listenerType }, new EventBindingInvocationHandler(target, methodName, eventTypes)));
    082            addEventListener(eventSource, listenerType, listener);
    083        }
    084
    085        private static class EventBindingInvocationHandler implements InvocationHandler
    086        {
    087            private final Object target;
    088            private final String methodName;
    089            private final Set<String> eventTypes;
    090
    091            public EventBindingInvocationHandler(final Object target, final String methodName, String[] eventTypes)
    092            {
    093                this.target = target;
    094                this.methodName = methodName;
    095                this.eventTypes = new HashSet<String>(Arrays.asList(eventTypes));
    096            }
    097
    098            public Object invoke(final Object proxy, final Method method, final Object[] parameters) throws Throwable
    099            {
    100                if ( eventTypes.isEmpty() || eventTypes.contains(method.getName()))
    101                {
    102                    if (hasMatchingParametersMethod(method))
    103                    {
    104                        return MethodUtils.invokeMethod(target, methodName, parameters);
    105                    }
    106                    else
    107                    {
    108                        return MethodUtils.invokeMethod(target, methodName);
    109                    }
    110                }
    111                return null;
    112            }
    113
    114            private boolean hasMatchingParametersMethod(final Method method)
    115            {
    116                return MethodUtils.getAccessibleMethod(target.getClass(), methodName, method.getParameterTypes()) != null;
    117            }
    118        }
    119    }