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