View Javadoc
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    *      http://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.collections4.functors;
18  
19  import java.lang.reflect.InvocationTargetException;
20  import java.lang.reflect.Method;
21  
22  import org.apache.commons.collections4.FunctorException;
23  import org.apache.commons.collections4.Transformer;
24  
25  /**
26   * Transformer implementation that creates a new object instance by reflection.
27   * <p>
28   * <b>WARNING:</b> from v4.1 onwards this class will <b>not</b> be serializable anymore
29   * in order to prevent potential remote code execution exploits. Please refer to
30   * <a href="https://issues.apache.org/jira/browse/COLLECTIONS-580">COLLECTIONS-580</a>
31   * for more details.
32   * </p>
33   *
34   * @since 3.0
35   */
36  public class InvokerTransformer<I, O> implements Transformer<I, O> {
37  
38      /** The method name to call */
39      private final String iMethodName;
40      /** The array of reflection parameter types */
41      private final Class<?>[] iParamTypes;
42      /** The array of reflection arguments */
43      private final Object[] iArgs;
44  
45      /**
46       * Gets an instance of this transformer calling a specific method with no arguments.
47       *
48       * @param <I>  the input type
49       * @param <O>  the output type
50       * @param methodName  the method name to call
51       * @return an invoker transformer
52       * @throws NullPointerException if methodName is null
53       * @since 3.1
54       */
55      public static <I, O> Transformer<I, O> invokerTransformer(final String methodName) {
56          if (methodName == null) {
57              throw new NullPointerException("The method to invoke must not be null");
58          }
59          return new InvokerTransformer<>(methodName);
60      }
61  
62      /**
63       * Gets an instance of this transformer calling a specific method with specific values.
64       *
65       * @param <I>  the input type
66       * @param <O>  the output type
67       * @param methodName  the method name to call
68       * @param paramTypes  the parameter types of the method
69       * @param args  the arguments to pass to the method
70       * @return an invoker transformer
71       * @throws NullPointerException if methodName is null
72       * @throws IllegalArgumentException if paramTypes does not match args
73       */
74      public static <I, O> Transformer<I, O> invokerTransformer(final String methodName, final Class<?>[] paramTypes,
75                                                                final Object[] args) {
76          if (methodName == null) {
77              throw new NullPointerException("The method to invoke must not be null");
78          }
79          if (((paramTypes == null) && (args != null))
80              || ((paramTypes != null) && (args == null))
81              || ((paramTypes != null) && (args != null) && (paramTypes.length != args.length))) {
82              throw new IllegalArgumentException("The parameter types must match the arguments");
83          }
84          if (paramTypes == null || paramTypes.length == 0) {
85              return new InvokerTransformer<>(methodName);
86          }
87          return new InvokerTransformer<>(methodName, paramTypes, args);
88      }
89  
90      /**
91       * Constructor for no arg instance.
92       *
93       * @param methodName  the method to call
94       */
95      private InvokerTransformer(final String methodName) {
96          super();
97          iMethodName = methodName;
98          iParamTypes = null;
99          iArgs = null;
100     }
101 
102     /**
103      * Constructor that performs no validation.
104      * Use <code>invokerTransformer</code> if you want that.
105      * <p>
106      * Note: from 4.0, the input parameters will be cloned
107      *
108      * @param methodName  the method to call
109      * @param paramTypes  the constructor parameter types
110      * @param args  the constructor arguments
111      */
112     public InvokerTransformer(final String methodName, final Class<?>[] paramTypes, final Object[] args) {
113         super();
114         iMethodName = methodName;
115         iParamTypes = paramTypes != null ? paramTypes.clone() : null;
116         iArgs = args != null ? args.clone() : null;
117     }
118 
119     /**
120      * Transforms the input to result by invoking a method on the input.
121      *
122      * @param input  the input object to transform
123      * @return the transformed result, null if null input
124      */
125     @Override
126     @SuppressWarnings("unchecked")
127     public O transform(final Object input) {
128         if (input == null) {
129             return null;
130         }
131         try {
132             final Class<?> cls = input.getClass();
133             final Method method = cls.getMethod(iMethodName, iParamTypes);
134             return (O) method.invoke(input, iArgs);
135         } catch (final NoSuchMethodException ex) {
136             throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" +
137                                        input.getClass() + "' does not exist");
138         } catch (final IllegalAccessException ex) {
139             throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" +
140                                        input.getClass() + "' cannot be accessed");
141         } catch (final InvocationTargetException ex) {
142             throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" +
143                                        input.getClass() + "' threw an exception", ex);
144         }
145     }
146 
147 }