/*
* Copyright (c) 2006 Rogério Liesenfeld
* This file is subject to the terms of the MIT license (see LICENSE.txt).
*/
package mockit.internal.reflection;
import java.lang.reflect.*;
import javax.annotation.*;
import static java.lang.reflect.Modifier.isStatic;
import mockit.internal.util.*;
import static mockit.internal.reflection.ParameterReflection.*;
import static mockit.internal.util.Utilities.ensureThatMemberIsAccessible;
public final class ConstructorReflection
{
private static final Constructor<?> OBJECT_CONSTRUCTOR;
static
{
try { OBJECT_CONSTRUCTOR = Object.class.getConstructor(); }
catch (NoSuchMethodException e) { throw new RuntimeException(e); }
}
private ConstructorReflection() {}
@Nonnull
public static <T> T newInstance(
@Nonnull Class<T> aClass, @Nonnull Class<?>[] parameterTypes, @Nullable Object... initArgs)
{
if (initArgs == null) {
throw invalidArguments();
}
Constructor<T> constructor = findSpecifiedConstructor(aClass, parameterTypes);
return invoke(constructor, initArgs);
}
@Nonnull
static <T> Constructor<T> findSpecifiedConstructor(@Nonnull Class<?> theClass, @Nonnull Class<?>[] paramTypes)
{
for (Constructor<?> declaredConstructor : theClass.getDeclaredConstructors()) {
Class<?>[] declaredParameterTypes = declaredConstructor.getParameterTypes();
int firstRealParameter = indexOfFirstRealParameter(declaredParameterTypes, paramTypes);
if (
firstRealParameter >= 0 &&
matchesParameterTypes(declaredParameterTypes, paramTypes, firstRealParameter)
) {
//noinspection unchecked
return (Constructor<T>) declaredConstructor;
}
}
String paramTypesDesc = getParameterTypesDescription(paramTypes);
throw new IllegalArgumentException(
"Specified constructor not found: " + theClass.getSimpleName() + paramTypesDesc);
}
@Nonnull
public static <T> T invoke(@Nonnull Constructor<T> constructor, @Nonnull Object... initArgs)
{
ensureThatMemberIsAccessible(constructor);
try {
return constructor.newInstance(initArgs);
}
catch (InstantiationException e) {
throw new RuntimeException(e);
}
catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof Error) {
throw (Error) cause;
}
else if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
}
else {
ThrowOfCheckedException.doThrow((Exception) cause);
throw new IllegalStateException("Should never get here", cause);
}
}
}
@Nonnull
public static <T> T newInstance(
@Nonnull String className, @Nonnull Class<?>[] parameterTypes, @Nullable Object... initArgs)
{
Class<T> theClass = ClassLoad.loadClass(className);
return newInstance(theClass, parameterTypes, initArgs);
}
@Nonnull
public static <T> T newInstance(@Nonnull String className, @Nullable Object... nonNullArgs)
{
if (nonNullArgs == null) {
throw invalidArguments();
}
Class<?>[] argTypes = getArgumentTypesFromArgumentValues(nonNullArgs);
Class<T> theClass = ClassLoad.loadClass(className);
Constructor<T> constructor = findCompatibleConstructor(theClass, argTypes);
return invoke(constructor, nonNullArgs);
}
@Nonnull
private static <T> Constructor<T> findCompatibleConstructor(@Nonnull Class<?> theClass, @Nonnull Class<?>[] argTypes)
{
Constructor<T> found = null;
Class<?>[] foundParameters = null;
Constructor<?>[] declaredConstructors = theClass.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
Class<?>[] declaredParamTypes = declaredConstructor.getParameterTypes();
int firstRealParameter = indexOfFirstRealParameter(declaredParamTypes, argTypes);
if (
firstRealParameter >= 0 &&
(matchesParameterTypes(declaredParamTypes, argTypes, firstRealParameter) ||
acceptsArgumentTypes(declaredParamTypes, argTypes, firstRealParameter)) &&
(found == null || hasMoreSpecificTypes(declaredParamTypes, foundParameters))
) {
//noinspection unchecked
found = (Constructor<T>) declaredConstructor;
foundParameters = declaredParamTypes;
}
}
if (found != null) {
return found;
}
Class<?> declaringClass = theClass.getDeclaringClass();
Class<?>[] paramTypes = declaredConstructors[0].getParameterTypes();
if (paramTypes.length > argTypes.length && paramTypes[0] == declaringClass) {
throw new IllegalArgumentException("Invalid instantiation of inner class; use newInnerInstance instead");
}
String argTypesDesc = getParameterTypesDescription(argTypes);
throw new IllegalArgumentException("No compatible constructor found: " + theClass.getSimpleName() + argTypesDesc);
}
@Nonnull
public static <T> T newInstance(@Nonnull Class<? extends T> aClass, @Nullable Object... nonNullArgs)
{
if (nonNullArgs == null) {
throw invalidArguments();
}
Class<?>[] argTypes = getArgumentTypesFromArgumentValues(nonNullArgs);
Constructor<T> constructor = findCompatibleConstructor(aClass, argTypes);
return invoke(constructor, nonNullArgs);
}
@Nonnull
public static <T> T newInstanceUsingDefaultConstructor(@Nonnull Class<T> aClass)
{
try {
//noinspection ClassNewInstance
return aClass.newInstance();
}
catch (InstantiationException ie) {
throw new RuntimeException(ie);
}
catch (IllegalAccessException ignore) {
return newInstance(aClass, (Object[]) NO_PARAMETERS);
}
}
@Nullable
public static <T> T newInstanceUsingDefaultConstructorIfAvailable(@Nonnull Class<T> aClass)
{
try {
//noinspection ClassNewInstance
return aClass.newInstance();
}
catch (InstantiationException ignore) { return null; }
catch (IllegalAccessException ignore) { return null; }
}
@Nonnull
public static <T> T newInstanceUsingPublicDefaultConstructor(@Nonnull Class<T> aClass)
{
Constructor<T> publicConstructor;
try { publicConstructor = aClass.getConstructor(); }
catch (NoSuchMethodException e) { throw new RuntimeException(e); }
return invoke(publicConstructor);
}
@Nullable
public static <T> T newInstanceUsingPublicConstructorIfAvailable(
@Nonnull Class<T> aClass, @Nonnull Class<?>[] parameterTypes, @Nonnull Object... initArgs)
{
Constructor<T> publicConstructor;
try { publicConstructor = aClass.getConstructor(parameterTypes); }
catch (NoSuchMethodException ignore) { return null; }
return invoke(publicConstructor, initArgs);
}
@Nonnull
public static <T> T newInnerInstance(
@Nonnull Class<? extends T> innerClass, @Nonnull Object outerInstance, @Nullable Object... nonNullArgs)
{
if (nonNullArgs == null) {
throw invalidArguments();
}
if (isStatic(innerClass.getModifiers())) {
throw new IllegalArgumentException(innerClass.getSimpleName() + " is not an inner class");
}
Object[] initArgs = argumentsWithExtraFirstValue(nonNullArgs, outerInstance);
return newInstance(innerClass, initArgs);
}
@Nonnull
public static <T> T newInnerInstance(
@Nonnull String innerClassName, @Nonnull Object outerInstance, @Nullable Object... nonNullArgs)
{
Class<?> outerClass = outerInstance.getClass();
ClassLoader loader = outerClass.getClassLoader();
String className = outerClass.getName() + '$' + innerClassName;
Class<T> innerClass = ClassLoad.loadFromLoader(loader, className);
return newInnerInstance(innerClass, outerInstance, nonNullArgs);
}
@SuppressWarnings({"UnnecessaryFullyQualifiedName", "UseOfSunClasses"})
private static final sun.reflect.ReflectionFactory REFLECTION_FACTORY =
sun.reflect.ReflectionFactory.getReflectionFactory();
@Nonnull
public static <T> T newUninitializedInstance(@Nonnull Class<T> aClass)
{
try {
Constructor<?> fakeConstructor = REFLECTION_FACTORY.newConstructorForSerialization(aClass, OBJECT_CONSTRUCTOR);
if (fakeConstructor == null) { // can happen on Java 9
//noinspection ConstantConditions
return null;
}
@SuppressWarnings("unchecked") T newInstance = (T) fakeConstructor.newInstance();
return newInstance;
}
catch (NoClassDefFoundError e) {
StackTrace.filterStackTrace(e);
e.printStackTrace();
throw e;
}
catch (ExceptionInInitializerError e) {
StackTrace.filterStackTrace(e);
e.printStackTrace();
throw e;
}
catch (InstantiationException e) { throw new RuntimeException(e); }
catch (IllegalAccessException e) { throw new RuntimeException(e); }
catch (InvocationTargetException e) { throw new RuntimeException(e.getCause()); }
}
}