• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2016 Mockito contributors
3  * This program is made available under the terms of the MIT License.
4  */
5 package org.mockito.internal.creation.instance;
6 
7 import java.lang.reflect.Constructor;
8 import org.mockito.internal.util.reflection.AccessibilityChanger;
9 
10 import static org.mockito.internal.util.StringUtil.join;
11 
12 public class ConstructorInstantiator implements Instantiator {
13 
14     private final Object outerClassInstance;
15 
ConstructorInstantiator(Object outerClassInstance)16     public ConstructorInstantiator(Object outerClassInstance) {
17         this.outerClassInstance = outerClassInstance;
18     }
19 
newInstance(Class<T> cls)20     public <T> T newInstance(Class<T> cls) {
21         if (outerClassInstance == null) {
22             return noArgConstructor(cls);
23         }
24         return withParams(cls, outerClassInstance);
25     }
26 
withParams(Class<T> cls, Object... params)27     private static <T> T withParams(Class<T> cls, Object... params) {
28         try {
29             //this is kind of over-engineered because we don't need to support more params
30             //however, I know we will be needing it :)
31             for (Constructor<?> constructor : cls.getDeclaredConstructors()) {
32                 Class<?>[] types = constructor.getParameterTypes();
33                 if (paramsMatch(types, params)) {
34                     return invokeConstructor(constructor, params);
35                 }
36             }
37         } catch (Exception e) {
38             throw paramsException(cls, e);
39         }
40         throw noMatchingConstructor(cls);
41     }
42 
43     @SuppressWarnings("unchecked")
invokeConstructor(Constructor<?> constructor, Object... params)44     private static <T> T invokeConstructor(Constructor<?> constructor, Object... params) throws java.lang.InstantiationException, IllegalAccessException, java.lang.reflect.InvocationTargetException {
45         AccessibilityChanger accessibility = new AccessibilityChanger();
46         accessibility.enableAccess(constructor);
47         return (T) constructor.newInstance(params);
48     }
49 
paramsException(Class<T> cls, Exception cause)50     private static <T> InstantiationException paramsException(Class<T> cls, Exception cause) {
51         return new InstantiationException(
52                 join("Unable to create instance of '" + cls.getSimpleName() + "'.",
53                      "Please ensure that the outer instance has correct type and that the target class has 0-arg constructor."),
54                 cause);
55     }
56 
noMatchingConstructor(Class<T> cls)57     private static <T> InstantiationException noMatchingConstructor(Class<T> cls) {
58         return new InstantiationException(
59                 join("Unable to create instance of '" + cls.getSimpleName() + "'.",
60                      "Unable to find a matching 1-arg constructor for the outer instance.")
61                 , null);
62     }
63 
paramsMatch(Class<?>[] types, Object[] params)64     private static boolean paramsMatch(Class<?>[] types, Object[] params) {
65         if (params.length != types.length) {
66             return false;
67         }
68         for (int i = 0; i < params.length; i++) {
69             if (!types[i].isInstance(params[i])) {
70                 return false;
71             }
72         }
73         return true;
74     }
75 
noArgConstructor(Class<T> cls)76     private static <T> T noArgConstructor(Class<T> cls) {
77         try {
78             return invokeConstructor(cls.getDeclaredConstructor());
79         } catch (Throwable t) {
80             throw new InstantiationException(join(
81                     "Unable to create instance of '" + cls.getSimpleName() + "'.",
82                     "Please ensure it has 0-arg constructor which invokes cleanly."),
83                                              t);
84         }
85     }
86 }
87