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