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.Constructor;
20 import java.lang.reflect.InvocationTargetException;
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 * <strong>WARNING:</strong> from v4.1 onwards this class will <strong>not</strong> 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 * @param <T> The type of the input and result to the function.
35 * @since 3.0
36 */
37 public class InstantiateTransformer<T> implements Transformer<Class<? extends T>, T> {
38
39 /** Singleton instance that uses the no arg constructor */
40 @SuppressWarnings("rawtypes")
41 private static final Transformer NO_ARG_INSTANCE = new InstantiateTransformer<>();
42
43 /**
44 * Gets a typed no-arg instance.
45 *
46 * @param <T> the type of the objects to be created
47 * @return Transformer<Class<? extends T>, T>
48 */
49 public static <T> Transformer<Class<? extends T>, T> instantiateTransformer() {
50 return NO_ARG_INSTANCE;
51 }
52
53 /**
54 * Transformer method that performs validation.
55 *
56 * @param <T> the type of the objects to be created
57 * @param paramTypes The constructor parameter types
58 * @param args The constructor arguments
59 * @return An instantiate transformer
60 * @throws IllegalArgumentException if paramTypes does not match args
61 */
62 public static <T> Transformer<Class<? extends T>, T> instantiateTransformer(final Class<?>[] paramTypes,
63 final Object[] args) {
64 if (paramTypes == null && args != null
65 || paramTypes != null && args == null
66 || paramTypes != null && args != null && paramTypes.length != args.length) {
67 throw new IllegalArgumentException("Parameter types must match the arguments");
68 }
69
70 if (paramTypes == null || paramTypes.length == 0) {
71 return new InstantiateTransformer<>();
72 }
73 return new InstantiateTransformer<>(paramTypes, args);
74 }
75
76 /** The constructor parameter types */
77 private final Class<?>[] iParamTypes;
78
79 /** The constructor arguments */
80 private final Object[] iArgs;
81
82 /**
83 * Constructor for no arg instance.
84 */
85 private InstantiateTransformer() {
86 iParamTypes = null;
87 iArgs = null;
88 }
89
90 /**
91 * Constructor that performs no validation.
92 * Use {@code instantiateTransformer} if you want that.
93 * <p>
94 * Note: from 4.0, the input parameters will be cloned
95 *
96 * @param paramTypes The constructor parameter types
97 * @param args The constructor arguments
98 */
99 public InstantiateTransformer(final Class<?>[] paramTypes, final Object[] args) {
100 iParamTypes = paramTypes != null ? paramTypes.clone() : null;
101 iArgs = args != null ? args.clone() : null;
102 }
103
104 /**
105 * Transforms the input Class object to a result by instantiation.
106 *
107 * @param input The input object to transform
108 * @return The transformed result
109 */
110 @Override
111 public T transform(final Class<? extends T> input) {
112 try {
113 if (input == null) {
114 throw new FunctorException(
115 "InstantiateTransformer: Input object was not an instanceof Class, it was a null object");
116 }
117 final Constructor<? extends T> con = input.getConstructor(iParamTypes);
118 return con.newInstance(iArgs);
119 } catch (final NoSuchMethodException ex) {
120 throw new FunctorException("InstantiateTransformer: The constructor must exist and be public ");
121 } catch (final InstantiationException ex) {
122 throw new FunctorException("InstantiateTransformer: InstantiationException", ex);
123 } catch (final IllegalAccessException ex) {
124 throw new FunctorException("InstantiateTransformer: Constructor must be public", ex);
125 } catch (final InvocationTargetException ex) {
126 throw new FunctorException("InstantiateTransformer: Constructor threw an exception", ex);
127 }
128 }
129
130 }