EventUtils.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      https://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. package org.apache.commons.lang3.event;

  18. import java.lang.reflect.InvocationHandler;
  19. import java.lang.reflect.Method;
  20. import java.lang.reflect.Proxy;
  21. import java.util.Arrays;
  22. import java.util.HashSet;
  23. import java.util.Set;

  24. import org.apache.commons.lang3.reflect.MethodUtils;

  25. /**
  26.  * Provides some useful event-based utility methods.
  27.  *
  28.  * @since 3.0
  29.  */
  30. public class EventUtils {

  31.     private static final class EventBindingInvocationHandler implements InvocationHandler {
  32.         private final Object target;
  33.         private final String methodName;
  34.         private final Set<String> eventTypes;

  35.         /**
  36.          * Creates a new instance of {@link EventBindingInvocationHandler}.
  37.          *
  38.          * @param target the target object for method invocations
  39.          * @param methodName the name of the method to be invoked
  40.          * @param eventTypes the names of the supported event types
  41.          */
  42.         EventBindingInvocationHandler(final Object target, final String methodName, final String[] eventTypes) {
  43.             this.target = target;
  44.             this.methodName = methodName;
  45.             this.eventTypes = new HashSet<>(Arrays.asList(eventTypes));
  46.         }

  47.         /**
  48.          * Checks whether a method for the passed in parameters can be found.
  49.          *
  50.          * @param method the listener method invoked
  51.          * @return a flag whether the parameters could be matched
  52.          */
  53.         private boolean hasMatchingParametersMethod(final Method method) {
  54.             return MethodUtils.getAccessibleMethod(target.getClass(), methodName, method.getParameterTypes()) != null;
  55.         }

  56.         /**
  57.          * Handles a method invocation on the proxy object.
  58.          *
  59.          * @param proxy the proxy instance
  60.          * @param method the method to be invoked
  61.          * @param parameters the parameters for the method invocation
  62.          * @return the result of the method call
  63.          * @throws SecurityException if an underlying accessible object's method denies the request.
  64.          * @see SecurityManager#checkPermission
  65.          * @throws Throwable if an error occurs
  66.          */
  67.         @Override
  68.         public Object invoke(final Object proxy, final Method method, final Object[] parameters) throws Throwable {
  69.             if (eventTypes.isEmpty() || eventTypes.contains(method.getName())) {
  70.                 if (hasMatchingParametersMethod(method)) {
  71.                     return MethodUtils.invokeMethod(target, methodName, parameters);
  72.                 }
  73.                 return MethodUtils.invokeMethod(target, methodName);
  74.             }
  75.             return null;
  76.         }
  77.     }

  78.     /**
  79.      * Adds an event listener to the specified source.  This looks for an "add" method corresponding to the event
  80.      * type (addActionListener, for example).
  81.      * @param eventSource   the event source
  82.      * @param listenerType  the event listener type
  83.      * @param listener      the listener
  84.      * @param <L>           the event listener type
  85.      * @throws IllegalArgumentException if the object doesn't support the listener type
  86.      */
  87.     public static <L> void addEventListener(final Object eventSource, final Class<L> listenerType, final L listener) {
  88.         try {
  89.             MethodUtils.invokeMethod(eventSource, "add" + listenerType.getSimpleName(), listener);
  90.         } catch (final ReflectiveOperationException e) {
  91.             throw new IllegalArgumentException("Unable to add listener for class " + eventSource.getClass().getName()
  92.                     + " and public add" + listenerType.getSimpleName()
  93.                     + " method which takes a parameter of type " + listenerType.getName() + ".");
  94.         }
  95.     }

  96.     /**
  97.      * Binds an event listener to a specific method on a specific object.
  98.      *
  99.      * @param <L>          the event listener type
  100.      * @param target       the target object
  101.      * @param methodName   the name of the method to be called
  102.      * @param eventSource  the object which is generating events (JButton, JList, etc.)
  103.      * @param listenerType the listener interface (ActionListener.class, SelectionListener.class, etc.)
  104.      * @param eventTypes   the event types (method names) from the listener interface (if none specified, all will be
  105.      *                     supported)
  106.      */
  107.     public static <L> void bindEventsToMethod(final Object target, final String methodName, final Object eventSource,
  108.             final Class<L> listenerType, final String... eventTypes) {
  109.         final L listener = listenerType.cast(Proxy.newProxyInstance(target.getClass().getClassLoader(),
  110.                 new Class[] { listenerType }, new EventBindingInvocationHandler(target, methodName, eventTypes)));
  111.         addEventListener(eventSource, listenerType, listener);
  112.     }

  113.     /**
  114.      * Make private in 4.0.
  115.      *
  116.      * @deprecated TODO Make private in 4.0.
  117.      */
  118.     @Deprecated
  119.     public EventUtils() {
  120.         // empty
  121.     }
  122. }