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