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    *      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.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   * Tests ConstructorUtils
39   * <p>
40   * This is public for its own tests.
41   * </p>
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 }