/*
* Copyright (c) 2006 Rogério Liesenfeld
* This file is subject to the terms of the MIT license (see LICENSE.txt).
*/
package mockit.internal.injection;
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;
import javax.annotation.*;
import mockit.*;
import mockit.internal.expectations.mocking.*;
import mockit.internal.util.*;
import static mockit.external.asm.Opcodes.*;
public final class TestedClassInstantiations
{
private static final int FIELD_ACCESS_MASK = ACC_SYNTHETIC + ACC_STATIC;
@Nonnull private final List<TestedField> testedFields;
@Nonnull private final List<MockedType> injectableFields;
@Nonnull private final InjectionState injectionState;
public TestedClassInstantiations()
{
testedFields = new LinkedList<TestedField>();
injectableFields = new ArrayList<MockedType>();
injectionState = new InjectionState();
}
public boolean findTestedAndInjectableFields(@Nonnull Class<?> testClass)
{
findAllTestedAndInjectableFieldsInTestClassHierarchy(testClass);
return !testedFields.isEmpty();
}
private void findAllTestedAndInjectableFieldsInTestClassHierarchy(@Nonnull Class<?> testClass)
{
Class<?> superclass = testClass.getSuperclass();
if (superclass.getClassLoader() != null) {
findAllTestedAndInjectableFieldsInTestClassHierarchy(superclass);
}
Field[] fieldsFromTestClass = testClass.getDeclaredFields();
for (Field candidateField : fieldsFromTestClass) {
int fieldModifiers = candidateField.getModifiers();
if ((fieldModifiers & FIELD_ACCESS_MASK) == 0) {
addAsTestedOrInjectableFieldIfApplicable(candidateField);
}
}
}
private void addAsTestedOrInjectableFieldIfApplicable(@Nonnull Field fieldFromTestClass)
{
for (Annotation fieldAnnotation : fieldFromTestClass.getDeclaredAnnotations()) {
Tested testedMetadata = getTestedAnnotationIfPresent(fieldAnnotation);
if (testedMetadata != null) {
TestedField testedField = new TestedField(injectionState, fieldFromTestClass, testedMetadata);
testedFields.add(testedField);
break;
}
else if (fieldAnnotation instanceof Injectable) {
MockedType mockedType = new MockedType(fieldFromTestClass);
injectableFields.add(mockedType);
break;
}
}
}
@Nullable
private static Tested getTestedAnnotationIfPresent(@Nonnull Annotation annotation)
{
if (annotation instanceof Tested) {
return (Tested) annotation;
}
return annotation.annotationType().getAnnotation(Tested.class);
}
public void createTestedParameters(@Nonnull Object testClassInstance, @Nonnull TestMethod testMethod)
{
int n = testMethod.getParameterCount();
for (int i = 0; i < n; i++) {
TestedParameter testedParameter = createTestedParameterIfApplicable(testMethod, i);
if (testedParameter != null) {
instantiateTestedObject(testClassInstance, testedParameter);
}
}
}
@Nullable
private TestedParameter createTestedParameterIfApplicable(@Nonnull TestMethod testMethod, @Nonnegative int i)
{
Annotation[] parameterAnnotations = testMethod.getParameterAnnotations(i);
for (Annotation parameterAnnotation : parameterAnnotations) {
Tested testedMetadata = getTestedAnnotationIfPresent(parameterAnnotation);
if (testedMetadata != null) {
return new TestedParameter(injectionState, testMethod, i, testedMetadata);
}
}
return null;
}
private void instantiateTestedObject(@Nonnull Object testClassInstance, @Nonnull TestedObject testedObject)
{
try {
testedObject.instantiateWithInjectableValues(testClassInstance);
}
finally {
injectionState.resetConsumedInjectables();
}
}
public void assignNewInstancesToTestedFields(@Nonnull Object testClassInstance, boolean beforeSetup)
{
injectionState.buildListsOfInjectables(testClassInstance, injectableFields);
for (TestedObject testedField : testedFields) {
if (!beforeSetup || testedField.isAvailableDuringSetup()) {
instantiateTestedObject(testClassInstance, testedField);
}
}
}
public void clearTestedObjects()
{
injectionState.lifecycleMethods.executeTerminationMethodsIfAny();
injectionState.clearTestedObjectsAndInstantiatedDependencies();
Object testClassInstance = injectionState.getCurrentTestClassInstance();
for (TestedObject testedField : testedFields) {
testedField.clearIfAutomaticCreation(testClassInstance);
}
}
@Nonnull
public BeanExporter getBeanExporter() { return injectionState; }
}