1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.lang3.reflect;
18
19 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
20 import static org.junit.jupiter.api.Assertions.assertEquals;
21 import static org.junit.jupiter.api.Assertions.assertNotNull;
22 import static org.junit.jupiter.api.Assertions.assertNull;
23 import static org.junit.jupiter.api.Assertions.assertThrows;
24
25 import java.lang.reflect.Constructor;
26 import java.util.Arrays;
27 import java.util.HashMap;
28 import java.util.Map;
29
30 import org.apache.commons.lang3.AbstractLangTest;
31 import org.apache.commons.lang3.ArrayUtils;
32 import org.apache.commons.lang3.math.NumberUtils;
33 import org.apache.commons.lang3.mutable.MutableObject;
34 import org.junit.jupiter.api.BeforeEach;
35 import org.junit.jupiter.api.Test;
36
37
38
39
40
41
42
43 public class ConstructorUtilsTest extends AbstractLangTest {
44
45 private static class BaseClass {
46 }
47
48 static class PrivateClass {
49 @SuppressWarnings("unused")
50 public static class PublicInnerClass {
51 public PublicInnerClass() {
52 }
53 }
54
55 private final String value;
56
57 @SuppressWarnings("unused")
58 public PrivateClass() {
59 this("default");
60 }
61
62 public PrivateClass(final Number number) {
63 this.value = String.valueOf(number);
64 }
65
66 public PrivateClass(final String value) {
67 this.value = value;
68 }
69
70 public String getValue() {
71 return value;
72 }
73 }
74
75 private static final class SubClass extends BaseClass {
76 }
77
78 public static class TestBean {
79 private final String toString;
80 final String[] varArgs;
81
82 public TestBean() {
83 toString = "()";
84 varArgs = null;
85 }
86
87 public TestBean(final BaseClass bc, final String... s) {
88 toString = "(BaseClass, String...)";
89 varArgs = s;
90 }
91
92 public TestBean(final double d) {
93 toString = "(double)";
94 varArgs = null;
95 }
96
97 public TestBean(final int i) {
98 toString = "(int)";
99 varArgs = null;
100 }
101
102 public TestBean(final Integer i) {
103 toString = "(Integer)";
104 varArgs = null;
105 }
106
107 public TestBean(final Integer first, final int... args) {
108 toString = "(Integer, String...)";
109 varArgs = new String[args.length];
110 for (int i = 0; i < args.length; ++i) {
111 varArgs[i] = Integer.toString(args[i]);
112 }
113 }
114
115 public TestBean(final Integer i, final String... s) {
116 toString = "(Integer, String...)";
117 varArgs = s;
118 }
119
120 public TestBean(final Object o) {
121 toString = "(Object)";
122 varArgs = null;
123 }
124
125 public TestBean(final String s) {
126 toString = "(String)";
127 varArgs = null;
128 }
129
130 public TestBean(final String... s) {
131 toString = "(String...)";
132 varArgs = s;
133 }
134
135 @Override
136 public String toString() {
137 return toString;
138 }
139
140 void verify(final String str, final String[] args) {
141 assertEquals(str, toString);
142 assertArrayEquals(args, varArgs);
143 }
144 }
145
146 private final Map<Class<?>, Class<?>[]> classCache;
147
148 public ConstructorUtilsTest() {
149 classCache = new HashMap<>();
150 }
151
152 private void expectMatchingAccessibleConstructorParameterTypes(final Class<?> cls, final Class<?>[] requestTypes, final Class<?>[] actualTypes) {
153 final Constructor<?> c = ConstructorUtils.getMatchingAccessibleConstructor(cls, requestTypes);
154 assertArrayEquals(actualTypes, c.getParameterTypes(), toString(c.getParameterTypes()) + " not equals " + toString(actualTypes));
155 }
156
157 @BeforeEach
158 public void setUp() {
159 classCache.clear();
160 }
161
162 private Class<?>[] singletonArray(final Class<?> c) {
163 Class<?>[] result = classCache.get(c);
164 if (result == null) {
165 result = new Class[] { c };
166 classCache.put(c, result);
167 }
168 return result;
169 }
170
171 @Test
172 void testConstructor() throws Exception {
173 assertNotNull(MethodUtils.class.getConstructor().newInstance());
174 }
175
176 @Test
177 void testGetAccessibleConstructor() throws Exception {
178 assertNotNull(ConstructorUtils.getAccessibleConstructor(Object.class.getConstructor(ArrayUtils.EMPTY_CLASS_ARRAY)));
179 assertNull(ConstructorUtils.getAccessibleConstructor(PrivateClass.class.getConstructor(ArrayUtils.EMPTY_CLASS_ARRAY)));
180 assertNull(ConstructorUtils.getAccessibleConstructor(PrivateClass.PublicInnerClass.class));
181 }
182
183 @Test
184 void testGetAccessibleConstructorFromDescription() {
185 assertNotNull(ConstructorUtils.getAccessibleConstructor(Object.class, ArrayUtils.EMPTY_CLASS_ARRAY));
186 assertNull(ConstructorUtils.getAccessibleConstructor(PrivateClass.class, ArrayUtils.EMPTY_CLASS_ARRAY));
187 }
188
189 @Test
190 void testGetMatchingAccessibleConstructorOnNonPublicClass() {
191 assertNull(ConstructorUtils.getMatchingAccessibleConstructor(PrivateClass.class));
192 assertNull(ConstructorUtils.getMatchingAccessibleConstructor(PrivateClass.class, String.class));
193 assertNull(ConstructorUtils.getMatchingAccessibleConstructor(PrivateClass.class, Integer.class));
194 assertNull(ConstructorUtils.getMatchingAccessibleConstructor(PrivateClass.PublicInnerClass.class));
195 }
196
197 @Test
198 void testGetMatchingAccessibleMethod() {
199 expectMatchingAccessibleConstructorParameterTypes(TestBean.class, ArrayUtils.EMPTY_CLASS_ARRAY, ArrayUtils.EMPTY_CLASS_ARRAY);
200 expectMatchingAccessibleConstructorParameterTypes(TestBean.class, null, ArrayUtils.EMPTY_CLASS_ARRAY);
201 expectMatchingAccessibleConstructorParameterTypes(TestBean.class, singletonArray(String.class), singletonArray(String.class));
202 expectMatchingAccessibleConstructorParameterTypes(TestBean.class, singletonArray(Object.class), singletonArray(Object.class));
203 expectMatchingAccessibleConstructorParameterTypes(TestBean.class, singletonArray(Boolean.class), singletonArray(Object.class));
204 expectMatchingAccessibleConstructorParameterTypes(TestBean.class, singletonArray(Byte.class), singletonArray(Integer.TYPE));
205 expectMatchingAccessibleConstructorParameterTypes(TestBean.class, singletonArray(Byte.TYPE), singletonArray(Integer.TYPE));
206 expectMatchingAccessibleConstructorParameterTypes(TestBean.class, singletonArray(Short.class), singletonArray(Integer.TYPE));
207 expectMatchingAccessibleConstructorParameterTypes(TestBean.class, singletonArray(Short.TYPE), singletonArray(Integer.TYPE));
208 expectMatchingAccessibleConstructorParameterTypes(TestBean.class, singletonArray(Character.class), singletonArray(Integer.TYPE));
209 expectMatchingAccessibleConstructorParameterTypes(TestBean.class, singletonArray(Character.TYPE), singletonArray(Integer.TYPE));
210 expectMatchingAccessibleConstructorParameterTypes(TestBean.class, singletonArray(Integer.class), singletonArray(Integer.class));
211 expectMatchingAccessibleConstructorParameterTypes(TestBean.class, singletonArray(Integer.TYPE), singletonArray(Integer.TYPE));
212 expectMatchingAccessibleConstructorParameterTypes(TestBean.class, singletonArray(Long.class), singletonArray(Double.TYPE));
213 expectMatchingAccessibleConstructorParameterTypes(TestBean.class, singletonArray(Long.TYPE), singletonArray(Double.TYPE));
214 expectMatchingAccessibleConstructorParameterTypes(TestBean.class, singletonArray(Float.class), singletonArray(Double.TYPE));
215 expectMatchingAccessibleConstructorParameterTypes(TestBean.class, singletonArray(Float.TYPE), singletonArray(Double.TYPE));
216 expectMatchingAccessibleConstructorParameterTypes(TestBean.class, singletonArray(Double.class), singletonArray(Double.TYPE));
217 expectMatchingAccessibleConstructorParameterTypes(TestBean.class, singletonArray(Double.TYPE), singletonArray(Double.TYPE));
218 expectMatchingAccessibleConstructorParameterTypes(TestBean.class, new Class<?>[] { SubClass.class, String[].class },
219 new Class<?>[] { BaseClass.class, String[].class });
220 }
221
222 @Test
223 void testInvokeConstructor() throws Exception {
224 assertEquals("()", ConstructorUtils.invokeConstructor(TestBean.class, (Object[]) ArrayUtils.EMPTY_CLASS_ARRAY).toString());
225 assertEquals("()", ConstructorUtils.invokeConstructor(TestBean.class, (Object[]) null).toString());
226 assertEquals("()", ConstructorUtils.invokeConstructor(TestBean.class).toString());
227 assertEquals("(String)", ConstructorUtils.invokeConstructor(TestBean.class, "").toString());
228 assertEquals("(Object)", ConstructorUtils.invokeConstructor(TestBean.class, new Object()).toString());
229 assertEquals("(Object)", ConstructorUtils.invokeConstructor(TestBean.class, Boolean.TRUE).toString());
230 assertEquals("(Integer)", ConstructorUtils.invokeConstructor(TestBean.class, NumberUtils.INTEGER_ONE).toString());
231 assertEquals("(int)", ConstructorUtils.invokeConstructor(TestBean.class, NumberUtils.BYTE_ONE).toString());
232 assertEquals("(double)", ConstructorUtils.invokeConstructor(TestBean.class, NumberUtils.LONG_ONE).toString());
233 assertEquals("(double)", ConstructorUtils.invokeConstructor(TestBean.class, NumberUtils.DOUBLE_ONE).toString());
234 ConstructorUtils.invokeConstructor(TestBean.class, NumberUtils.INTEGER_ONE).verify("(Integer)", null);
235 ConstructorUtils.invokeConstructor(TestBean.class, "a", "b").verify("(String...)", new String[] { "a", "b" });
236 ConstructorUtils.invokeConstructor(TestBean.class, NumberUtils.INTEGER_ONE, "a", "b").verify("(Integer, String...)", new String[] { "a", "b" });
237 ConstructorUtils.invokeConstructor(TestBean.class, new SubClass(), new String[] { "a", "b" }).verify("(BaseClass, String...)",
238 new String[] { "a", "b" });
239 }
240
241 @Test
242 void testInvokeConstructorOnNonPublicClass() {
243 assertThrows(NoSuchMethodException.class, () -> ConstructorUtils.invokeConstructor(PrivateClass.class));
244 assertThrows(NoSuchMethodException.class, () -> ConstructorUtils.invokeConstructor(PrivateClass.class, "test"));
245 assertThrows(NoSuchMethodException.class, () -> ConstructorUtils.invokeConstructor(PrivateClass.class, Integer.valueOf(1)));
246 assertThrows(NoSuchMethodException.class, () -> ConstructorUtils.invokeConstructor(PrivateClass.PublicInnerClass.class));
247 }
248
249 @Test
250 void testInvokeExactConstructor() throws Exception {
251 assertEquals("()", ConstructorUtils.invokeExactConstructor(TestBean.class, (Object[]) ArrayUtils.EMPTY_CLASS_ARRAY).toString());
252 assertEquals("()", ConstructorUtils.invokeExactConstructor(TestBean.class, (Object[]) null).toString());
253 assertEquals("(String)", ConstructorUtils.invokeExactConstructor(TestBean.class, "").toString());
254 assertEquals("(Object)", ConstructorUtils.invokeExactConstructor(TestBean.class, new Object()).toString());
255 assertEquals("(Integer)", ConstructorUtils.invokeExactConstructor(TestBean.class, NumberUtils.INTEGER_ONE).toString());
256 assertEquals("(double)",
257 ConstructorUtils.invokeExactConstructor(TestBean.class, new Object[] { NumberUtils.DOUBLE_ONE }, new Class[] { Double.TYPE }).toString());
258
259 assertThrows(NoSuchMethodException.class, () -> ConstructorUtils.invokeExactConstructor(TestBean.class, NumberUtils.BYTE_ONE));
260 assertThrows(NoSuchMethodException.class, () -> ConstructorUtils.invokeExactConstructor(TestBean.class, NumberUtils.LONG_ONE));
261 assertThrows(NoSuchMethodException.class, () -> ConstructorUtils.invokeExactConstructor(TestBean.class, Boolean.TRUE));
262 }
263
264 @Test
265 void testInvokeExactConstructorOnNonPublicClass() {
266 assertThrows(NoSuchMethodException.class, () -> ConstructorUtils.invokeExactConstructor(PrivateClass.class));
267 assertThrows(NoSuchMethodException.class, () -> ConstructorUtils.invokeExactConstructor(PrivateClass.class, "test"));
268 assertThrows(NoSuchMethodException.class, () -> ConstructorUtils.invokeExactConstructor(PrivateClass.PublicInnerClass.class));
269 }
270
271 @Test
272 void testNullArgument() {
273 expectMatchingAccessibleConstructorParameterTypes(MutableObject.class, singletonArray(null), singletonArray(Object.class));
274 }
275
276 @Test
277 void testVarArgsUnboxing() throws Exception {
278 final TestBean testBean = ConstructorUtils.invokeConstructor(TestBean.class, Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3));
279
280 assertArrayEquals(new String[] { "2", "3" }, testBean.varArgs);
281 }
282
283 private String toString(final Class<?>[] c) {
284 return Arrays.asList(c).toString();
285 }
286
287 }