blob: 049d1b8e3a45878cd06a0d24bbc225707e2eae4b [file] [log] [blame]
/*
* Copyright (c) 2016 Mockito contributors
* This program is made available under the terms of the MIT License.
*/
package org.mockito.internal.creation.instance;
import java.lang.reflect.Constructor;
import org.mockito.internal.util.reflection.AccessibilityChanger;
import static org.mockito.internal.util.StringUtil.join;
public class ConstructorInstantiator implements Instantiator {
private final Object outerClassInstance;
public ConstructorInstantiator(Object outerClassInstance) {
this.outerClassInstance = outerClassInstance;
}
public <T> T newInstance(Class<T> cls) {
if (outerClassInstance == null) {
return noArgConstructor(cls);
}
return withParams(cls, outerClassInstance);
}
private static <T> T withParams(Class<T> cls, Object... params) {
try {
//this is kind of over-engineered because we don't need to support more params
//however, I know we will be needing it :)
for (Constructor<?> constructor : cls.getDeclaredConstructors()) {
Class<?>[] types = constructor.getParameterTypes();
if (paramsMatch(types, params)) {
return invokeConstructor(constructor, params);
}
}
} catch (Exception e) {
throw paramsException(cls, e);
}
throw noMatchingConstructor(cls);
}
@SuppressWarnings("unchecked")
private static <T> T invokeConstructor(Constructor<?> constructor, Object... params) throws java.lang.InstantiationException, IllegalAccessException, java.lang.reflect.InvocationTargetException {
AccessibilityChanger accessibility = new AccessibilityChanger();
accessibility.enableAccess(constructor);
return (T) constructor.newInstance(params);
}
private static <T> InstantiationException paramsException(Class<T> cls, Exception cause) {
return new InstantiationException(
join("Unable to create instance of '" + cls.getSimpleName() + "'.",
"Please ensure that the outer instance has correct type and that the target class has 0-arg constructor."),
cause);
}
private static <T> InstantiationException noMatchingConstructor(Class<T> cls) {
return new InstantiationException(
join("Unable to create instance of '" + cls.getSimpleName() + "'.",
"Unable to find a matching 1-arg constructor for the outer instance.")
, null);
}
private static boolean paramsMatch(Class<?>[] types, Object[] params) {
if (params.length != types.length) {
return false;
}
for (int i = 0; i < params.length; i++) {
if (!types[i].isInstance(params[i])) {
return false;
}
}
return true;
}
private static <T> T noArgConstructor(Class<T> cls) {
try {
return invokeConstructor(cls.getDeclaredConstructor());
} catch (Throwable t) {
throw new InstantiationException(join(
"Unable to create instance of '" + cls.getSimpleName() + "'.",
"Please ensure it has 0-arg constructor which invokes cleanly."),
t);
}
}
}