Home | History | Annotate | Download | only in instance
      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 
     16     public ConstructorInstantiator(Object outerClassInstance) {
     17         this.outerClassInstance = outerClassInstance;
     18     }
     19 
     20     public <T> T newInstance(Class<T> cls) {
     21         if (outerClassInstance == null) {
     22             return noArgConstructor(cls);
     23         }
     24         return withParams(cls, outerClassInstance);
     25     }
     26 
     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")
     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 
     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 
     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 
     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 
     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