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