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