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