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